Викиучебник
ruwikibooks
https://ru.wikibooks.org/wiki/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0
MediaWiki 1.45.0-wmf.8
first-letter
Медиа
Служебная
Обсуждение
Участник
Обсуждение участника
Викиучебник
Обсуждение Викиучебника
Файл
Обсуждение файла
MediaWiki
Обсуждение MediaWiki
Шаблон
Обсуждение шаблона
Справка
Обсуждение справки
Категория
Обсуждение категории
Полка
Обсуждение полки
Импортировано
Обсуждение импортированного
Рецепт
Обсуждение рецепта
Задача
Обсуждение задачи
TimedText
TimedText talk
Модуль
Обсуждение модуля
Я тебя люблю
0
5544
261819
261800
2025-07-03T15:03:50Z
Alegri4B
71812
Добавлен язык гоанский конкани
261819
wikitext
text/x-wiki
{{цитата|автор=[[w:Л. Н. Толстой|Л. Н. Толстой]]|Всегда кажется, что нас любят за то, что мы хороши.|А не догадываемся, что любят нас оттого, что хороши те, кто нас любит.}}
Эта статья содержит признания в любви на сотнях естественных и искусственных языков мира. При машинном переводе часто присутствуют неточности, а найти перевод на некоторые языки нелегко. Видите ошибку — исправляйте. Знаете язык, которого в нашей статье нет - добавляйте. Любите и будьте любимыми! :)
<!--
# [[w:|язык]]:
-->
# [[Файл:Flag of Abazinia.svg|22px|border]] [[w:Абазинский язык|Абазинский]]: '''сара бара бзи бызбитӀ''' («сара бара бзи бызбит») - женщине; '''сара уара бзи уызбитӀ''' («сара уара бзи уызбит») - мужчине.
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:en:Abé language|Абе]]: '''mon ko lo fon''' («мон ко ло фон»).
# [[Файл:Flag of Maine.svg|22px|border]] [[w:Абенаки (язык)|Абенаки]]: '''k’kezalmel''' («къ(э)зальмъ(э)ль»).<p>[[w:Абенаки (язык)|Абнаки]]: см. Абенаки
# [[Файл:Flag of Abruzzo.svg|22px|border]] [[w:Неаполитанский язык|Абруццский]]: '''t-vujie bbene''' («твуджэ ббэнэ»).
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:en:Abure language|Абуре]]: '''u'm wloloho''' («юм влолохо»).
# [[Файл:Flag of the Republic of Abkhazia.svg|22px|border]] [[w:Абхазский язык|Абхазский]]: '''сара бара бзиа бызбоит''' («сара бара бзиа бызбоит») — женщине; '''сара уара бзиа узбоит''' («сара уара бзиа узбоит») — мужчине.
# [[Файл:Flag of Awadh.svg|22px|border]] [[w:Авадхи|Авадхи]]: '''मइँ तोहसे पिरेम करत हउँ''' («май тохсе пирэм карат хау»).<p>[[w:Агуакатекский язык|Авакатек]]: см. Агуакатекский
# [[Файл:Conlangflag.svg|22px|border]] Аваллаэн: '''vüväloiek dü quuo''' («вювялойек дю кууо»).
# [[Файл:Flag of Avars.svg|22px|border]] [[w:Аварский язык|Аварский]]: '''дие мун йокъула''' («дие мун йокъула») — женщине; '''дие мун вокьула''' («дие мун вокьула») — мужчине.
# [[Файл:Flag of Austria.svg|22px|border]] [[w:Австрийский вариант немецкого языка|Австрийский немецкий]]: '''i lieb di''' («и либ ди»), '''i mog di''' («и мог ди»), '''i hob di gern''' («и хоб ди гэрн»).
# [[Файл:Faravahar-Gold.svg|22px|border]] [[w:Авестийский язык|Авестийский]]: '''𐬀𐬵𐬨𐬌 𐬯𐬙𐬀 𐬟𐬭𐬌''' («ахми ста фри»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Агуакатекский язык|Агуакатекский]]: '''wachinpeq' tzawe'''' («уачинпек цауэ»).
# [[Файл:Flag of Peru.svg|22px|border]] [[w:Агуаруна|Агуаруна]]: '''wii pachit-ja-me''' («уии пачит йя ме»).
# [[Файл:Flag of Aghuls.svg|22px|border]] [[w:Агульский язык|Агульский]]: '''зус вун кандаа''' («зус вун кандаа»).
# [[Файл:Flag of Louisiana.svg|22px|border]] [[w:Адаи|Адаи]]: '''hekátœk kotán a enálœk''' («хэкаток котан а эналок»).<p>[[w:Адаи|Адайский]]: см. Адаи
# [[Файл:Flag of Ghana.svg|22px|border]] [[w:Адангме (язык)|Адангме]]: '''i suɔ mo''' («и суо мо»).
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:Аджукру|Аджукру]]: '''m'b'errourong''' («мбэрруронг»).
# [[Файл:Flag of Adygea.svg|22px|border]] [[w:Адыгейский язык|Адыгейский]]: '''сэ шӏу усэлъэгъу''' («сэ ш'у усэл'эг'у»).
# [[Файл:Flag of Azerbaijan.svg|22px|border]] [[w:Азербайджанский язык|Азербайджанский]]: '''mən səni sevirəm''' («мэн сэни севирэм»).
# [[Файл:Banner of the Qulla Suyu (1979).svg|22px|border]] [[w:Аймара (язык)|Аймара]]: '''munsmawa''' («мунсмава»).
# [[Файл:Flag of Ainu.svg|22px|border]] [[w:Айнский язык|Айнский]]: '''アエオマプ''' («аэомап»).
# [[Файл:Flag of Ghana.svg|22px|border]] [[w:Акан|Акан]]: '''mo dow''' («мо доу»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Акатекский язык|Акатекский]]: '''chachinkamk’ulnehan''' («чачинкамк ульнеан»).
# [[Файл:Akkadian Empire Flag of Map (Year-2250 BC).png|22px|border]] [[w:Аккадский язык|Аккадский]]: '''𒀀𒊏𒀀𒄠𒆠''' («араамки») - женщине; '''𒀀𒊏𒀀𒄠𒅗''' («араамка») - мужчине.
# [[Файл:PH-AKL Flag.png|22px|border]] [[w:en:Aklanon language|Акланонский]]: '''palangga ko ikaw''' («палангга ко икау»).
# [[Файл:Flag of Astrakhan Oblast.svg|22px|border]] [[w:Алабугатско-татарский язык|Алабугатско-татарский]]: '''мен сені чаw болман''' («мэн сэни чау болман»).
# [[Файл:Flag of North Sumatra.svg|22px|border]] [[w:en:Alas language|Алас-клуэт]]: '''holong do rohangku tu ho''' («холонг до рохангку ту хо»).
# [[Файл:Flag of Kosovo.svg|22px|border]] [[w:Гегский диалект албанского языка|Албанский (гегский)]]: '''unë të dua''' («ун тэ дуа»).
# [[Файл:Flag of Albania.svg|22px|border]] [[w:Тоскский диалект албанского языка|Албанский (тоскский)]]: '''unë të dua''' («ун тэ дуа»).<p>[[w:Алгонквинский язык|Алгонквин]]: см. Алгонквинский
# [[Файл:Flag of Quebec.svg|22px|border]] [[w:Алгонквинский язык|Алгонквинский]]: '''kiságiyán''' («кисагийан»), '''kuwumáras''' («куумадас»).<p>[[w:Алгонквинский язык|Алгонкинский]]: см. Алгонквинский
# [[Файл:Proposed Alemannic flag.svg|22px|border]] [[w:Алеманнский диалект|Алеманнский]]: '''ich lieb dich''' («ихь лиэб дихь»), '''i liäbä di''' («и лиэбэ ди»), '''i ha di gärn''' («и ха ди гэрн»), '''ich han dich gärn''' («ихь хан дихь гэрн»).
# [[Файл:Alentejo.gif|22px|border]] [[w:en:Alentejan Portuguese|Алентежанский португальский]]: '''gosto de ti''' («госту дже чи»).
# [[Файл:Flag of Aleutsky Raion.png|22px|border]] [[w:Алеутский язык|Алеутский]]: '''txin yaktakuq''' («тхин яктакук»).
# [[Файл:Flag of Algeria.svg|22px|border]] [[w:Алжирский диалект арабского языка|Алжирский арабский]]: '''أحبك''' («ухыббуки») — женщине; '''أحبك''' («ухыббука») — мужчине.
# [[Файл:Flag of Altai Republic.svg|22px|border]] [[w:Алтайский язык|Алтайский]]: '''мен сени сӱӱп jадым''' («мен сэни сююп ядым»).
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:en:Alur language|Алур]]: '''ameri''' («амери»).<p>[[w:Арабский литературный язык|Ал-фусха]]: см. Арабский литературный<p>[[w:Арабский литературный язык|Аль-фусха]]: см. Арабский литературный<p>[[w:Алютикский язык|Алютик]]: см. Алютикский
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Алютикский язык|Алютикский]]: '''qunukamken''' («кунукамкен»).
# [[Файл:Flag of Olyutorsky rayon (Kamchatka krai).png|22px|border]] [[w:Алюторский язык|Алюторский]]: '''гыммы гытты гәму лэӈэкткэн''' («гыммы гытты гэму лэнэкткэн»).
# [[Файл:POL Bielsko Biała flag.svg|22px|border]] [[w:en:Alzenau dialect|Альтцнерский]]: '''(e)ich ho dü gahn''' («эйхь хо дю ган»).<p>[[w:Арабский литературный язык|АЛЯ]]: см. Арабский литературный
# [[Файл:Flag of Brazil.svg|22px|border]] [[w:Амавака|Амавака]]: '''hiya min cúunyovaquinu''' («хийя мин кууньёвакину»).<p>[[w:Ньенгату|Амазонский лингва-жерал]]: см. Ньенгату<p>[[w:Капампанганский язык|Аманунг-сисуан]]: см. Капампанганский<p>[[w:Амавака|Амауака]]: см. Амавака
# [[Файл:Flag of Norway.svg|22px|border]] [[w:en:American Norwegian|Американский норвежский]]: '''elsker dæ''' («эльска дэ»).<p>[[w:Амисский язык|Амис]]: см. Амисский
# [[Файл:Flag of the Republic of China.svg|22px|border]] [[w:Амисский язык|Амисский]]: '''maolahay kako tisowanan''' («маолахай како тисованан»).
# [[Файл:Flag of Ethiopia.svg|22px|border]] [[w:Амхарский язык|Амхарский]]: '''እወድሃለሁ''' («ивэдихалэху») — мужчине, '''እወድሻለሁ''' («ивэдишалэху») — женщине.
# [[Файл:Unofficial flag of Nagaland.svg|22px|border]] [[w:Ангами (язык)|Ангами]]: '''angu ani se ho''' («ангу ани сэ хо»).
# [[Файл:Flag of the Cooch Bihar State.svg|22px|border]] [[w:Ангика|Ангика]]: '''hamme tohrā pyār kare chiye''' («хамме тохра пьяр каре чийе»).
# [[Файл:Flag of North Sumatra.svg|22px|border]] [[w:Ангкола|Ангкола]]: '''holong do rohangku tu ho''' («холонг до рохангку ту хо»).
# [[Файл:Flag of England.svg|22px|border]] [[w:Английский язык|Английский]]: '''i love you''' («ай лав ю»).<p>[[w:Древнеанглийский|Англосаксонский]]: см. Древнеанглийский
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Андийский язык|Андийский]]: '''дыё мен джилидо''' («дыё мен джилидó»).
# [[Файл:Flag of CARICOM.svg|22px|border]] [[w:Антильский франко-креольский язык|Антильский франко-креольский]]: '''men ainmainw''' («ман энмэн»).
# [[Файл:Flag of Jonglei.png|22px|border]] [[w:Ануак (язык)|Ануак]]: '''a mëër ki ïïni''' («а мээр кы иины»).<p>[[w:Ануак (язык)|Аньва]]: см. Ануак
# [[Файл:Unofficial flag of Nagaland.svg|22px|border]] [[w:Ао (язык)|Ао]]: '''ni na meimer''' («ни на меимер»), '''nina mimar''' («нина мимар»).
# [[Файл:Bandeira do Tocantins.svg|22px|border]] [[w:Апинайе (язык)|Апинайе]]: '''iman a-mã anon''' («иман а мэ анон»).
# [[Файл:Flag of Peru.svg|22px|border]] [[w:Арабела|Арабела]]: '''quia pani-ya-nijia''' («киа пани йя нихья»).
# [[Файл:Arabic-Language-Flag.svg|22px|border]] [[w:Арабский язык|Арабский]]: '''أُحِبُّكِ''' («ухыббуки») — женщине; '''أحبك''' («ухыббука») — мужчине.
# [[Файл:Flag of the Arab League.svg|22px|border]] [[w:Арабский литературный язык|Арабский литературный]]: '''أحبك''' («ухиббуки») — женщине; '''أُحِبُّكَ''' («ухиббука») — мужчине.
# [[Файл:Flag of the Cooperation Council for the Arab States of the Gulf.svg|22px|border]] [[w:Арабский диалект Персидского залива|Арабский Персидского залива]]: '''اَحِبِّچْ''' («эхиббич») — женщине; '''اَحِبِّكْ''' («эхиббик») — мужчине.
# [[Файл:Flag of Aragon.svg|22px|border]] [[w:Арагонский язык|Арагонский]]: '''te quiero''' («тэ кьеро»), '''t'amo''' («тамо»).
# [[Файл:Flag-Val d'Aran.svg|22px|border]] [[w:Аранский язык|Аранский]]: '''que t'aimi''' («кё тэми»).
# [[Файл:Flag of Arapaho Nation.svg|22px|border]] [[w:Арапахо (язык)|Арапахо]]: '''biixoo3é3en''' («биихоосесэн»).<p>[[w:Мапуче (язык)|Арауканский]]: см. Мапуче
# [[Файл:Flag of Sicilian-Arbëreshë.svg|22px|border]] [[w:Арберешский диалект|Арберешский]]: '''të dua''' («тэ дуа»).<p>[[w:Кубачинский язык|Арбукский]]: см. Кубачинский
# [[Файл:Fictitious flag of Lazistan.png|22px|border]] [[w:Лазский язык|Ардешенский лазский]]: '''ma si maoropen''' («ма си маоропэн»).
# [[Файл:Flag of Armenia.svg|22px|border]] [[w:Армянский язык|Армянский]]: '''ես քեզ սիրում եմ''' («ес кез сирум эм»).<p>[[w:Арумынский язык|Аромунский]]: см. Арумынский<p>[[w:Франкопровансальский язык|Арпитанский]]: см. Франкопровансальский
# [[Файл:Aromanian flag.svg|22px|border]] [[w:Арумынский язык|Арумынский]]: '''ti voi''' («ти вой»).
# [[Файл:Fictitious flag of Lazistan.png|22px|border]] [[w:Лазский язык|Архавский лазский]]: '''ma si p'orom''' («ма сип ором»).<p>[[w:Карабахский диалект армянского языка|Арцахский]]: см. Карабахский армянский
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Арчинский язык|Арчинский]]: '''зон ун кьӏан кер''' («зон ун къхан кер»).
# [[Файл:Morning Star flag.svg|22px|border]] [[w:Асмат (язык)|Асмат]]: '''manám afpín''' («манам афпин»).
# [[Файл:Flag of United Liberation Front of Asom.svg|22px|border]] [[w:Ассамский язык|Ассамский]]: '''মই তোমাক ভাল পাওঁ''' («мой тумак бхал пао»).
# [[Файл:Flag of the Assyrians (no Assur).svg|22px|border]] [[w:Ассирийский новоарамейский язык|Ассирийский новоарамейский]]: '''ki bayyinakh''' («ки байинакх») - женщине, '''ki bayyanoukh''' («ки байяноукх») - мужчине, '''maghbinnakh''' («магхбиннакх») - женщине, '''makhbannoukh''' («макхбанноукх») - мужчине.<p>[[w:Аккадский язык|Ассиро-вавилонский]]: см. Аккадский<p>[[w:Астекские языки|Астекский]]: см. Науатль
# [[Файл:Flag of Asturias.svg|22px|border]] [[w:Астурийский язык|Астурийский]]: '''quiérote''' («кьерути»), '''te quiero''' («те кьеро»).
# [[Файл:Flag of Asturias.svg|22px|border]] [[w:Астурлеонский язык|Астурлеонский]]: '''quiérote''' («кьерути»).
# [[Файл:Flag of Quebec.svg|22px|border]] [[w:Атикамек (язык)|Атикамек]]: '''ki micta sakihitin''' («ки микта сакхитин»), '''kisakihitin''' («кисакихитин»).
# [[Файл:Fictitious flag of Lazistan.png|22px|border]] [[w:Лазский язык|Атинский лазский]]: '''ma si malimben''' («ма си малимбэн»).
# [[Файл:Conlangflag.svg|22px|border]] [[w:Атлантийский язык|Атлантийский]]: [[File:Atlantean K.png|20 px]][[File:Atlantean A.png|20 px]][[File:Atlantean H.png|20 px]][[File:Atlantean G.png|20 px]] [[File:Atlantean M.png|20 px]][[File:Atlantean O.png|20 px]][[File:Atlantean H.png|20 px]][[File:Atlantean K.png|20 px]] [[File:Atlantean M.png|20 px]][[File:Atlantean A.png|20 px]][[File:Atlantean H.png|20 px]][[File:Atlantean T.png|20 px]] («кахг мохк махт»).
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:Аттие (язык)|Аттие]]: '''min bou la yé''' («мин боу ла йе»).<p>[[w:Ассирийский новоарамейский язык|Атурая]]: см. Ассирийский новоарамейский<p>[[w:Ндюка|Аукан]]: см. Ндюка
# [[Файл:Flag of the Afar Liberation Front.svg|22px|border]] [[w:Афарский язык|Афарский]]: '''ko kicinio''' («ко кисинио»).<p>[[w:Пушту|Афганский]]: см. Пушту<p>[[w:Дари|Афганско-персидский]]: см. Дари
# [[Файл:Afrikaner Vryheidsvlag.svg|22px|border]] [[w:Африкаанс|Африкаанс]]: '''ek het jou lief''' («эк эт ю лиф») — повседневная форма, '''ek’s lief vir jou''' («экс лиф ви яу») — формальный вариант.
# [[Файл:Flag of the African Union.svg|22px|border]] [[w:Африхили|Африхили]]: '''misopa wu''' («мисопа ву»).<p>[[w:Астекские языки|Ацтекский]]: см. Науатль<p>[[w:Ачехский язык|Ачех]]: см. Ачехский
# [[Файл:Flag of Free Aceh Movement.svg|22px|border]] [[w:Ачехский язык|Ачехский]]: '''kaleuh gunci''' («калеух гунчи»).
# [[Файл:Flag of Baja Verapaz, Guatemala.png|22px|border]] [[w:Ачи (язык)|Ачи]]: '''k’ax katinna’o''' («к’аш катинна’о»).
# [[Файл:Flag of Acholi.svg|22px|border]] [[w:Ачоли (язык)|Ачоли]]: '''amari''' («амари»).
# [[Файл:Flag of Peru.svg|22px|border]] [[w:Ачуар-шивиар|Ачуар-шивиар]]: '''anéajime''' («анеахиме»).
# [[Файл:Flag of Peru.svg|22px|border]] [[w:Ашенинка (язык)|Ашенинка]]: '''no-cova-a-mi''' («но кова а ми»).
# [[Файл:Flag of Ayacucho.svg|22px|border]] [[w:Аякучанский кечуа|Аякучанский кечуа]]: '''kuyaykim''' («куяйким»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Бабин-вицувитен|Бабин-вицувитен]]: '''nkests'iy''' («нкесций»).<p>[[w:Астурийский язык|Бабле]]: см. Астурийский
# [[Файл:Flag of Bavaria (lozengy).svg|22px|border]] [[w:Баварский язык|Баварский]]: '''i lieb di''' («и либ ди»).<p>[[w:Багвалинский язык|Багвалальский]]: см. Багвалинский
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Багвалинский язык|Багвалинский]]: '''де̄ мен хелал''' («дэ мен хелал»).<p>[[w:Багвалинский язык|Багулальский]]: см. Багвалинский
# [[Файл:..Madhya Pradesh Flag(INDIA).png|22px|border]] [[w:Багхели|Багхели]]: '''ham tohī cāhit ha''' («хам тохи кахит ха»).<p>[[w:Баварский язык|Баериш]]: см. Баварский
# [[Файл:Flag of Canton of Basel.svg|22px|border]] [[w:Базельский диалект|Базельский немецкий]]: '''i ha di gärn''' («и ха ди гярн»).<p>[[w:Авадхи|Байсвари]]: см. Авадхи<p>[[w:Кого (язык)|Бакоко]]: см. Кого
# [[Файл:Old Flag of Bali.svg|22px|border]] [[w:Балийский язык|Балийский]]: '''titiyang tresna sareng ragane''' («титиянг трэсна сарэн раганэ»).<p>[[w:Карачаево-балкарский язык|Балкарский]]: см. Карачаево-балкарский<p>[[w:Белуджский язык|Балочи]]: см. Белуджский<p>[[w:Белуджский язык|Балучи]]: см. Белуджский<p>[[w:Воламо|Балта]]: см. Воламо
# [[Файл:Flag of Mali.svg|22px|border]] [[w:Бамана|Бамана]]: '''m’bi fe''' («мби фе»).<p>[[w:Бамана|Бамананкан]]: см. Бамана<p>[[w:Бамана|Бамбара]]: см. Бамана
# [[Файл:Local flag of German controlled Bamum.jpg|22px|border]] [[w:Бамум (язык)|Бамум]]: '''me naa ngu nyu''' («мэ наа нгу нью»).
# [[Файл:Flag of South Kalimantan.png|22px|border]] [[w:Банджарский язык|Банджарский]]: '''aku cinta dua ikam''' («аку чинта дуа икам»).
# [[Файл:PH-ROM Flag.png|22px|border]] [[w:en:Bantoanon language|Бантонский]]: '''palangga ka nako''' («палангга ка нако»).
# [[Файл:Flag of the Bari people.svg|22px|border]] [[w:en:Bari language|Бари]]: '''man nyanyar do''' («ман ньяньяр до»).<p>[[w:Воламо|Бародда]]: см. Воламо
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Баса (язык)|Баса]]: '''mengweswe''' («мэнгуэсуэ»).
# [[Файл:Flag of the Basque Country.svg|22px|border]] [[w:Баскский язык|Баскский]]: '''maite zaitut''' («майтэ сайтуд»).
# [[Файл:Flag of Liberia.svg|22px|border]] [[w:Басса (язык)|Басса]]: '''ḿ ɖɛ̀ɓɛ̀ m̀ mú''' («м денбе м му»).<p>[[w:Бетави (язык)|Батавский]]: см. Бетави
# [[Файл:Flag of Batak.svg|22px|border]] [[w:en:Batak Karo language|Батакский каро]]: '''keleng ateku kam''' («келенг атэку кам»).
# [[Файл:Flag of Batak.svg|22px|border]] [[w:en:Batak Simalungun language|Батакский сималунгун]]: '''holong do uhurhu bamu''' («холонг до ухурху баму»).
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:en:Baoulé language|Бауле]]: '''mi klôa''' («ми клооа»).<p>[[w:Чинский язык|Баунгше]]: см. Чинский
# [[Файл:Flag of Bashkortostan (1918).svg|22px|border]] [[w:Башкирский язык|Башкирский]]: '''мин һине яратам''' («мин хине яратам»).
# [[Файл:Flag of Kingdom of Kakheti.svg|22px|border]] [[w:Бацбийский язык|Бацбийский]]: '''ვეწ სო ჰ’ოჼ''' («вэць со хо») - мужчина, '''ჲეწ სო ჰ’ოჼ''' («йець со хо») - женщина.<p>[[w:Бежтинский язык|Бежитинский]]: см. Бежтинский
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Бежтинский язык|Бежтинский]]: '''до ми ятӀца''' («до ми ятхца»).
# [[Файл:Flag of England.svg|22px|border]] [[w:Бейсик-инглиш|Бейсик-инглиш]]: '''i love you''' («ай лав ю»).<p>[[w:Палауский язык|Белау]]: см. Палауский<p>[[w:Нухалк|Беллакула]]: см. Нухалк<p>[[w:Нухалк|Белла-кула]]: см. Нухалк
# [[Файл:Flag of Belarus (1918, 1991-1995).svg|22px|border]] [[w:Белорусский язык|Белорусский]]: '''кахаю цябе''' («кахаю тябе»)
# [[Файл:Balochistan flag.svg|22px|border]] [[w:Белуджский язык|Белуджский]]: '''tu mana doost biyeh''' («ту мана доост бийех»).
# [[Файл:Flag of the Bemba people.svg|22px|border]] [[w:Бемба (язык)|Бемба]]: '''nalikutemwa''' («наликутемва»).
# [[Файл:Flag of the Republic of the Congo.svg|22px|border]] [[w:en:Bembe language (Kibembe)|Бембе]]: '''nauunda''' («науунда»).
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:en:Beng language|Бенг]]: '''mam vimini''' («мам вимини»).
# [[Файл:Flag of Equatorial Guinea.svg|22px|border]] [[w:en:Benga language|Бенга]]: '''i tondoko nd'ovè''' («и тондоко ндове»).<p>[[w:Бенгальский язык|Бенгали]]: см. Бенгальский
# [[Файл:Flag of Bangladesh.svg|22px|border]] [[w:Бенгальский язык|Бенгальский]]: '''আমি তোমাকে ভালবাসি''' («ами томаке бхалообаши»).
# [[Файл:Flag of Benin.svg|22px|border]] [[w:en:Berba language|Берба]]: '''lakh tirikh''' («лах тирих»).
# [[Файл:Flag of Bergamo.svg|22px|border]] [[w:Бергамский диалект ломбардского языка|Бергамский ломбардский]]: '''ta 'öre be''' («та эрэ бэ»).
# [[Файл:Flag of Berlin.svg|22px|border]] [[w:Берлинский диалект|Берлинский немецкий]]: '''ick liebe dir''' («ик либэ диар»).
# [[Файл:Flag of Yogyakarta.svg|22px|border]] [[w:Бетави (язык)|Бетави]]: '''gua cinte ame elu''' («гуа синтэ амэ элу»).
# [[Файл:Bété People Flag.svg|22px|border]] [[w:Бете (языки)|Бете]]: '''djiba han djibameu ba''' («джиба хан джибамэу ба»).<p>[[w:Древнееврейский язык|Библейский иврит]]: см. Древнееврейский
# [[Файл:PH-CAS Flag.png|22px|border]] [[w:Бикольский язык|Бикольский]]: '''namumutan ta ka''' («намумутан та ка»).
# [[Файл:Flag of Sarangani.png|22px|border]] [[w:en:Blaan language|Билаан]]: '''kando ta ge’''' («кандо та ге»).<p>[[w:Билин (язык)|Билен]]: см. Билин
# [[Файл:Flag of Eritrea.svg|22px|border]] [[w:Билин (язык)|Билин]]: '''ንኳአተካያ''' («нэкваʾэтэкайя»).<p>[[w:Тангале (язык)|Биллири]]: см. Тангале
# [[Файл:Flag of Het Bildt.svg|22px|border]] [[w:en:Bildts|Билтский нидерландский]]: '''ich zien dich gan''' («их зьен дих ган»).
# [[Файл:Bendera Kesultanan Bima.png|22px|border]] [[w:Бима (язык)|Бима]]: '''nahu ne’e nggomi''' («наху не э нгоми»).<p>[[w:Эдо (язык)|Бини]]: см. Эдо
# [[Файл:Flag of Myanmar.svg|22px|border]] [[w:Бирманский язык|Бирманский]]: '''ချစ်ပါတယ်''' («чит па дэ»).
# [[Файл:Flag of Burkina Faso.svg|22px|border]] [[w:Биса (язык)|Биса]]: '''mii nan''' («мии нан»).
# [[Файл:Flag of Vanuatu.svg|22px|border]] [[w:Бислама|Бислама]]: '''mi lavèm yu''' («ми лавэм ю»).<p>[[w:Хо (язык)|Бихар-хо]]: см. Хо<p>[[w:Бишнуприя-манипури|Бишнуприя]]: см. Бишнуприя-манипури
# [[Файл:Flag of Manipur (stripes variant).svg|22px|border]] [[w:Бишнуприя-манипури|Бишнуприя-манипури]]: '''mi tore hada paori''' («ми торе хада паори»).<p>[[w:Билин (язык)|Блин]]: см. Билин
# [[Файл:Flag of the Blackfoot Confederacy.jpg|22px|border]] [[w:Блэкфут (язык)|Блэкфут]]: '''kitsiikákomimmo''' («китсиикакомиммо»).
# [[Файл:Flag of Burkina Faso.svg|22px|border]] [[w:en:Bobo language|Бобо]]: '''ma kia bé nà''' («ма киа бэ на»).<p>[[w:Билин (язык)|Богос]]: см. Билин
# [[Файл:Bandera Bodoland.svg|22px|border]] [[w:Бодо (язык)|Бодо]]: '''आं नोंखौ मोजां मोनो''' («анг нвунгхоу гвусвусуиу»).
# [[Файл:Flag of Bulgaria.svg|22px|border]] [[w:Болгарский язык|Болгарский]]: '''аз те обичам''' («аз тэ обичам») — формально, '''обичам те''' («обичам тэ») — неформально.
# [[Файл:Flag of Native Peoples of Colombia.svg|22px|border]] [[w:Бора (язык)|Бора]]: '''o wajyuóo uuké''' («о уайюоо ууке»).<p>[[w:Бодо (язык)|Боро]]: см. Бодо<p>[[w:Боснийский язык|Босанский]]: см. Боснийский
# [[Файл:Flag of Bosnia and Herzegovina (1992-1998).svg|22px|border]] [[w:Боснийский язык|Боснийский]]: '''volim te''' («волим те»).<p>[[w:Сербохорватский язык|Боснийско-хорватско-сербский]]: см. Сербохорватский<p>[[w:Сербохорватский язык|Боснийско-хорватско-черногорско-сербский]]: см. Сербохорватский<p>[[w:Боснийский язык|Босняцкий]]: см. Боснийский
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Ботлихский язык|Ботлихский]]: '''ди мин иде''' («ди мин идэ»).
# [[Файл:Flag of Bohol Province, Philippines.svg|22px|border]] [[w:en:Boholano dialect|Бохолано]]: '''nahigugma ko nimu''' («нахигугма ко ниму»).<p>[[w:Боснийский язык|Бошняцкий]]: см. Боснийский
# [[Файл:Flag of Brazil.svg|22px|border]] [[w:Бразильский вариант португальского языка|Бразильский португальский]]: '''eu te amo''' («эйю тчи аму»).<p>[[w:Брауи|Брагуи]]: см. Брауи<p>[[w:Брауи|Брагуйский]]: см. Брауи
# [[Файл:Flag of the Brahui People.svg|22px|border]] [[w:Брауи|Брауи]]: '''nehton merve kewa''' («нэхтон мэрвэ кэва»), '''kane nehton merve arre''' («канэ нэхтон мэрвэ аррэ»).<p>[[w:Брауи|Брахуи]]: см. Брауи
# [[Файл:Flag of Brittany.svg|22px|border]] [[w:Бретонский язык|Бретонский]]: '''da garout a ran''' («да гарут а ран»), '''c’hwant m’eus diouzhit''' («хван мэус дивит»).<p>[[w:Брешийский диалект ломбардского языка|Брешанский]]: см. Брешийский ломбардский<p>[[w:Брешийский диалект ломбардского языка|Брешианский]]: см. Брешийский ломбардский
# [[Файл:Flag of Brescia.svg|22px|border]] [[w:Брешийский диалект ломбардского языка|Брешийский ломбардский]]: '''ta öle bhé''' («та эле бэ»).
# [[Файл:Brithenig.png|22px|border]] [[w:Brithenig|Бритениг]]: '''eo dy af''' («эо ди ав»), '''eo dy af twy''' («эо ди ав туи»).<p>[[w:Brithenig|Бритеник]]: см. Бритениг<p>[[w:Brithenig|Брифениг]]: см. Бритениг<p>[[w:Будухский язык|Будугский]]: см. Будухский
# [[Файл:Budukh flag.jpg|22px|border]] [[w:Будухский язык|Будухский]]: '''заз вын йикаджы''' («заз вын йикаджи»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:en:Bukusu dialect|Букусу]]: '''nakhusima''' («накусима»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Bulu language|Булу]]: '''ma nye'e wo''' («ма нье'е во»).
# [[Файл:Flag of Madhya Pradesh.svg|22px|border]] [[w:Бундели|Бундели]]: '''hum tume chahat hain''' («хум туме чахат хаин»).<p>[[w:Бундели|Бунделкханди]]: см. Бундели<p>[[w:Африкаанс|Бурский]]: см. Африкаанс
# [[Файл:Flag of Morobe.png|22px|border]] [[w:en:Burum language|Бурум]]: '''iripki nembi''' («ирипки нэмби»), '''sepki öröbi''' («сэпки эрэби»).
# [[Файл:Flag of Jammu and Kashmir (1952-2019).svg|22px|border]] [[w:Бурушаски|Бурушаски]]: '''جا ان کا شل بلا/jaa un ka shul bila''' («джаа ун ка шул била»).
# [[Файл:Flag of Buryatia.svg|22px|border]] [[w:Бурятский язык|Бурятский]]: '''би шамай дурлаха''' («би шамай дурлаха»).
# [[Файл:Flag of Mayotte (local).svg|22px|border]] [[w:Буши (язык)|Буши]]: '''anaou tiakou''' («анау тьяку»), '''zahou mitiya anaou''' («заху мития анау»).
# [[Файл:Flag of Jharkhand.svg|22px|border]] [[w:Бходжпури|Бходжпури]]: '''हम तोहसे प्यार करेलीं''' («хам тосе пьаар карила»).<p>[[w:Севернокитайский язык|Бэйфанхуа]]: см. Севернокитайский
# [[Файл:Flag of Liberia.svg|22px|border]] [[w:Ваи (язык)|Ваи]]: '''na lia''' («на лиа»).
# [[Файл:Flag of the Valencian Community (2x3).svg|22px|border]] [[w:Валенсийский диалект|Валенсийский]]: '''t’estime''' («тэстимэ»).
# [[Файл:Proposed Flag of Romanchia.svg|22px|border]] [[w:en:Vallader dialect|Валладерский романшский]]: '''eu t’am''' («эу тэм»).
# [[Файл:Flag of Wales.svg|22px|border]] [[w:Валлийский язык|Валлийский]]: '''rwy`n dy garu di''' («руаин ды хару ди»).
# [[Файл:Flag of Wallonia.svg|22px|border]] [[w:Валлонский язык|Валлонский]]: '''dji t`veû vol`tî''' («дит вё вёльти»).<p>[[w:Массачусетский язык|Вампаноаг]]: см. Массачусетский<p>[[w:Массачусетский язык|Вампаноаг-массачусетский]]: см. Массачусетский
# [[Файл:Vlag Fil Samar.gif|22px|border]] [[w:Варайский язык|Варайский]]: '''hinigugma ta ikaw''' («хинигугма та икау»).
# [[Файл:Pamiri Flag.webp|22px|border]] [[w:Ваханский язык|Ваханский]]: '''wuz taw dust δorəm''' («ууз тау дуст дорэм»).<p>[[w:Гуахиро (язык)|Ваюу]]: см. Гуахиро<p>[[w:Юрок (язык)|Вейцпекан]]: см. Юрок
# [[Файл:Flag of Hungary.svg|22px|border]] [[w:Венгерский язык|Венгерский]]: '''szeretlek''' («сэ́рэтлэк»).
# [[Файл:Flag of Venda (1973–1994).svg|22px|border]] [[w:Венда (язык)|Венда]]: '''ndi a ni funa''' («нди а ни фуна»).<p>[[w:Нижнелужицкий язык|Вендский]]: см. Нижнелужицкий
# [[Файл:Flag of RTC.gif|22px|border]] [[w:Wenedyk|Венедик]]: '''jo jemu cie''' («йо йему че»).
# [[Файл:Flag of Veneto.svg|22px|border]] [[w:Современный венетский язык|Венетский]]: '''te amo''' («тэ амо»).
# [[Файл:Flag of Vepsia.svg|22px|border]] [[w:Вепсский язык|Вепсский]]: '''minä armastan sindai''' («ми́ня а́рмастан си́ндай»).<p>[[w:Южноюкагирский язык|Верхнеколымский]]: см. Южноюкагирский
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Верхнекускоквимский язык|Верхнекускоквимский]]: '''nughistsin'''' («нухистсин»).
# [[Файл:Upper Sorbian Flag.gif|22px|border]] [[w:Верхнелужицкий язык|Верхнелужицкий]]: '''ja će lubuju''' («я че лубую»).
# [[Файл:Flag of Saxony.svg|22px|border]] [[w:Верхнесаксонский диалект|Верхнесаксонский]]: '''isch hab dsch gerne''' («иш хаб дш гэрнэ»).
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Верхний танана|Верхний танана]]: '''naa dithistsįį''' («наа диистсин»), '''naa ihtsįį''' («наа итсин»).
# [[File:Västerbottens län vapenflagga.svg|22px|border]] [[w:Диалекты шведского языка|Вестерботтенский шведский]]: '''ja elschke degg''' («я эльшке дэгг»).<p>[[w:Северо-западный марийский язык|Ветлужский марийский]]: см. Северо-западный марийский
# [[Файл:Flag of the Republic of the Congo.svg|22px|border]] [[w:en:Vili language|Вили]]: '''mi be ku zole''' («ми бэ ку золе»).
# [[Файл:POL Wilamowice COA.svg|14px|border]] [[w:Вилямовский язык|Вилямовский]]: '''yh ho dih gan''' («их хо дих ган»).<p>[[w:Варайский язык|Винарайский]]: см. Варайский
# [[Файл:Pine Ridge Flag.svg|22px|border]] [[w:Виннебаго (язык)|Виннебаго]]: '''haipį́na''' («хаипинна»).
# [[Файл:Flag of Vlachs in Serbia.svg|22px|border]] [[w:Влашский язык (Сербия)|Влашский]]: '''ti voi''' («ти вой»).<p>[[w:Мансийский язык|Вогульский]]: см. Мансийский
# [[Файл:Votic Flag.svg|22px|border]] [[w:Водский язык|Водский]]: '''miä suvaan sinua''' («миа суваан синуа»).<p>[[w:Воламо|Волайта]]: см. Воламо
# [[Файл:Flag of the Southern Nations, Nationalities, and Peoples' Region.svg|22px|border]] [[w:Воламо|Воламо]]: '''ta nena siiqays''' («та нэна сиикайс»), '''taani nena siiqays''' («таани нэна сиикайс»).
# [[Файл:Flag of Volapük.svg|22px|border]] [[w:Волапюк|Волапюк]]: '''löfob oli''' («лёфоб оли»).
# [[Файл:Flag of Senegal.svg|22px|border]] [[w:Волоф (язык)|Волоф]]: '''sopp naa la''' («сопп наа ля»), '''nopp naa la''' («нопп наа ля»), '''dama la nob''' («дама ля ноб»).<p>[[w:Волапюк|Воляпюк]]: см. Волапюк<p>[[w:Кабардино-черкесский язык|Восточно-адыгский]]: см. Кабардино-черкесский
# [[Файл:Flag of Artsakh.svg|22px|border]] [[w:Восточноармянский язык|Восточноармянский]]: '''ես քե սիրում ում''' («ес кэ сирум ум»).
# [[Файл:Flag of New Mexico.svg|22px|border]] [[w:Кересские языки|Восточнокересский (кацтья)]]: '''shro-tse-mah''' («шро-тсе-ма»).<p>[[w:Фиджийский язык|Восточнофиджийский]]: см. Фиджийский
# [[Файл:Flag of East Frisia.svg|22px|border]] [[w:Восточнофризский язык|Восточнофризский]]: '''iek hääb die ljoo''' («ик хейб ди лийёо»).<p>[[w:Панджаби|Восточный панджаби]]: см. Панджаби<p>[[w:Фриульский язык|Восточный ретороманский]]: см. Фриульский
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Vute language|Вуте]]: '''ma wou ndoune''' («ма ву ндуне»).<p>[[w:Выруский диалект|Выро]]: см. Выруский
# [[Файл:Flag of the Võro People.svg|22px|border]] [[w:Выруский диалект|Выруский]]: '''(ma) armasta sinno''' («(ма) армаста синно»).
# [[Файл:Bandera de Alto Valirio.png|22px|border]] [[w:Валирийские языки|Высокий валирийский]]: '''avy jorrāelan''' («авю джоррааэлан»).
# [[Файл:Flag of South Vietnam.svg|22px|border]] [[w:Вьетнамский язык|Вьетнамский]]: '''anh yêu em''' («ан йеу эм») — женщине; '''em yêu anh''' («эм йеу ан») — мужчине.
# [[Файл:Flag of Ghana.svg|22px|border]] [[w:Га (язык)|Га]]: '''miisumo bo''' («мийсумо бо»).
# [[Файл:Kanaka Maoli flag.svg|22px|border]] [[w:Гавайский язык|Гавайский]]: '''aloha wau iâ 'oe''' («алоха уау иа ое»).
# [[Файл:Flag of the Gagauz people.svg|22px|border]] [[w:Гагаузский язык|Гагаузский]]: '''bän seni severim''' («бянь сени северимь»).
# [[Файл:Flag of Haiti.svg|22px|border]] [[w:Гаитянский креольский язык|Гаитянский креольский]]: '''mwen renmen’w''' («муен ренмен у»).
# [[Файл:Flag of Guyana.svg|22px|border]] [[w:Гайанский креольский язык|Гайанский креольский]]: '''i love yuh''' («а лов ю»).
# [[Файл:Flag of Galicia.svg|22px|border]] [[w:Галисийский язык|Галисийский]]: '''ámote''' («амотэ») — формально; '''quérote''' («кэротэ») — неформально.<p>[[w:Оромо (язык)|Галла]]: см. Оромо
# [[Файл:Flag of Brittany (Gwenn ha du).svg|22px|border]] [[w:Галло (язык)|Галло]]: '''j'sea un diot do tae''' («жьсэа ан дьё до тэ»).
# [[Файл:Flag of Manipur (stripes variant).svg|22px|border]] [[w:en:Gangte language|Гангте]]: '''kahun lungsiet na hi''' («кахун лунгсиет на хи»), '''kahung lungsiet hi''' («кахунг лунгсиет хи»), '''kalungsiet na hi''' («калунгсиет на хи»).<p>[[w:Луганда|Ганда]]: см. Луганда<p>[[w:Воламо|Ганта]]: см. Воламо
# [[Файл:Flag of Garifuna.svg|22px|border]] [[w:Гарифуна (язык)|Гарифуна]]: '''hínsiñetibu nun''' («хинсиньетибу нун»).
# [[Файл:In garoland.svg|22px|border]] [[w:Гаро (язык)|Гаро]]: '''anga nangna ka'sara''' («анга нангна ка'сара»).
# [[Файл:Flag of South West State of Somalia.svg|22px|border]] [[w:en:Garre language|Гарре]]: '''si feda''' («си фэда»).
# [[Файл:Flag of the Princely State of Tehri Garhwal.svg|22px|border]] [[w:en:Garhwali language|Гархвали]]: '''mithe twe batin agnaan chh''' («митхэ твэ батин агнаан кх»).
# [[Файл:Gascogne drapeau.svg|22px|border]] [[w:Гасконский язык|Гасконский]]: '''que v's aimi''' («кё взэми») - формально, '''que t'aimi''' («ке тэми») - неформально.<p>[[w:Шотландский язык (кельтский)|Гаэльский]]: см. Шотландский (кельтский)
# [[Файл:Flag of the Central African Republic.svg|22px|border]] [[w:en:Gbaya languages|Гбайя]]: '''mi ko me''' («ми ко мэ»).
# [[Файл:Unofficial flag of Guadeloupe (local).svg|22px|border]] [[w:Антильский франко-креольский язык|Гваделупский франко-креольский]]: '''an enmé'w''' («ан эмэу»).<p>[[w:Сукума (язык)|Гве]]: см. Сукума
# [[Файл:Flag of Guinea-Bissau.svg|22px|border]] [[w:Гвинейский креольский язык|Гвинейский креольский]]: '''n gosta di bo''' («н госта ди бо»).
# [[Файл:Flag of the Northwest Territories.svg|22px|border]] [[w:Гвичин|Гвичин]]: '''neet’ihthan''' («неет’ихсан»).<p>[[w:Ген (язык)|Ге]]: см. Ген<p>[[w:Геэз|Геез]]: см. Геэз
# [[Файл:Flag of Togo (3-2).svg|22px|border]] [[w:Ген (язык)|Ген]]: '''un lon o''' («ун лон о»).<p>[[w:Ген (язык)|Генгбе]]: см. Ген<p>[[w:Пуэльче (язык)|Геннакен]]: см. Пуэльче
# [[Файл:Flag of Genoa.svg|22px|border]] [[w:Генуэзский диалект лигурского языка|Генуэзский лигурский]]: '''te ammu'''' («тэ амму»).
# [[Файл:Flag of Hereroland.svg|22px|border]] [[w:Гереро (язык)|Гереро]]: '''mbeku suvera''' («мбэку сувэра»).
# [[Файл:Flag of Guernsey.svg|22px|border]] [[w:Гернсийский диалект нормандского языка|Гернсийский нормандский]]: '''j’t’oïme''' («жьтойм»).
# [[Файл:Flag of Guerrero.svg|22px|border]] [[w:Геррерский науатль|Геррерский науатль]]: '''nimitstlasojtla''' («нимитстласойтла»).
# [[File:Flag of Hesse (state).svg|22px|border]] [[w:Гессенский диалект|Гессенский немецкий]]: '''isch habb disch libb''' («иш хабб диш либб»).
# [[Файл:Ethiopian Pennants.svg|22px|border]] [[w:Геэз|Геэз]]: '''አፈቀረኪ''' («афекераки») - женщине, '''አፈቀረካ''' («афекерека») - мужчине.<p>[[w:Кикуйю (язык)|Гикуйю]]: см. Кикуйю<p>[[w:Кирибати (язык)|Гилбертский]]: см. Кирибати<p>[[w:Гилянский язык|Гиляки]]: см. Гилянский
# [[Файл:Flag of Persian Socialist Soviet Republic.svg|22px|border]] [[w:Гилянский язык|Гилянский]]: '''تی سرور دانم''' («ти сервире данам»).<p>[[w:Нивхский язык|Гиляцкий]]: см. Нивхский
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Гитксан|Гитксан]]: '''ansip'iny niin''' («ансип'ини ниин»).
# [[Файл:POL Gliwice flag.svg|22px|border]] [[w:Силезский язык|Гливицкий силезский]]: '''przajã ci''' («пшая чи»).
# [[Файл:Glosa flag.jpg|22px|border]] [[w:Глоса|Глоса]]: '''mi amo tu''' («ми амо ту»).
# [[Файл:Flag of Goa.svg|22px|border]] [[w:Конкани (язык)|Гоанский конкани]]: '''हांव तुजेर मोग करता''' («ту магель мога чо»).<p>[[w:Гуахиро (язык)|Гоахиро]]: см. Гуахиро
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Годоберинский язык|Годоберинский]]: '''ден мин идалъавда''' («дэн мин идалавда»).<p>[[w:Нидерландский язык|Голландский]]: см. Нидерландский
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Гомала|Гомала]]: '''me kwoh wu''' («мэ кво ву»).
# [[Файл:Flag of Maharashtra Navnirman Sena.svg|22px|border]] [[w:Гонди (язык)|Гонди]]: '''ana nik pirem kiyatona''' («ана ник пирэм кийятона»).
# [[Файл:Flag of Gornomariysky rayon.svg|22px|border]] [[w:Горномарийский язык|Горномарийский]]: '''мый тыйым яратем''' («мынь тыньим яратэм»).<p>[[w:Димаса|Горный качари]]: см. Димаса
# [[Файл:Ethnic flag of Tat people (Caucasus).svg|22px|border]] [[w:Горско-еврейский язык|Горско-еврейский]]: '''mə tyrə xosdənym''' («мэ тюрэ хосдэнюм»).
# [[Файл:Gothic Sunrise Flag.jpg|22px|border]] [[w:Готский язык|Готский]]: '''𐌹𐌺 𐍆𐍂𐌹𐌾𐍉 𐌸𐌿𐌺''' («ик фри́йо сук»).<p>[[w:Нама|Готтентотский]]: см. Нама<p>[[w:Романшский язык|Граубюнденский]]: см. Романшский<p>[[w:Романшский язык|Граубюндер]]: см. Романшский
# [[Файл:Flag of Greenland.svg|22px|border]] [[w:Гренландский язык|Гренландский]]: '''asavakit''' («асавакит»).
# [[Файл:Flag of Greece.svg|22px|border]] [[w:Греческий язык|Греческий]]: '''σ΄αγαπώ''' («сагапо») — неформально, '''σας αγαπώ''' («сас агапо») — формально.
# [[Файл:Flag of Groningen.svg|22px|border]] [[w:Гронингенский диалект|Гронингенский]]: '''k hol van die''' («к хол ван ди»).
# [[Файл:Flag of Georgia official.svg|22px|border]] [[w:Грузинский язык|Грузинский]]: '''მიყვარხარ''' («миквархар»).<p>[[w:Юэ (язык)|Гуандунский]]: см. Юэ<p>[[w:Кантонский диалект|Гуанчжоуский]]: см. Кантонский<p>[[w:Севернокитайский язык|Гуаньхуа]]: см. Севернокитайский
# [[Файл:Guarani flag.svg|22px|border]] [[w:Гуарани (язык)|Гуарани]]: '''rojhayhû''' («рохаыху»).
# [[Файл:Panteera Wayuu Aumentado.png|22px|border]] [[w:Гуахиро (язык)|Гуахиро]]: '''aisü pia tapüla''' («аисы пиа тапыра»).
# [[Файл:Flag of the Gujarat Sultanate.svg|22px|border]] [[w:Гуджарати|Гуджарати]]: '''હું તને પ્રેમ કરું છુ''' («хум танээ прээма карум чу»).<p>[[w:Гуджарати|Гуджаратский]]: см. Гуджарати<p>[[w:Гусии (язык)|Гузии]]: см. Гусии
# [[Файл:Flag of Benin.svg|22px|border]] [[w:Гун (язык)|Гун]]: '''n’yiwanna we''' («ньиванна вэ»).
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Гунзибский язык|Гунзибский]]: '''диъи дуго атӏ''' («дии дуго ата») - женщине, '''диъи дуго й-атӏ''' («дии дуго йата») - мужчине.
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Гусии (язык)|Гусии]]: '''nigwachete''' («нигвачэтэ»).<p>[[w:Пуэльче (язык)|Гынына-кынэ]]: см. Пуэльче<p>[[w:Пуэльче (язык)|Гынына-яхэч]]: см. Пуэльче<p>[[w:Геэз|Гыыз]]: см. Геэз<p>[[w:Шотландский язык (кельтский)|Гэльский]]: см. Шотландский (кельтский)
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Дабида (язык)|Дабида]]: '''nakukunde''' («накукунде»).<p>[[w:Дагбани (язык)|Дагбамба]]: см. Дагбани
# [[Файл:Flag of Ghana.svg|22px|border]] [[w:Дагбани (язык)|Дагбани]]: '''mi ndigui''' («ми ндигуи»).<p>[[w:Дагбани (язык)|Дагомба]]: см. Дагбани<p>[[w:Румынский язык|Дако-румынский]]: см. Румынский
# [[Файл:Pine Ridge Flag.svg|22px|border]] [[w:Дакота (сиуанский язык)|Дакота]]: '''thečhíȟiŋda''' («тхэчихинда»).<p>[[w:Эльвдальский диалект|Далекарлийский]]: см. Эльвдальский<p>[[w:Афарский язык|Данакильский]]: см. Афарский<p>[[w:Адангме (язык)|Дангме]]: см. Адангме
# [[Файл:Даргинский флаг (неофициальный, одна из народных вариаций).jpg|22px|border]] [[w:Даргинский литературный язык|Даргинский]]: '''хӀу наб ригахъуре''' («хӀу наб ригахъуре»).
# [[Файл:Flag of Afghanistan (2013-2021).svg|22px|border]] [[w:Дари|Дари]]: '''دوستت دارم''' («достат даарам»).
# [[Файл:Flag of Denmark.svg|22px|border]] [[w:Датский язык|Датский]]: '''jeg elsker dig''' («я эльска да»).
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Дег-хитан (язык)|Дег-хитан]]: '''inga dist’a''' («инга дист'а»).<p>[[w:Чипевайан (язык)|Дене Сулин]]: см. Чипевайан
# [[Файл:Flag of All Assam Tribal Sangha.svg|22px|border]] [[w:Деори (язык)|Деори]]: '''aa nona chu nimai''' («аа нона чу нимаи»).
# [[Файл:Flag of FLNKS.svg|22px|border]] [[w:Деху|Деху]]: '''eni a hnimi eö''' («эни аними эё»).<p>[[w:Сефардский язык|Джудезмо]]: см. Сефардский<p>[[w:Сефардский язык|Джудекко]]: см. Сефардский<p>[[w:Ндюка|Джука]]: см. Ндюка<p>[[w:Горско-еврейский язык|Джуури]]: см. Горско-еврейский<p>[[w:Горско-еврейский язык|Джухури]]: см. Горско-еврейский<p>[[w:Дзонг-кэ|Дзонгкха]]: см. Дзонг-кэ
# [[Файл:Flag of Bhutan.svg|22px|border]] [[w:Дзонг-кэ|Дзонг-кэ]]: '''ང་གིས་ཁྱོད་ལུ་བྱམས་པ་འབདཝ་ཨིན''' («нга ги че лу га»).
# [[Файл:Flag of Gambia.svg|22px|border]] [[w:en:Jahanka language|Джаханка]]: '''n’gné kanou''' («ннэ кану»).<p>[[w:Зарма (язык)|Джерма]]: см. Зарма
# [[Файл:Flag of Jersey.svg|22px|border]] [[w:Джерсийский диалект нормандского языка|Джерсийский нормандский]]: '''j’t’aime''' («жьтэм»).<p>[[w:Мальдивский язык|Дивехи]]: см. Мальдивский<p>[[w:Цезский язык|Дидойский]]: см. Цезский
# [[Файл:Flag of All Assam Tribal Sangha.svg|22px|border]] [[w:Димаса (язык)|Димаса]]: '''ang ningkhe hamjaodu''' («анг нингкхе хамджаоду»).<p>[[w:Димаса|Димаса-качари]]: см. Димаса
# [[Файл:Flag of Dinka people.svg|22px|border]] [[w:Динка (язык)|Динка]]: '''yin nhiar''' («йин ниар»).
# [[Файл:Flag of Senegal.svg|22px|border]] [[w:en:Jola-Fonyi language|Диола-фоньи]]: '''nifañifañ''' («нифаньифань»).
# [[Файл:Flag of Senegal.svg|22px|border]] [[w:en:Jola-Fonyi language|Диола-фоньи (булуф)]]: '''i mãnghy mãng''' («и манни манг»).
# [[Файл:Flag of Jammu and Kashmir (1952-2019).svg|22px|border]] [[w:Догри|Догри]]: '''में तुगी हिरख करना''' («минджо тэрэ наал пьяр хэга»).
# [[Файл:Flag of Taymyr Autonomous Okrug.svg|22px|border]] [[w:Долганский язык|Долганский]]: '''мин энигин таптыыбын''' («мин энигин таптыыбын»), '''мен эничан таптычан''' («мен эничан таптычан»).<p>[[w:Ладинский язык|Доломитский]]: см. Ладинский
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Долуо|Долуо]]: '''aheri''' («ахери»).
# [[Файл:Flag of the Romani people.svg|22px|border]] [[w:Домари|Домари]]: '''ḥaskarmèré''' («хаскармере»), '''āmā tèrs ḥaskarmé''' («аамаа терс хаскарме»).
# [[Файл:Dothraki flag.webp|22px|border]] [[w:Дотракийский язык|Дотракийский]]: '''anha zhilak yera''' («анха жилак йера»).<p>[[w:Дотракийский язык|Дотракин]]: см. Дотракийский
# [[Файл:White Dragon Flag of England.png|22px|border]] [[w:Древнеанглийский язык|Древнеанглийский]]: '''ic lufie þe''' («ик луфи тэ»).
# [[Файл:Banner of the Holy Roman Emperor with haloes (1400-1806).svg|22px|border]] [[w:Древневерхненемецкий язык|Древневерхненемецкий]]: '''ich minne dich''' («ихь миннэ дихь»).<p>[[w:Древнерусский|Древневосточнославянский]]: см. Древнерусский
# [[Файл:Ancient Greek Culture Flag.jpg|22px|border]] [[w:Древнегреческий язык|Древнегреческий]]: '''σὲ φιλῶ''' («сэ фило»), '''σὲ ἀγαπῶ''' («сэ агапо»).
# [[Файл:Flag Kingdom of Judah.png|22px|border]] [[w:Древнееврейский язык|Древнееврейский]]: '''אהבתיך''' («аhавти́х») - женщине; '''אהבתיך''' («аhaвти́ха») — мужчине.
# [[Файл:Banner of the Lordship of Ireland.svg|22px|border]] [[w:Древнеирландский язык|Древнеирландский]]: '''notcharaim''' («нод харэйм»).
# [[Файл:Flag of The Icelandic Commonwealth.svg|22px|border]] [[w:en:Old Norse#Old Icelandic|Древнеисландский]]: '''ann ek þér''' («анн эк сэр»).<p>[[w:Прусский язык|Древнепрусский]]: см. Прусский
# [[Файл:Banner of the Novgorod Republic (c. 1385).svg|22px|border]] [[w:Древнерусский язык|Древнерусский]]: '''люблѭ тѧ''' («люблю тя»).<p>[[w:Древнескандинавский язык|Древнесеверный]]: см. Древнескандинавский
# [[Файл:Raven Banner.svg|22px|border]] [[w:Древнескандинавский язык|Древнескандинавский]]: '''ek elska þik''' («эк эльска сик»), '''ek ann þér''' («эк анн сэр»).<p>[[w:Орхоно-енисейский язык|Древнетюркский]]: см. Орхоно-енисейский
# [[Файл:Oriflamme of Charlemagne.png|22px|border]] [[w:Древнефранкский язык|Древнефранкский]]: '''ik minnon thigh''' («ик миннон тиг»).
# [[Файл:Sweden-Flag-1562.svg|22px|border]] [[w:Древнешведский язык|Древнешведский]]: '''iak elski þik''' («як эльски сик»).<p>[[w:Геэз|Древнеэфиопский]]: см. Геэз<p>[[w:Древнееврейский язык|Древний иврит]]: см. Древнееврейский<p>[[w:Полабский язык|Древяно-полабский]]: см. Полабский
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Дуала (язык)|Дуала]]: '''na tondi wa''' («на тонди ва»).
# [[Файл:ТУНГАНИСТАНСКИЙ ФЛАГ.png|22px|border]] [[w:Дунганский язык|Дунганский (ганьсуйский)]]: '''вә э ни''' («вэ ай ни»).
# [[Файл:ТУНГАНИСТАНСКИЙ ФЛАГ.png|22px|border]] [[w:Дунганский язык|Дунганский (шэньсийский)]]: '''ңə э ни''' («нэ ай ни»).
# [[Файл:Flag of Sabah.svg|22px|border]] [[w:en:Dusun language|Дусунский]]: '''siuhang oku dia''' («сиуханг оку диа»).<p>[[w:Мальдивский язык|Дхивехи]]: см. Мальдивский
# [[Файл:Flag of Burkina Faso.svg|22px|border]] [[w:Дьюла (язык)|Дьюла]]: '''m'bi fê''' («мби фэ»).<p>[[w:Сефардский язык|Еврейско-испанский]]: см. Сефардский<p>[[w:Горско-еврейский язык|Еврейско-татский]]: см. Горско-еврейский
# [[Файл:Ancient Egyptian Culture Flag.webp|22px|border]] [[w:Египетский язык|Египетский]]: '''𓌻𓂋𓀁𓀀𓍿𓈖''' («мери и чен») — женщине, '''𓌻𓂋𓀁𓁐𓍿𓅱''' («мери и чю») — мужчине.
# [[Файл:Flag of Egypt.svg|22px|border]] [[w:Египетский диалект арабского языка|Египетский арабский]]: '''ٲنَا بحِبَّك''' («ана бахиббак») — мужчине, '''ٲنَا بَحِبِّك''' («ана бахиббик») — женщине.
# [[Файл:Flag of Yenish people.svg|22px|border]] [[w:Енишский язык|Енишский]]: '''ketta jaan bettu''' («кэтта яан бэтту»).
# [[Файл:Bandeira do Amazonas.svg|22px|border]] [[w:Жамамади|Жамамади]]: '''tera oka-nofi-beya''' («тэра ока нофи бэя»).
# [[Файл:Flag of Žemaitija.svg|22px|border]] [[w:Жемайтское наречие|Жемайтский]]: '''aš tavi mīlu''' («аш тави миилю»).
# [[Файл:Flag of Montreal.svg|22px|border]] [[w:Жуаль|Жуаль]]: '''sh’teme''' («щтэмэ»).
# [[Файл:Flag of Zazaistan.svg|22px|border]] [[w:Зазаки|Зазаки]]: '''ez to has kenan''' («эз то хас кэнан»).<p>[[w:Адыгейский язык|Западноадыгский]]: см. Адыгейский
# [[Файл:Flag of Arizona.svg|22px|border]] [[w:Западно-апачский язык|Западно-апачский]]: '''sheth she'n zho'n''' («шэз шэн жон»).
# [[Файл:Flag of Western Armenia.svg|22px|border]] [[w:Западноармянский язык|Западноармянский]]: '''ես քեզ կը սիրեմ''' («йез кез гэсирем»).
# [[Файл:Banner of the Duchy of Brabant.svg|22px|border]] [[w:Западнобрабантский диалект|Западнобрабантский]]: ''''k zien a gère''' («кзьен а херэ»).<p>[[w:Ительменский язык|Западноительменский]]: см. Ительменский
# [[File:Flag of New Mexico.svg|22px|border]] [[w:Кересские языки|Западнокересский (ааку)]]: '''thro sii muu''' («сро сии муу»).
# [[File:Flag of New Mexico.svg|22px|border]] [[w:Кересские языки|Западнокересский (каваик)]]: '''аmuu-thro-maa''' («амуу-сро-маа»).<p>[[w:Нижнесаксонские диалекты|Западнонижненемецкий]]: см. Нижнесаксонский
# [[Файл:Touareg People Flag.svg|22px|border]] [[w:Западнотамахакский язык|Западнотамахакский]]: '''riɣ-kem''' («риг кем»).
# [[Файл:Flag of West Flanders.svg|22px|border]] [[w:Западнофламандские диалекты|Западнофламандский]]: '''ik zie oe geerne''' («ик зие ое геерне»).
# [[Файл:Frisian flag.svg|22px|border]] [[w:Западнофризский язык|Западнофризский]]: '''ik hâld fan dy''' («и холд фром тай»).
# [[Файл:Flag of the Oromo Liberation Front.svg|22px|border]] [[w:Оромо (язык)|Западно-центральный оромо]]: '''si jaalladha''' («си джаалладха»).
# [[Файл:Bandera Front Alliberament Cham.svg|22px|border]] [[w:Чамский язык|Западночамский]]: '''ai ranam dai''' («аи ранам даи») — женщине; '''dai ranam ai''' («даи ранам аи») — мужчине.<p>[[w:Адыгейский язык|Западночеркесский]]: см. Адыгейский
# [[File:Piapot First Nation flag.svg|22px|border]] [[w:Западный кри|Западный кри]]: '''ᑭᓵᑭᐦᐃᑎᐣ''' («кисаакихитин»).<p>[[w:Бабин-вицувитен|Западный кэрриер]]: см. Бабин-вицувитен<p>[[w:Луба (язык)|Западный луба]]: см. Луба
# [[Файл:Flag of Mali.svg|22px|border]] [[w:en:Kassonke language|Западный манинка]]: '''mbi fê''' («мби фэ»).
# [[Файл:Flag of Manitoba.svg|22px|border]] [[w:Западный оджибве|Западный оджибве]]: '''kisawēnmin''' («кисавеенмин»).<p>[[w:Западнотамахакский язык|Западный тамахак]]: см. Западнотамахакский
# [[Файл:DEU Saterland Flag.svg|22px|border]] [[w:Затерландский фризский язык|Затерландский фризский]]: '''iek hääb die ljoo''' («иэ хээб ди йо»).
# [[Файл:Flag of Niger.svg|22px|border]] [[w:Зарма (язык)|Зарма]]: '''aye ga banin''' («айе га банин»).
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Зибирхалинский диалект|Зибирхалинский]]: '''дени мини идалъавда''' («дэни мини идалавда»).<p>[[w:Северный ндебеле|Зимбабвийский ндебеле]]: см. Северный ндебеле
# [[Файл:Flag of Zulu.png|22px|border]] [[w:Зулу|Зулу]]: '''ngiyakuthanda''' («нийякутанда»).<p>[[w:Зулу|Зулусский]]: см. Зулу
# [[Файл:Flag of Zuni.svg|22px|border]] [[w:Зуни (язык)|Зуни]]: '''tom ho’ ichema''' («том хо ичема»).<p>[[w:Зуни (язык)|Зуньи]]: см. Зуни
# [[Файл:PH-BEN Flag.png|22px|border]] [[w:en:Ibaloi language|Ибалои]]: '''pip-piyan taha''' («пип-пийан таха»).
# [[Файл:Flag of Cagayan.svg|22px|border]] [[w:en:Ibanag language|Ибанаг]]: '''iddu taka''' («идду така»).
# [[Файл:Flag of Sarawak.svg|22px|border]] [[w:Ибанский язык|Ибанский]]: '''aku sayau ke nuan''' («аку сайяу ке нуан»).
# [[Файл:Flag of Biafra.svg|22px|border]] [[w:Ибибио (язык)|Ибибио]]: '''mma ma fi''' («мма ма фи»).
# [[Файл:Batanes Flag.png|22px|border]] [[w:en:Ivatan language|Иватанский]]: '''chaddao kuymu''' («чаддао куйму»), '''ichaddao ko imu''' («ичаддао ко иму»).<p>[[w:Мегрельский язык|Иверский]]: см. Мегрельский
# [[Файл:Flag of Israel.svg|22px|border]] [[w:Иврит|Иврит]]: '''אני אוהב אותך''' («ани оев отах») — женщине; '''אני אוהבת אותך''' («ани оевет отха») — мужчине.
# [[Файл:Flag of Biafra.svg|22px|border]] [[w:Игбо (язык)|Игбо]]: '''m hụrụ gị n’anya''' («мхууруу гии н анья»).<p>[[w:Тиндинский язык|Идаринский]]: см. Тиндинский<p>[[w:Тиндинский язык|Идеринский]]: см. Тиндинский
# [[Файл:Proposed Yiddish flag.svg|22px|border]] [[w:Идиш|Идиш]]: '''איך האָב דיך ליב''' («их об дих либ»).
# [[Файл:Flag of Ido.svg|22px|border]] [[w:Идо|Идо]]: '''me amoras tu''' («мэ аморас ту»).
# [[Файл:Flag igora.svg|22px|border]] [[w:Ижорский язык|Ижорский]]: '''miä suvvaan sinnua''' («миэ сувваан синнуа»).
# [[Файл:Flag of Uganda.svg|22px|border]] [[w:Ик (язык)|Ик]]: '''tsamia bi''' («тсамиа би»).<p>[[w:Илоканский язык|Илокано]]: см. Илоканский
# [[Файл:Flag of Ilocos Sur.svg|22px|border]] [[w:Илоканский язык|Илоканский]]: '''ayayatenka''' («айяйятэнка»), '''ipatpategka''' («ипатпатэгка»), '''ikarkarayoka''' («икаркарайока»).<p>[[w:Илоканский язык|Илоко]]: см. Илоканский<p>[[w:Хилигайнон|Илонгго]]: см. Хилигайнон
# [[Файл:Flag of the Syriac-Aramaic People.svg|22px|border]] [[w:en:Imperial Aramaic|Имперский арамейский]]: '''ܟܠܐܢܐ ܡܝܪ''' («кльнь мир»).
# [[Файл:Sami flag.svg|22px|border]] [[w:Инари-саамский язык|Инари-саамский]]: '''mun rähistâm tuu''' («мун ра́хистам туу»).
# [[Файл:Flag of Ingushetia.svg|22px|border]] [[w:Ингушский язык|Ингушский]]: '''хьо сона еза''' («хо сона еза») — женщине; '''хьо сона веза''' («хо сона веза») — мужчине.
# [[Файл:Flag of Indonesia.svg|22px|border]] [[w:Индонезийский язык|Индонезийский]]: '''aku cinta kamu''' («аку чинта камуу»).<p>[[w:Монтанье-наскапи (язык)|Инну]]: см. Монтанье-наскапи<p>[[w:Монтанье-наскапи (язык)|Инну-аймун]]: см. Монтанье-наскапи
# [[Файл:Conlangflag.svg|22px|border]] [[w:Интерглосса|Интерглосса]]: '''mi amo tu''' («ми амо ту»), '''mi esthe philo tu''' («ми эстэ фило ту»).
# [[Файл:Interlingua2.png|22px|border]] [[w:Интерлингва (IALA)|Интерлингва]]: '''io te ama''' («ио тэ ама»).<p>[[w:Окциденталь|Интерлингве]]: см. Окциденталь
# [[Файл:Flag of Nunavut.svg|22px|border]] [[w:Инуиннактун|Инуиннактун]]: '''piqpagiyagin''' («пикпагийягин»).
# [[Файл:Flag of Nunavik (Thomassie Mangiok).svg|22px|border]] [[w:Инуктитут|Инуктитут]]: '''ᓇᒡᓕᒋᕙᒋᑦ''' («наглигивагит»).
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:en:Iñupiaq language|Инупиак]]: '''nakuagigikpin''' («накуагигикпин»), '''nakuagikpakpin''' («накуагикпакпин»), '''piqpavagin''' («пикпавагин»).
# [[Файл:Flag of Bangsamoro.svg|22px|border]] [[w:en:Iranun language|Иранун]]: '''kababayaan ku ska''' («кабабайаан ку ска»).
# [[Файл:Flag of Ireland.svg|22px|border]] [[w:Ирландский язык|Ирландский]]: '''gráím thú''' («грэм ту»).<p>[[w:Ирландский язык|Ирландский гэльский]]: см. Ирландский
# [[Файл:Flag of Thailand.svg|22px|border]] [[w:Исанский язык|Исанский]]: '''ຂ້ອຍຮັກເຈົ້າ''' («кхёой хак чао»).
# [[Файл:Flag of Iceland.svg|22px|border]] [[w:Исландский язык|Исландский]]: '''ég elska þig''' («эг эльска сиг»).
# [[Файл:Delta State Flag.gif|22px|border]] [[w:en:Isoko language|Исоко]]: '''me you owhai''' («мэ ю оухаи»).
# [[Файл:Flag of Spain.svg|22px|border]] [[w:Испанский язык|Испанский]]: '''te quiero''' («тэ керо»).
# [[Файл:Flag of the Romani people.svg|22px|border]] [[w:Испанский кало|Испанский кало]]: '''te camelo''' («тэ камэло»).
# [[Файл:Flag of the Zapotec Peoples.svg|22px|border]] [[w:Истмусский сапотекский язык|Истмусский сапотекский]]: '''nadxieeʼ lii''' («наджиее лии»).
# [[Файл:Flag of Istria (historical).svg|22px|border]] [[w:Истрорумынский язык|Истрорумынский]]: '''te iubeåć''' («тэ юбэач»).
# [[Файл:Insignia-coatofarms-isfahan-province.png|22px|border]] [[w:en:Esfahani accent|Исфаханский персидский]]: '''شومارو دوس دارم''' («шума ру дус дарэм»).
# [[Файл:Flag of Italy.svg|22px|border]] [[w:Итальянский язык|Итальянский]]: '''ti amo''' («ти амо»).
# [[Файл:Flag of Kamchatka Oblast.svg|22px|border]] [[w:Ительменский язык|Ительменский]]: '''кәмман кәзза тлфталакзусчэн''' («кымман кызза тлфталакзусчэн»).
# [[Файл:Flag of the Patujú flower.svg|22px|border]] [[w:Итонама (язык)|Итонама]]: '''ka’k’isilo’ne’mo''' («ка к исило нэ мо») — женщине; '''a’k’isilo’ne’mo''' («а к исило нэ мо») — мужчине.
# [[Файл:Conlangflag.svg|22px|border]] Иульджи: '''я любюніаі а ято''' («я любюниаи а ято»), '''я любюніаі а ята''' («я любюниаи а ята»).
# [[Файл:Ithkuil Flag.webp|22px|border]] [[w:Ифкуиль|Ифкуиль]]: '''ôžasi ükhi''' («ожаси юкхи»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Ица (язык)|Ица]]: '''ink’atech''' («инкатэч»).<p>[[w:Ишильский язык|Ишиль]]: см. Ишильский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Ишильский язык|Ишильский]]: '''nikin sa' axh''' («никин са аш»).
# [[Файл:Conlangflag.svg|22px|border]] Иширкиан: '''nukisi''' («нукиси»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Йемба|Йемба]]: '''men nkon' wou''' («мэн нкон ву»).
# [[Файл:Sami flag.svg|22px|border]] [[w:Йоканьгско-саамский язык|Йоканьгско-саамский]]: '''мунн со̄бпам то̄ны''' («мунн со́бпам то́ны»).
# [[Файл:Flag of county Wexford.svg|22px|border]] [[w:Йола (язык)|Йола]]: '''ich love thou''' («ихь лав зу»).
# [[Файл:Australian Aboriginal Flag.svg|22px|border]] [[w:en:Yolŋu languages|Йолнгу]]: '''ŋarra djäl nhuna''' («нгарра джал нхуна»).
# [[Файл:Flag of Egbe Omo Yoruba.svg|22px|border]] [[w:Йоруба (язык)|Йоруба]]: '''mo nifẹẹ rẹ''' («мо нифеэ рэ»).
# [[Файл:Flag of Kabardino-Balkaria.svg|22px|border]] [[w:Кабардино-черкесский язык|Кабардино-черкесский]]: '''сэ уэ лагун''' («сэ уэ лагун»).
# [[Файл:Flag-kabyle.svg|22px|border]] [[w:Кабильский язык|Кабильский]]: '''hamlaɣkem''' («хамлагкем») — женщине, '''hamlaɣk''' («хамлагк») — мужчине.<p>[[w:Кабувердьяну|Кабо-вердианский креол]]: см. Кабувердьяну<p>[[w:Кабувердьяну|Кабо-вердиану]]: см. Кабувердьяну
# [[Файл:Flag of Cape Verde.svg|22px|border]] [[w:Кабувердьяну|Кабувердьяну]]: '''`n crebu tcheu''' («нкребу тчу»).<p>[[w:Дари|Кабули]]: см. Дари
# [[Файл:Flag of Acadiana.svg|22px|border]] [[w:Каджунский диалект французского языка|Каджунский французский]]: '''mi aime jou''' («ми эм жу»).
# [[Файл:Flag of Kazakhstan.svg|22px|border]] [[w:Казахский язык|Казахский]]: '''мен сені жақсы көремін'''' («мен сени жаксы коремин»).
# [[Файл:Flag of the Iroquois Confederacy.svg|22px|border]] [[w:Кайюга (язык)|Кайюга]]: '''go-nóhkwa’'''' («го-нохква»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Какчикельский язык|Какчикельский]]: '''yatinwajo'''' («йатинуахо»).
# [[Файл:Flag of Montana.svg|22px|border]] [[w:Калиспел|Калиспел]]: '''kʷinχaménč''' («куинхаменч»).
# [[Файл:Flag of Kalmykia.svg|22px|border]] [[w:Калмыцкий язык|Калмыцкий]]: '''би чамд дуртав''' («би чамд дуртав»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Камба (язык, Кения)|Камба]]: '''ningwemdete''' («нингвемдэтэ»).
# [[Файл:Flag of Sardinia.svg|22px|border]] [[w:Кампиданский диалект|Кампиданский сардинский]]: '''deu t’amu''' («дэу тьяму»).<p>[[w:Ительменский язык|Камчадальский]]: см. Ительменский
# [[Файл:Flag of Quebec.svg|22px|border]] [[w:en:Canadian French|Канадский французский]]: '''je t’aime''' («штэм»).
# [[Файл:Flag of the Cordillera Administrative Region.svg|22px|border]] [[w:en:Kankanaey language|Канканаэйский]]: '''laylaydek sik-a''' («лайлайдэк сик-а»).
# [[Файл:Flag of the Kannada people.svg|22px|border]] [[w:Каннада|Каннада]]: '''ನಾ ನಿನ್ನ ಪ್ರೀತಿಸ್ತೀನಿ''' («наа нинна преэтистээни»).
# [[Файл:Flag of Japan.svg|22px|border]] [[w:Кансайский диалект|Кансайский]]: '''好いとんねん''' («суитоннэн»), '''好きやねん''' («сукиянэн»).
# [[Файл:Flag of Hong Kong.svg|22px|border]] [[w:Кантонский диалект|Кантонский]]: '''我愛你''' («нго ой нэй»).
# [[Файл:Flag of the Kanuri people.svg|22px|border]] [[w:Канури (язык)|Канури]]: '''nya raakna''' («нья раакна»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Канхобальский язык|Канхобальский]]: '''chwachwechej''' («чуачуэчех»).<p>[[w:Капампанганский язык|Капампанган]]: см. Капампанганский
# [[Файл:Pampanga Flag.png|22px|border]] [[w:Капампанганский язык|Капампанганский]]: '''kaluguran daka''' («калугуран дака»).
# [[Файл:PH-CAP.png|22px|border]] [[w:en:Capiznon language|Каписнон]]: '''mahal kita''' («махал кита»).<p>[[w:Бежтинский язык|Капучинский]]: см. Бежтинский
# [[Файл:Flag of Artsakh.svg|22px|border]] [[w:Карабахский диалект армянского языка|Карабахский армянский]]: '''ես քեզ սիրում ում''' («ес кез сирум ум»).
# [[Файл:Flag of Crimean Karaites.svg|22px|border]] [[w:Караимский язык|Караимский]]: '''mień sieni siuviam''' («мень сени сювям»).
# [[Файл:Flag of Karakalpakstan.svg|22px|border]] [[w:Каракалпакский язык|Каракалпакский]]: '''мен сени жақсы көремен''' («мен сени жаксы коремен»).<p>[[w:Шона (язык)|Каранга]]: см. Шона
# [[Файл:Karachay Flag.jpg|22px|border]] [[w:Карачаево-балкарский язык|Карачаево-балкарский]]: '''мен сени сюеме''' («мэн сэни сюэмэ»).<p>[[w:Карачаево-балкарский язык|Карачаевский]]: см. Карачаево-балкарский
# [[Файл:Karelian National Flag.svg|22px|border]] [[w:Карельский язык|Карельский]]: '''mie šilma šuvačen''' («мие шилма шувачен»), '''minä armastan sindai''' («миня армастан синдаи»).
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:en:Kari language|Кари]]: '''ni kou zololo''' («ни коу зололо»).
# [[Файл:Flag of Venezuela.svg|22px|border]] [[w:Карибский язык|Карибский]]: '''kysiropimaya''' («кисиропимайя»).
# [[Файл:Bandeira de Rondônia.svg|22px|border]] [[w:Каритиана (язык)|Каритиана]]: '''ÿn mã a'ak''' («юн маа а ак»).<p>[[w:Карук (язык)|Карок]]: см. Карук
# [[Файл:Flag of California.svg|22px|border]] [[w:Карук (язык)|Карук]]: '''nu-'íimnih-tih''' («ну иимни ти»).
# [[Файл:Flag of Senegal.svg|22px|border]] [[w:en:Kasa language|Каса]]: '''lifañifañ''' («лифаньифань»), '''difañifañ''' («дифаньифань»).
# [[Файл:Banner of Castile (Modern Design Variant).svg|22px|border]] [[w:Кастильский диалект|Кастильский]]: '''te amo''' («тэ амо»), '''te quiero''' («тэ керо»).
# [[Файл:Estelada blava.svg|22px|border]] [[w:Каталанский язык|Каталанский]]: '''t’estimo''' («тэстимо»).<p>[[w:Каталанский язык|Каталонский]]: см. Каталанский
# [[Файл:Kachin People Flag.svg|22px|border]] [[w:Качинский язык (цзинпо)|Качинский]]: '''nang hpe ngai tsaw ra ai''' («нэн пе най тсоо рэ ай»).<p>[[w:Кашубский язык|Кашебский]]: см. Кашубский<p>[[w:Кашмирский язык|Кашмири]]: см. Кашмирский
# [[Файл:Jammu Kashmir Liberation Front flag.svg|22px|border]] [[w:Кашмирский язык|Кашмирский]]: '''مےٚ چھِ چٲنؠ ماے''' («мэ чи каань маай»).
# [[Файл:POL Kaszuby flag.svg|22px|border]] [[w:Кашубский язык|Кашубский]]: '''jô cë kòchóm''' («е це квехом»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Квакиутль|Квакиутль]]: '''łaxwala nuxwan tłus''' («ляхьвала нухьван тлюс»).<p>[[w:Багвалинский язык|Кванадинский]]: см. Багвалинский
# [[Файл:Flag of Namibia.svg|22px|border]] [[w:en:Kwangali language|Квангали]]: '''ame naku hara''' («амэ наку хара»).
# [[Файл:Quenya flag.svg|22px|border]] [[w:Квенья|Квенья]]: '''melinyel''' («мэлиньел»), '''amin mela lle''' («амин мэла ллэ»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Кекчи (язык)|Кекчи]]: '''nakatinra''' («накатинра»).
# [[Файл:Flag of Siberia.svg|22px|border]] [[w:Кетский язык|Кетский]]: '''иль укаӈт лювероӈавет''' («иль укант люверонавет»), '''ать у люверовет''' («ать у люверовет»).<p>[[w:Кечуанские языки|Кечва]]: см. Кечуа
# [[Файл:Flag of Cusco (1978–2021).svg|22px|border]] [[w:Кечуанские языки|Кечуа]]: '''canda munani''' («канда мунани»).<p>[[w:Аякучанский кечуа|Кечуа-чанка]]: см. Аякучанский кечуа
# [[Файл:Flagge Köln.svg|22px|border]] [[w:Кёльнский диалект|Кёльнский]]: '''isch han dich leev''' («иш хан диш леев»), '''isch han dich jään''' («иш хан диш еен»).
# [[Файл:Flag of Idaho.svg|22px|border]] [[w:Кёр-д’ален (язык)|Кёр-д'ален]]: '''ku nx̣amínč''' («кунхаминч»).
# [[Файл:Bandera poble Batwa.svg|22px|border]] [[w:en:Kiga language|Кига]]: '''ninkukunda iwe''' («нинкукунда ивэ»).<p>[[w:Тамашек|Кидаль]]: см. Тамашек<p>[[w:Тамашек|Кидаль-тамашек]]: см. Тамашек
# [[Файл:Flag of Oklahoma.svg|22px|border]] [[w:Кикапу (язык)|Кикапу]]: '''ketapaanene''' («кетупуунэнэ»).<p>[[w:Конго (язык)|Киконго]]: см. Конго
# [[Файл:Kikuyu Flag (Mountain of Brightness).svg|22px|border]] [[w:Кикуйю (язык)|Кикуйю]]: '''nĩngwendete''' («нинкуэдете»).
# [[Файл:Flag of Washington.svg|22px|border]] [[w:Килеут (язык)|Килеут]]: '''nayeli''' («найели»).<p>[[w:Луба-катанга|Килуба]]: см. Луба-катанга<p>[[w:Кильдинский саамский язык|Кильдин-саамский]]: см. Кильдинский саамский
# [[Файл:Sami flag.svg|22px|border]] [[w:Кильдинский саамский язык|Кильдинский саамский]]: '''мунн то̄н шоа̄бша''' («мунн тоон шоа́бша»).<p>[[w:Северный мбунду|Кимбунду]]: см. Северный мбунду<p>[[w:Валлийский язык|Кимрский]]: см. Валлийский<p>[[w:Руанда (язык)|Киньяруанда]]: см. Руанда
# [[Файл:Semaia tes Kyprou.svg|22px|border]] [[w:Кипрский диалект греческого языка|Кипрский греческий]]: '''αγαπώ σε''' («агапо сэ»).
# [[Файл:Flag of Cyprus.svg|22px|border]] [[w:Кипрско-арабский язык|Кипрско-арабский]]: '''uḥibbuk''' («ухиббук»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:en:Kipsigis language|Кипсигис]]: '''achamin''' («ачамин»).
# [[Файл:Flag of Kyrgyzstan.svg|22px|border]] [[w:Киргизский язык|Киргизский]]: '''мен сени сүйөм''' («мен сэни сюйом»).
# [[Файл:Flag of Kiribati.svg|22px|border]] [[w:Кирибати (язык)|Кирибати]]: '''i tangiriko''' («и тангирико»).<p>[[w:Кирибати (язык)|Кирибатский]]: см. Кирибати<p>[[w:Рунди (язык)|Кирунди]]: см. Рунди<p>[[w:Гусии (язык)|Кисии]]: см. Гусии<p>[[w:Суахили|Кисуахили]]: см. Суахили<p>[[w:Сукума (язык)|Кисукума]]: см. Сукума
# [[Файл:Flag of China.svg|22px|border]] [[w:Китайский язык|Китайский]]: '''我愛你''' («во ай ни»).
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:Китуба|Китуба]]: '''mu zola ngé''' («му зола нге»).
# [[File:Flag of Viejšnoryja (Veyshnoria), fictional enemy republic for Zapad 2017 exercise.svg|22px|border]] Кихчексела: '''iðériscán''' («изэрискан»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Киче (язык)|Киче]]: '''katwaj''' («катуах»).
# [[Файл:Flag of Cusco (1978–2021).svg|22px|border]] [[w:Кичуа (язык)|Кичуа]]: '''juyanimi''' («хуяними»).
# [[Файл:Flag of Washington.svg|22px|border]] [[w:Клаллам (язык)|Клаллам]]: '''nəsƛ̕éʔ cxʷ''' («нэслэтчх»).<p>[[w:Кламат-модокский язык|Кламат]]: см. Кламат-модокский<p>[[w:Кламат-модокский язык|Кламат-модок]]: см. Кламат-модокский
# [[Файл:Flag of Oregon.svg|22px|border]] [[w:Кламат-модокский язык|Кламат-модокский]]: '''Moo ?ams ni stinta''' («моо амс ни стинта»).<p>[[w:Геэз|Классический эфиопский]]: см. Геэз
# [[Файл:Klingon Empire Flag.svg|22px|border]] [[w:Клингонский язык|Клингонский]]: '''qamuSHa'''' («камуша»), '''bahng-WI' shokh''' («банг-ви шох»).
# [[Файл:Brunssum vlag.svg|22px|border]] [[w:Диалекты нидерландского языка|Клингский нидерландский]]: '''ik zien ô gjèèren''' («ик зьен оо херээн»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Кого (язык)|Кого]]: '''ma din wa''' («ма дин ва»).
# [[Файл:TIPRA flag.jpg|22px|border]] [[w:Кокборок|Кокборок]]: '''ang nono hamjakgo''' («анг ноно хамджакго»).
# [[Файл:Flag of the City of London.svg|22px|border]] [[w:Кокни|Кокни]]: '''you are my turtle dove''' («ю а май тортл дав»).<p>[[w:Тлингитский язык|Колошский]]: см. Тлингитский
# [[Файл:Sami flag.svg|22px|border]] [[w:Колтта-саамский язык|Колтта-саамский]]: '''(mon) rääʹǩǩstam tuu''' («(мон) ра́кстам туу»).<p>[[w:Южноюкагирский язык|Колымский]]: см. Южноюкагирский<p>[[w:Команчский язык|Команче]]: см. Команчский
# [[Файл:Flag of Oklahoma.svg|22px|border]] [[w:Команчский язык|Команчский]]: '''ʉ kamakʉtʉ nʉ''' («э камакэт нэ»).<p>[[w:Коми язык|Коми]]: см. Коми-зырянский
# [[Файл:Komi Nordic cross flag.svg|22px|border]] [[w:Коми язык|Коми-зырянский]]: '''ме тэнö радейта''' («ме тэ́нэ ра́дейта»), '''ме тэнö мусала''' («ме тэ́нэ му́сала»).
# [[Файл:Flag of Izhemsky rayon (Komia).png|22px|border]] [[w:Ижемский диалект коми языка|Коми-ижемский]]: '''ме тэнэ радейта''' («ме тэ́нэ ра́дейта»), '''ме тэнэ любита''' («ме тэ́нэ лю́бита»).
# [[Файл:Flag of Permyakia.svg|22px|border]] [[w:Коми-пермяцкий язык|Коми-пермяцкий]]: '''ме тэнӧ любита''' («ме тэнэ любита»), '''ме тэнӧ радейта''' («ме тэнэ радейта»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Комокс|Комокс]]: '''χaƛnomɛč''' («халномэч»).
# [[Файл:Flag of Comoros.svg|22px|border]] [[w:Коморский язык|Коморский]]: '''n'game handzo''' («нгаме хандзо»).
# [[Файл:Flag of Musikongo.svg|22px|border]] [[w:Конго (язык)|Конго]]: '''mono ke zola nge''' («моно кэ зола нге»), '''na lingui yo''' («на лингуи йо»).
# [[Файл:Flag of Goa.svg|22px|border]] [[w:Конкани (язык)|Конкани]]: '''हांव तुजेर मोग करता''' («ту магель мога чо»).
# [[Файл:Coptic cross.svg|22px|border]] [[w:Коптский язык|Коптский]]: '''Ϯⲙⲉ ⲙ︦ⲙⲟ''' («тимэ ммо») — женщине, '''Ϯⲙⲉ ⲙ︦ⲙⲟⲕ''' («тимэ ммок») — мужчине.
# [[Файл:Flag of South Korea.svg|22px|border]] [[w:Корейский язык|Корейский]]: '''사랑해''' («саранхэ»).<p>[[w:Кипрско-арабский язык|Кормакити]]: см. Кипрско-арабский<p>[[w:Корнский язык|Корнваллийский]]: см. Корнский
# [[Файл:Flag of Cornwall.svg|22px|border]] [[w:Корнский язык|Корнский]]: '''my a’th kar''' («ми афкар»).<p>[[w:Корнский язык|Корнуэльский]]: см. Корнский
# [[Файл:Flag of Corsica.svg|22px|border]] [[w:Корсиканский язык|Корсиканский]]: '''ti tengu cara''' («ти тэнгу кара») — женщине; '''ti tengu caru''' («ти тэнгу кару») — мужчине.
# [[Файл:Flag of Koryakia.svg|22px|border]] [[w:Корякский язык|Корякский]]: '''гмнан гъчи гаймо ткулн'ги''' («гы́мнан гы́чи гэ́ймо тку́лнги»), '''гымнан гыччи ылӈу тыкулӈыги''' («гымнан гыччи ылну тыкулныги»).
# [[Файл:Flag of Transkei.svg|22px|border]] [[w:Коса (язык)|Коса]]: '''ndiyakuthanda''' («ндиякутанда»).<p>[[w:Авадхи|Косали]]: см. Авадхи<p>[[w:Гусии (язык)|Косова]]: см. Гусии
# [[Файл:Flag of Kosrae.svg|22px|border]] [[w:Косяэ|Косяэ]]: '''nga lungse kom''' («на лунсэ ком»).
# [[Файл:Flag of Gabon.svg|22px|border]] [[w:en:Kota language (Gabon)|Кота (Габон)]]: '''ma nono bè''' («ма ноно бе»).
# [[Файл:Flag of Kotava.svg|22px|border]] [[w:avk:Котава|Котава]]: '''va rin rená''' («ва рин рэна»).<p>[[w:Тем (язык)|Котоколи]]: см. Тем
# [[Файл:Flag of Siberia.svg|22px|border]] [[w:Коттский язык|Коттский]]: '''hamaʔ-u-th-āk-ŋ''' («хамаъ-у-с-аак-н»).
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Коюкон|Коюкон]]: '''nʉgh estsen'''' («нох эстсен»).
# [[Файл:Flag of Liberia.svg|22px|border]] [[w:Кпелле (язык)|Кпелле]]: '''iwalikana''' («иваликана»).
# [[Файл:Flag of the Northwest Territories.svg|22px|border]] [[w:Кри (языки)|Кри]]: '''ᑮᓯᑲᐦᐄᐦᐃᑏᐣ''' («кисаакихитин»).
# [[Файл:Flag of the Confederate States for the Muscogee (Creek) Nation.svg|22px|border]] [[w:Крикский язык|Крикский]]: '''ecenokecis''' («эценокецис»).
# [[Файл:Flag of Sierra Leone.svg|22px|border]] [[w:Крио|Крио]]: '''а lɛk yu''' («а лек ю»).<p>[[w:Малайско-португальский креольский язык|Кристанг]]: см. Малайско-португальский креольский
# [[Файл:Flag of Montana.svg|22px|border]] [[w:Кроу (язык)|Кроу]]: '''dii awátsišiky''' («дии ауатсишикь»).<p>[[w:Крымскотатарский язык|Крымский]]: см. Крымскотатарский
# [[Файл:Flag of the Crimean Tatar people.svg|22px|border]] [[w:Крымскотатарский язык|Крымскотатарский]]: '''men seni sevem''' («мэн сэни сэвэм»).<p>[[w:Крымскотатарский язык|Крымтатарский]]: см. Крымскотатарский
# [[Файл:Proposed flag of Krymchaks.svg|22px|border]] [[w:Крымчакский язык|Крымчакский]]: '''men seni sevem''' («мэн сэни сэвэм»).
# [[Файл:Flag of Dahadaevsky rayon (Dagestan).png|22px|border]] [[w:Кубачинский язык|Кубачинский]]: '''у дамми йикIулда''' («у дамми йик улда»).
# [[Файл:Flag of Jharkhand.svg|22px|border]] [[w:en:Kurmali language|Кудмали]]: '''môy tôke pôsôd kôrô''' («мой токе посод коро»).
# [[Файл:Flag of Palawan, Philippines.svg|22px|border]] [[w:Куйонон (язык)|Куйонон]]: '''inggegegma ta kaw''' («инггегегма та кау»), '''gegma ta kaw''' («гэгма та кау»), '''mal ta kaw''' («мал та кау»).<p>[[w:Куйонон (язык)|Куйонский]]: см. Куйонон
# [[Файл:Flag of the Cook Islands.svg|22px|border]] [[w:Кукский язык|Кукский]]: '''inangaro au ia koe''' («инангаро ау иа коэ»).<p>[[w:Кукский язык|Кукский маори]]: см. Кукский
# [[Файл:Flag of Côte d'Ivoire.svg|22px|border]] [[w:Куланго (язык)|Куланго]]: '''mi koriou''' («ми кориоу»).
# [[Файл:Flag of Altai Krai.svg|22px|border]] [[w:Кумандинское наречие|Кумандинский]]: '''меҥ сени сӱӱп jадым''' («мен сэни сююп ядым»).
# [[Файл:Flag of the Kumaon Kingdom.svg|22px|border]] [[w:Кумаони (язык)|Кумаони]]: '''mekai teri maya laagi chhoo''' («мэкай тэри майа лааги чхоо»).
# [[Файл:Flag of Kumyks.svg|22px|border]] [[w:Кумыкский язык|Кумыкский]]: '''мен сени сюемен''' («мэн си сюэмэн»).<p>[[w:Романшский язык|Курваль]]: см. Романшский
# [[Файл:Flag of Kurdistan.svg|22px|border]] [[w:Курдский язык|Курдский]]: '''ez hej te dikim''' («эз хэдж тэ диким»).
# [[Файл:Flag of Kurdistan.svg|22px|border]] [[w:Курманджи (диалект курдского языка)|Курманджи]]: '''ez jite hezdikim''' («эз житэ хэздиким»).
# [[Файл:Flag of Ghana.svg|22px|border]] [[w:Кусаал (язык)|Кусаал]]: '''m bood if''' («м бооди ф»).<p>[[w:Косяэ|Кусаеанский]]: см. Косяэ<p>[[w:Косяэ|Кусаие]]: см. Косяэ<p>[[w:Кусаал (язык)|Кусале]]: см. Кусаал<p>[[w:Кусаал (язык)|Кусаси]]: см. Кусаал
# [[Файл:Flag of Cusco (2021).svg|22px|border]] [[w:Кусканский кечуа|Кусканский кечуа]]: '''munakuyki''' («мунакуйки»).
# [[Файл:Flag of Sindh.svg|22px|border]] [[w:en:Kutchi language|Кутчи]]: '''આંઉ તોકે પ્રેમ કંઈયા તો''' («аау то-ке прем каийя то»).<p>[[w:Гвичин|Кучинский]]: см. Гвичин<p>[[w:Чхаттисгархи|Кхалтахи]]: см. Чхаттисгархи
# [[Файл:Bandera de Meghalaya.svg|22px|border]] [[w:Кхаси (язык)|Кхаси]]: '''nga ieid ia phi''' («нга иеид иа пхи»).
# [[Файл:Flag of Cambodia.svg|22px|border]] [[w:Кхмерский язык|Кхмерский]]: '''បងស្រឡាញ់អូន''' («бон сроланх оун») — женщине; '''អូនស្រឡាញ់បង''' («оун сроланх бон») — мужчине.<p>[[w:Коса (язык)|Кхоса]]: см. Коса<p>[[w:Косяэ|Косраэ]]: см. Косяэ<p>[[w:Киргизский язык|Кыргызский]]: см. Киргизский
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Кэрриер (язык)|Кэрриер]]: '''nyuk'enusi''' («ньюк энуси»).
# [[Файл:Flag of Kyakhta (Buryatia).png|22px|border]] [[w:Кяхтинский язык|Кяхтинский]]: '''моя твоя люби''' («моя твоя люби»).
# [[Файл:Conlangflag.svg|22px|border]] Лаала: '''mm do da''' («мм до да»).<p>[[w:Сефардский язык|Ладино]]: см. Сефардский
# [[Файл:Flag of Ladinia.svg|22px|border]] [[w:Ладинский язык|Ладинский]]: '''te ei gen''' («тэ эй гэн»).
# [[Файл:Fictitious flag of Lazistan.png|22px|border]] [[w:Лазский язык|Лазский]]: '''მასიმაოროპენ''' («масимаоропэн»).<p>[[w:Чинский язык|Лай]]: см. Чинский
# [[Файл:Pine Ridge Flag.svg|22px|border]] [[w:Лакота (язык)|Лакота]]: '''thečhíȟila''' («тхэчихила»).
# [[Файл:نماد لک های داغستان.jpg|22px|border]] [[w:Лакский язык|Лакский]]: '''ттунина ххирара''' («тунина хирара»).<p>[[w:Эвенский язык|Ламутский]]: см. Эвенский
# [[Файл:Flag of Piedmont.svg|22px|border]] [[w:Пьемонтский язык|Лангароло]]: '''ieu t’ame''' («ю тэм»).<p>[[w:Хо (язык)|Ланка-кол]]: см. Хо
# [[Файл:Flag of Laos.svg|22px|border]] [[w:Лаосский язык|Лаосский]]: '''ຂ້ອຍຮັກເຈົ້າ''' («кхёой хак чао»).<p>[[w:Субанон (язык)|Лапуян]]: см. Южный субанон
# [[Файл:Flag of Musikongo.svg|22px|border]] [[w:Конго (язык)|Лари]]: '''nge nzololo''' («нге нзололо»), '''nikuzololo''' («никузололо»).<p>[[w:Чхаттисгархи|Лариа]]: см. Чхаттисгархи
# [[Файл:Official flag of Latgale.svg|22px|border]] [[w:Латгальский язык|Латгальский]]: '''es tevi miloju''' («ас теви меелою»).
# [[Файл:Flag of the Roman Empire.svg|22px|border]] [[w:Латинский язык|Латинский]]: '''te amo''' («тэ амо»).<p>[[w:Латинский язык|Латынь]]: см. Латинский
# [[Файл:Flag of Latvia.svg|22px|border]] [[w:Латышский язык|Латышский]]: '''es tevi mīlu''' («эс тэви миилу»).<p>[[w:Урду|Лашкари]]: см. Урду<p>[[w:Лушуцид|Лашутсид]]: см. Лушуцид<p>[[w:Лингва де планета|ЛдП]]: см. Лингва де планета
# [[Файл:Lezgian flag.svg|22px|border]] [[w:Лезгинский язык|Лезгинский]]: '''заз вун кӏанзава''' («заз вун къанзава»).
# [[Файл:Flag of Taymyr Autonomous Okrug.svg|22px|border]] [[w:Лесной энецкий язык|Лесной энецкий]]: '''мо̂дь ууʼʼ комитазʼʼ''' («моодь уу́ комита́з»).<p>[[w:Древнееврейский язык|Лешон ха-кодеш]]: см. Древнееврейский
# [[Файл:Flag of Lebanon.svg|22px|border]] [[w:Ливанский диалект арабского языка|Ливанский арабский]]: '''بهباك''' («бахибак»).
# [[Файл:Flag of the Livonians.svg|22px|border]] [[w:Ливский язык|Ливский]]: '''minā ārmaztõb sinā''' («минаа аармазтыб синаа»).
# [[Файл:Flag of Liguria.svg|22px|border]] [[w:Лигурский язык (современный)|Лигурский]]: '''mi te amo''' («ми тэ амо»), '''te véuggio bén''' («тэ веуджо бэн»).<p>[[w:Лингва де планета|Лидепла]]: см. Лингва де планета
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Лиллуэт|Лиллуэт]]: '''stexwkan tu7 xatmintsin''' («стэхукан ту хатминтсин»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Limbum language|Лимбум]]: '''meh kong weh''' («мэ кон вэ»).
# [[Файл:Flag of Limburg (Netherlands).svg|22px|border]] [[w:Лимбургский язык|Лимбургский]]: '''ik hald van dich''' («ик халд фан дихь»).<p>[[w:Лимбургский язык|Лимбуржский]]: см. Лимбургский
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:Лингала|Лингала]]: '''nalingí yɔ̌''' («налинги йё»).<p>[[w:Тупи (язык)|Лингва бразилика]]: см. Тупи
# [[Файл:Lidepla.jpg|22px|border]] [[w:Лингва де планета|Лингва де планета]]: '''me lubi yu''' («ме луби ю»).<p>[[w:Тупи (язык)|Лингва жерал]]: см. Тупи
# [[Файл:Flag of Lingua Franca Nova.svg|22px|border]] [[w:Лингва франка нова|Лингва франка нова]]: '''me ama tu''' («мэ ама ту»).
# [[Файл:Flag of Lisbon.svg|22px|border]] [[w:Португальский язык|Лиссабонский португальский]]: '''gramo-te''' («грамотэ»).<p>[[w:Арабский литературный язык|Литературный арабский]]: см. Арабский литературный
# [[Файл:Flag of Lithuania.svg|22px|border]] [[w:Литовский язык|Литовский]]: '''aš tave myliu''' («аш тавя милю»).
# [[Файл:Conlangflag.svg|22px|border]] [[w:Логлан|Логлан]]: '''mi cluva tu''' («ми шлува ту»).
# [[Файл:Flag of Sardinia.svg|22px|border]] [[w:Логудорский диалект|Логудорский сардинский]]: '''deo t’amo''' («дэо тьямо»).
# [[Файл:Lojban logo.svg|22px|border]] [[w:Ложбан|Ложбан]]: '''mi do prami''' («ми до прами»).
# [[Файл:Flag of Barotseland.svg|22px|border]] [[w:Лози (язык)|Лози]]: '''na ku lata''' («на ку лата»).
# [[Файл:Flag of Lombardy.svg|22px|border]] [[w:Ломбардский язык|Ломбардский]]: '''te vòj ben''' («те вож бэн»), '''te veuli ben''' («тэ вэули бэн»).
# [[Файл:Unofficial flag of Nagaland.svg|22px|border]] [[w:en:Lotha language|Лотха]]: '''ana ni nzana la''' («ана ни нзана ла»).
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:Луба (язык)|Луба]]: '''ndi musua wewe''' («нди мусуа вэвэ»).<p>[[w:Луба (язык)|Луба-касаи]]: см. Луба
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:Луба-катанга|Луба-катанга]]: '''ami nkuswele''' («ами нкусвеле»).<p>[[w:Луба (язык)|Луба-лулуа]]: см. Луба<p>[[w:Луба-катанга|Луба-шаба]]: см. Луба-катанга
# [[Файл:Flag of Buganda.svg|22px|border]] [[w:Луганда|Луганда]]: '''nkwagala''' («нкуагала»).<p>[[w:Луговомарийский язык|Лугововосточный марийский]]: см. Луговомарийский
# [[Файл:Mari Ushem flag.svg|22px|border]] [[w:Луговомарийский язык|Луговомарийский]]: '''мый тыйым йӧратем''' («мый тыйим ёратэм»).
# [[Файл:Louisiana Creole Flag.svg|22px|border]] [[w:Луизианский креольский язык|Луизианский креольский]]: '''mo laime toi''' («мо лэм туа»).<p>[[w:Каджунский диалект французского языка|Луизианский французский]]: см. Каджунский французский<p>[[w:Языки лухья|Луйя]]: см. Лухья
# [[Файл:Sami flag.svg|22px|border]] [[w:Луле-саамский язык|Луле-саамский]]: '''mån æhtsáv duv''' («мон эхтсав дув»).<p>[[w:Долуо|Луо]]: см. Долуо<p>[[w:Чукотский язык|Луораветланский]]: см. Чукотский<p>[[w:Кламат-модокский язык|Лутуамийский]]: см. Кламат-модокский<p>[[w:Языки лухья|Лухиа]]: см. Лухья
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Языки лухья|Лухья]]: '''ndakhuyanza''' («ндакхуянза»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Языки лухья|Лухья (ванга)]]: '''ndakhuchama''' («ндахучама»), '''ndakhuyanza''' («ндахуянза»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Языки лухья|Лухья (мараголи)]]: '''nakuyanza''' («накуянза»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Языки лухья|Лухья (марачи)]]: '''ndakhujama''' («ндахуджяма»), '''ndakhukhera''' («ндахухера»).
# [[Файл:Flag of Washington.svg|22px|border]] [[w:Лушуцид|Лушуцид]]: '''ʔəsx̌aƛ̕tubicid čəd''' («эсшалтубицид чэд») - неромантически, '''cay čəxʷ dsx̌aƛ’''' («кай чэхв дсхау») - романтически.<p>[[w:Лингва франка нова|ЛФН]]: см. Лингва франка нова<p>[[w:Янито|Льянито]]: см. Янито
# [[Файл:Flag of Luxembourg.svg|22px|border]] [[w:Люксембургский язык|Люксембургский]]: '''ech hunn dech gär''' («эш ун дэш геар»).
# [[Файл:Flag of Canton of Lucerne.svg|22px|border]] [[w:Люцернский диалект|Люцернский немецкий]]: '''ech liebe dech''' («эх либэ дэх»).
# [[Файл:Flag of Mauritius.svg|22px|border]] [[w:Маврикийский креольский язык|Маврикийский креольский]]: '''mo kontan twa''' («мо контан туа»).
# [[Файл:Flag of Jharkhand.svg|22px|border]] [[w:Магахи|Магахи]]: '''हम तोरा से प्यार करऽऽ हियो।''' («хэм тоораа сээ пьяар кэрэ хийоо»).
# [[Файл:Flag of Maghreb.svg|22px|border]] [[w:Магрибский арабский язык|Магрибский арабский]]: '''تنبغيك''' («танбгхик»).
# [[Файл:Bandera de la ciudad de Madrid.svg|22px|border]] [[w:Мадридский диалект|Мадридский испанский]]: '''me molas''' («мэ молас»).
# [[Файл:Flag of Various Autonomous Indonesian States.svg|22px|border]] [[w:Мадурский язык|Мадурский]]: '''kula tresna panjengan''' («кула трэсна пандженган»).<p>[[w:Венгерский язык|Мадьярский]]: см. Венгерский
# [[Файл:Flag of Tapuria Mazandaran.jpg|22px|border]] [[w:Мазандеранский язык|Мазандеранский]]: '''tere del devesteme''' («тэрэ дэл дэвэстэмэ»).
# [[Файл:Flag of Iowa.svg|22px|border]] [[w:Майами-иллинойс|Майами-иллинойс]]: '''teepaalilaani''' («тап а ли ланг»).
# [[Файл:Flag of Jharkhand.svg|22px|border]] [[w:Майтхили|Майтхили]]: '''हम अहां सँ प्रेम करैत छी''' («хаум ахаам сэ прэм карэчи»).<p>[[w:Юкатекский язык|Майя]]: см. Юкатекский
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Makaa language|Макаа]]: '''me tchiel wo''' («мэ тчиэл уо»).
# [[File:Flag of South Sulawesi.svg|22px|border]] [[w:Макасарский язык|Макасарский]]: '''ᨀᨘᨂᨁᨕᨗᨀᨚ''' («кунгаико»).
# [[File:Flag of Macedonia (1992–1995).svg|22px|border]] [[w:Македонский язык|Македонский]]: '''те сакам''' («тэ сакам»).<p>[[w:Арумынский язык|Македо-румынский]]: см. Арумынский
# [[Файл:Flag of Mozambique.svg|22px|border]] [[w:Макуа (язык)|Макуа]]: '''mim kinophenta''' («мим кинофента»), '''kinotunani''' («кинотунани»).<p>[[w:Макуа (язык)|Макхува]]: см. Макуа
# [[Файл:Flag of Madagascar.svg|22px|border]] [[w:Малагасийский язык|Малагасийский]]: '''tiako ianao''' («тьяко йанао»).<p>[[w:Малайский язык|Малайзийский]]: см. Малайский
# [[Файл:Flag of Malaysia.svg|22px|border]] [[w:Малайский язык|Малайский]]: '''saya sayang awak''' («сайя сайянг авак»).
# [[Файл:Flag of Malacca.svg|22px|border]] [[w:Малайско-португальский креольский язык|Малайско-португальский креольский]]: '''em t' amo’b''' («эм тьямуб»).
# [[Файл:Malayali flag.svg|22px|border]] [[w:Малаялам|Малаялам]]: '''ഞാന് നിന്നെ പ്രേമിക്കുന്നു''' («ньяан ниннэ премиккунну»).<p>[[w:Малаялам|Малаяльский]]: см. Малаялам
# [[Файл:Flag of New Brunswick.svg|22px|border]] [[w:Малесит-пассамакводди|Малесит-пассамакводди]]: '''koselomol''' («кэсэлэмэл»).<p>[[w:Малесит-пассамакводди|Малисит-пассамакводди]]: см. Малесит-пассамакводди<p>[[w:Антильский франко-креольский язык|Малоантильский креольский]]: см. Антильский франко-креольский<p>[[w:Малагасийский язык|Мальгашский]]: см. Малагасийский
# [[Файл:Flag of Maldives.svg|22px|border]] [[w:Мальдивский язык|Мальдивский]]: '''އަހަރެން ތިބާ ދެކެ ލޯބިވަން''' («ахарэн тхибаа дхэкэ лоабиван»).
# [[Файл:Flag of Mallorca.svg|22px|border]] [[w:Балеарский диалект каталанского языка|Мальоркин]]: '''t`estim''' («тэстим»).
# [[Файл:Flag of Malta.svg|22px|border]] [[w:Мальтийский язык|Мальтийский]]: '''inħobbok''' («инхоббок»).
# [[Файл:Bandeira de Mato Grosso.svg|22px|border]] [[w:Мамаинде|Мамаинде]]: '''a-nu-dexexn''' («а ну дэшэшн»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Мамский язык|Мамский]]: '''at nk’ujleb’il''' («ат нкухлеб иль»).
# [[Файл:Flag of North Sulawesi.svg|22px|border]] [[w:en:Manado Malay|Манадо-малайский]]: '''kita suka pa ngana''' («кита сука па нгана»).
# [[Файл:Flag of North Sumatra.svg|22px|border]] [[w:en:Mandailing language|Мандаилинг]]: '''holong do rohangku tu ho''' («холонг до рохангку ту хо»).<p>[[w:Севернокитайский язык|Мандарин]]: см. Севернокитайский<p>[[w:Севернокитайский язык|Мандаринский]]: см. Севернокитайский<p>[[w:Севернокитайский язык|Мандаринский китайский]]: см. Севернокитайский
# [[Файл:Flag of Guinea-Bissau.svg|22px|border]] [[w:en:Manjak language|Манджак]]: '''ma ngal o''' («ма нгал о»).<p>[[w:Маньчжурский язык|Манджурский]]: см. Маньчжурский<p>[[w:Мандинка (язык)|Мандинго]]: см. Мандинка
# [[Файл:Flag of the Wassoulou Empire.svg|22px|border]] [[w:Мандинка (язык)|Мандинка]]: '''nye kanu laye''' («нье кану лайе»).
# [[Файл:Flag of Guinea.svg|22px|border]] [[w:Манинка|Манинка]]: '''ni bi fe''' («ни би фэ»).
# [[Файл:Flag of Guinea.svg|22px|border]] [[w:Манинка|Манинка (коньянка)]]: '''i diany gnè''' («и дьяни не»).
# [[Файл:Flag of Manipur (stripes variant).svg|22px|border]] [[w:Манипури (язык)|Манипури]]: '''ꯑꯩꯅ ꯅꯪꯕꯨ ꯅꯨꯡꯁꯤ''' («эи нанг-бу нунгши»).
# [[Файл:Flag of Yugra.svg|22px|border]] [[w:Мансийский язык|Мансийский]]: '''ам эруптэгум наӈын''' («ам эруптэгум нанын»).
# [[Файл:Flag of Manchukuo.svg|22px|border]] [[w:Маньчжурский язык|Маньчжурский]]: '''bi shimbe hairambi''' («би шимбе хайрамби»).
# [[Файл:Flag of Mayotte (local).svg|22px|border]] [[w:en:Maore dialect|Маоре]]: '''n'game handzo''' («н'гамэ хандзо»).
# [[Файл:Tino Rangatiratanga Maori sovereignty movement flag.svg|22px|border]] [[w:Маори (язык)|Маори]]: '''kei te aroha au ki a koe''' («кей тэ ароха ау ки а коэ»).<p>[[w:Маори (язык)|Маорийский]]: см. Маори<p>[[w:Кукский язык|Маори островов Кука]]: см. Кукский<p>[[w:Мапуче (язык)|Мапудунгун]]: см. Мапуче
# [[Файл:Flag of the Mapuches (1992).svg|22px|border]] [[w:Мапуче (язык)|Мапуче]]: '''inche poyekeyu''' («инче пойекейю»).
# [[Файл:PH-LAS Flag.png|22px|border]] [[w:Маранао (язык)|Маранао]]: '''pekababaya-an ko seka''' («пекабабайя ан ко сека»).
# [[Файл:Marathi Flag.svg|22px|border]] [[w:Маратхи (язык)|Маратхи]]: '''माझ तुइयावर प्रेम आहे''' («мааджха туийяавар прем аахе»).<p>[[w:Марвари|Марвади]]: см. Марвари
# [[Файл:Flag of Jodhpur.svg|22px|border]] [[w:Марвари|Марвари]]: '''main tanne pyaar karoon''' («маин танне пьяар кароон»).
# [[Файл:Flag of Marquesas Islands.svg|22px|border]] [[w:Маркизский язык|Маркизский]]: '''ua hinenao au ia oe''' («уа хинэнао ау иа оэ»).
# [[Файл:Flag of Morocco.svg|22px|border]] [[w:Марокканский диалект арабского языка|Марокканский арабский]]: '''كنبغيك''' («канэбгик») - женщине, '''كنموتعليك''' («канмутлик») - мужчине.
# [[Файл:Flag Ceuta.svg|22px|border]] [[w:Марокканский диалект арабского языка|Марокканский арабский Сеуты]]: '''تنبغيك''' («танбгик»).<p>[[w:Кипрско-арабский язык|Маронитский кипрско-арабский]]: см. Кипрско-арабский
# [[Файл:Flag-of-Martinique.svg|22px|border]] [[w:Антильский франко-креольский язык|Мартиникский франко-креольский]]: '''mwen enmen'w''' («муэн эмэнв»).
# [[Файл:Flag of the Marshall Islands.svg|22px|border]] [[w:Маршалльский язык|Маршалльский]]: '''ij io̧kwe eok''' («ий иаквэ эок»).<p>[[w:Языки лухья|Масаба-луйя]]: см. Лухья
# [[Файл:Bandera masai.svg|22px|border]] [[w:Масайский язык|Масайский]]: '''kanyor nanu''' («каньор нану»).<p>[[w:Крикский язык|Маскоги]]: см. Крикский<p>[[w:Египетский диалект арабского языка|Масри]]: см. Египетский арабский<p>[[w:Массачусетский язык|Массачусет]]: см. Массачусетский<p>[[w:Массачусетский язык|Массачусетт]]: см. Массачусетский
# [[Файл:Flag of Massachusetts.svg|22px|border]] [[w:Массачусетский язык|Массачусетский]]: '''kuwômônush''' («кэуаамаанэш»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Mafa language|Мафа]]: '''i way ka''' («и вай ка»).
# [[Файл:Flag of Vermont.svg|22px|border]] [[w:Махикан|Махикан]]: '''ktaʔwãanin''' («кта ваанин»).<p>[[w:Итонама (язык)|Мачото]]: см. Итонама
# [[Файл:Bandeira de Minas Gerais.svg|22px|border]] [[w:Машакали|Машакали]]: '''xate' ug-putuppax''' («шатэ уг путуппаш»).
# [[Файл:Flag of the Cooperation Council for the Arab States of the Gulf.svg|22px|border]] [[w:en:Mashriqi Arabic|Машрикский арабский]]: '''bahebbik''' («бахеббик») - женщине,'''bahebbak''' («бахеббак») - мужчине.
# [[Файл:Flag of the Republic of the Congo.svg|22px|border]] [[w:en:Mbama language|Мбама]]: '''bènan ndjala wè''' («бэнан нджала уэ»).
# [[Файл:Flag of the Republic of the Congo.svg|22px|border]] [[w:en:Mbere language|Мбере]]: '''me na dialayè''' («мэ на диалайе»), '''me ngoua dialayè''' («мэ нгуа диалайе»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:fr:Mbo (langue du Cameroun)|Мбо]]: '''mi ding wo''' («ми динг уо»).
# [[Файл:Flag of The Principality of Mingrelia (Portolan 1560).svg|22px|border]] [[w:Мегрельский язык|Мегрельский]]: '''მა სი მიორქ''' («ма си миорк»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Medumba language|Медумба]]: '''me ko ou''' («мэ ко у»).<p>[[w:Манипури (язык)|Мейтейлион]]: см. Манипури<p>[[w:Манипури (язык)|Мейтхей]]: см. Манипури<p>[[w:Межславянский язык|Междуславянский]]: см. Межславянский
# [[Файл:Flag of Interslavic.svg|22px|border]] [[w:Межславянский язык|Межславянский]]: '''ја љубју тебе/ja ljubju tebe''' («я любю тэбэ»), '''ја те љубју/ja tę ljubjų''' («я тэ любю»).
# [[Файл:Flag of Sierra Leone.svg|22px|border]] [[w:Менде (язык)|Менде]]: '''cale sa duie ca upeif''' («кале са дуйе ка упеиф»).
# [[Файл:Flag of Wisconsin.svg|22px|border]] [[w:Меномини (язык)|Меномини]]: '''ketapanen''' («кетапанен»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Меру (язык)|Меру]]: '''ikwendete''' («иквэндэтэ»).
# [[Файл:Bandera de Mizoram.svg|22px|border]] [[w:Мизо (язык)|Мизо]]: '''ka hmangaih che''' («ка нмангаих че»).
# [[Файл:Flag of the Miccosukee Tribe of Indians of Florida.svg|22px|border]] [[w:Микасуки (язык)|Микасуки]]: '''cheh moka is cheh''' («чех мока ис чех»).
# [[Файл:Mikmaq State Flag.svg|22px|border]] [[w:Микмак (язык)|Микмак]]: '''kesalul''' («кэсалул»).<p>[[w:Михе (язык)|Миксе]]: см. Михе
# [[Файл:Flag of the Province of Milan.svg|22px|border]] [[w:Миланский диалект западноломбардского языка|Миланский ломбардский]]: '''te vöeri ben''' («тэ вээри бэн»).<p>[[w:Ген (язык)|Мина]]: см. Ген
# [[Файл:Flag of Minang.svg|22px|border]] [[w:Минангкабау (язык)|Минангкабау]]: '''ambo cinto ka awak''' («амбо синто ка авак»).
# [[Файл:Flag of the Iroquois Confederacy.svg|22px|border]] [[w:Минго (язык)|Минго]]: '''könuöhkwa''' («гоннуохгуа»).<p>[[w:Мегрельский язык|Мингрельский]]: см. Мегрельский
# [[Файл:Conlangflag.svg|22px|border]] Мини: '''mi i amo a tu''' («ми и амо а ту»).<p>[[w:Мирандский язык|Мирандес]]: см. Мирандский
# [[Файл:Mirandese flag.svg|22px|border]] [[w:Мирандский язык|Мирандский]]: '''amo-te''' («амотэ»).
# [[Файл:Banderaayuukmixemf.svg|22px|border]] [[w:Михе (язык)|Михе]]: '''ntsëj kypts mejts''' («нцэх кипц мэхц»).
# [[Файл:Metis Blue.svg|22px|border]] [[w:Мичиф|Мичиф]]: '''keesha kee taen''' («киша ки тэн»).<p>[[w:Михе (язык)|Мише]]: см. Михе
# [[Файл:Bandera Mixteca.png|22px|border]] [[w:Миштекские языки|Миштекский]]: '''ku toulló ñeloosí''' («ку тоулло ньелооси»).<p>[[w:Могаукский язык|Могаук]]: см. Могаукский
# [[Файл:Flag of Ontario.svg|22px|border]] [[w:Могаукский язык|Могаукский]]: '''konnorónhkhwa''' («конноронкуа»).<p>[[w:Махикан|Могиканский]]: см. Махикан
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:fr:Moye (langue)|Мои]]: '''gakakayo''' («гакакайо»).
# [[Файл:Flag of Mwoakilloa.svg|22px|border]] [[w:Мокильский язык|Мокильский]]: '''ngoah mweoku kaua''' («нгоа муэоку кауа»).<p>[[w:Мокшанский язык|Мокша-мордовский]]: см. Мокшанский
# [[Файл:Flag of the Moksha people.svg|22px|border]] [[w:Мокшанский язык|Мокшанский]]: '''мон кельктядязь тинь''' («мон кельктядязь тинь»).
# [[Файл:Flag of Moldova.svg|22px|border]] [[w:Молдавский язык|Молдавский]]: '''te iubesc''' («тэ юбеск»).
# [[Файл:Flag of Mongolia.svg|22px|border]] [[w:Монгольский язык|Монгольский]]: '''би чамд хайртай''' («би чамд хайртай»).
# [[Файл:Flag of Monaco.svg|22px|border]] [[w:Монегаскский диалект|Монегаскский]]: '''te véuggio bén''' («тэ вэуджо бэн»).<p>[[w:Монегаскский диалект|Монегасский]]: см. Монегаскский<p>[[w:Ангами (язык)|Монр]]: см. Ангами
# [[Файл:Flag of Labrador.svg|22px|border]] [[w:Монтанье-наскапи (язык)|Монтанье-наскапи]]: '''tshemenuadeden''' («тшэменуадэдэн»).
# [[Файл:Flag of Burkina Faso.svg|22px|border]] [[w:Мооре|Мооре]]: '''mam nonga foo''' («мам нонга фоо»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Мопан (язык)|Мопан]]: '''ink’ateech''' («инкатэч»).<p>[[w:Мооре|Море]]: см. Мооре<p>[[w:Мооре|Мосси]]: см. Мооре<p>[[w:Могаукский язык|Мохавк]]: см. Могаукский<p>[[w:Могаукский язык|Мохаук]]: см. Могаукский
# [[Файл:Ensign of New England (pine only).svg|22px|border]] [[w:Мохеган-пекот|Мохеган-пекот]]: '''kuwômôyush''' («кэуаамаайэш»).<p>[[w:Могаукский язык|Мохок]]: см. Могаукский<p>[[w:Мооре|Моши]]: см. Мооре
# [[Файл:Bandera Región Loreto.svg|22px|border]] [[w:Муниче|Муниче]]: '''pjeñcamʷpʷ''' («пьенькамвпв»).
# [[Файл:Flag of Gabon.svg|22px|border]] [[w:en:Myene language|Мьене]]: '''mi tonda wè''' («ми тонда ве»).<p>[[w:Бирманский язык|Мьянма]]: см. Бирманский<p>[[w:Бирманский язык|Мьянманский]]: см. Бирманский
# [[Файл:Flag of the Isle of Mann.svg|22px|border]] [[w:Мэнский язык|Мэнский]]: '''ta graih aym ort''' («та гра эморт»).<p>[[w:Мэнский|Мэнский гэльский]]: см. Мэнский<p>[[w:Хмонг (язык)|Мяо]]: см. Хмонг<p>[[w:Арагонский язык|Наваррско-арагонский]]: см. Арагонский
# [[Файл:Navajo flag.svg|22px|border]] [[w:Навахо (язык)|Навахо]]: '''ayóóʼánííníshní''' («айёо аниинишни»).
# [[Файл:Conlangflag.svg|22px|border]] [[w:На'ви (язык)|На'ви]]: '''ngari ’efu oe tunu''' («нгари эфу оэ туну») - романтически, '''nga yawne lu oer''' («нга ёнэ лу оэр»), '''nga yawne leiu oer''' («нга ёнэ лэю оэр») - неромантически.
# [[Файл:Unofficial flag of Nagaland.svg|22px|border]] [[w:en:Nagamese creole|Нагамский креольский]]: '''môi tôi ke môrôm kôre''' («мои тои ке мором корэ»).
# [[Файл:Flag of Jharkhand.svg|22px|border]] [[w:en:Nagpuri language|Нагпури]]: '''moe toke cāhonā''' («моэ токе кахона»).<p>[[w:Нигерийский креольский язык|Найджа]]: см. Нигерийский креольский
# [[Файл:Flag of Namaland.svg|22px|border]] [[w:Нама|Нама]]: '''INam si ta ge a''' («нам си та ге а») — женщине; '''INam tsi ta ge a''' («нам тси та ге а») — мужчине.
# [[Файл:Flag of Nanaysky rayon (Khabarovsk kray).png|22px|border]] [[w:Нанайский язык|Нанайский]]: '''ми симбивэ улэсимби''' («ми симбивэ улэсимби»).<p>[[w:Астекские языки|Науа]]: см. Науатль<p>[[w:Пипиль (язык)|Науат]]: см. Пипиль
# [[Файл:Flag of Nahuas.svg|22px|border]] [[w:Астекские языки|Науатль]]: '''ni mits neki''' («ни митс неки»).<p>[[w:Астекские языки|Науаский]]: см. Науатль
# [[Файл:Flag of Nauru.svg|22px|border]] [[w:Науруанский язык|Науруанский]]: '''nga ebonu''' («нга эбону»).<p>[[w:Гунзибский язык|Нахадинский]]: см. Гунзибский<p>[[w:Лингала|Нгала]]: см. Лингала<p>[[w:Ангами (язык)|Нгами]]: см. Ангами
# [[Файл:Flag of Taymyr Autonomous Okrug.svg|22px|border]] [[w:Нганасанский язык|Нганасанский]]: '''мәнә тәнә мәнюнтүм''' («мэнэ тэнэ мэнюнтум»).
# [[Файл:Flag of Laos.svg|22px|border]] [[w:en:Ta'Oi language|Нгек]]: '''kaw ʔɛɛʔ maj''' («ко ээ май»).
# [[Файл:Flag of Rhodesia (1968-1979).svg|22px|border]] [[w:en:Ndau language|Ндау]]: '''ndinokudisisa''' («ндинокудисиса»).<p>[[w:Ндюка|Нджука]]: см. Ндюка
# [[Файл:Flag of Angola.svg|22px|border]] [[w:en:Ndombe language|Ндомбе]]: '''ndilakuyanda''' («ндилакуянда»).
# [[Файл:Flag of Ovamboland.svg|22px|border]] [[w:Ндонга (язык)|Ндонга]]: '''ondi ku hole''' («онди ку холе»).<p>[[w:Ндонга (язык)|Ндунга]]: см. Ндонга<p>[[w:Ндюка|Ндьюка]]: см. Ндюка
# [[Файл:Flag of Suriname.svg|22px|border]] [[w:Ндюка|Ндюка]]: '''mi lobi yu''' («ми лоби ю»).
# [[Файл:Flag of Naples.svg|22px|border]] [[w:Неаполитанский язык|Неаполитанский]]: '''ti amo''' («ти амо»).<p>[[w:Неварский язык|Невари]]: см. Неварский
# [[Файл:Newarashtriyamuktimorchayabanner.JPG|22px|border]] [[w:Неварский язык|Неварский]]: '''जितः छ नापं मतिना दु।''' («джит ча панам матина ду»).<p>[[w:Не-персе (язык)|Нез персэ]]: см. Не-персе
# [[Файл:Flag of Germany.svg|22px|border]] [[w:Немецкий язык|Немецкий]]: '''ich liebe dich''' («ихь либэ дихь»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Nen language (Cameroon)|Нен]]: '''mi nou ong'o hiki''' («ми ноу онго хики»).
# [[Файл:Proposed Flag of Unified Nenetsia.svg|22px|border]] [[w:Ненецкий язык|Ненецкий]]: '''мань хамзангав сит''' («мань хамзангав сит»).<p>[[w:Пиджин Соломоновых Островов|Нео-соломоник]]: см. Пиджин Соломоновых Островов<p>[[w:Неварский язык|Непал-бхаса]]: см. Неварский<p>[[w:Непальский язык|Непали]]: см. Непальский
# [[Файл:Flag of Nepal (white background, aspect ratio 3-2).svg|22px|border]] [[w:Непальский язык|Непальский]]: '''म तपाइलाइ माया गर्छु।''' («ма тапайнлай майя гарчу»).
# [[Файл:Flag of Idaho.svg|22px|border]] [[w:Не-персе (язык)|Не-персе]]: '''in ‘ee hetewise''' («ин ээ хэтэвизэ»).
# [[Файл:Flag of Nivkh people.svg|22px|border]] [[w:Нивхский язык|Нивхский]]: '''ни чи эзмудь''' («ни чи эзмудь»).
# [[Файл:Flag of Nigeria.svg|22px|border]] [[w:Нигерийский креольский язык|Нигерийский креольский]]: '''i love you''' («ай лав ю»).<p>[[w:Нигерийский креольский язык|Нигерийский пиджин]]: см. Нигерийский креольский
# [[Файл:Flag of Nigeria.svg|22px|border]] [[w:en:Nigerian Fulfulde|Нигерийский фульфульде]]: '''mi yidima''' («ми йидима»).
# [[Файл:Flag of the Netherlands.svg|22px|border]] [[w:Нидерландский язык|Нидерландский]]: '''ik houd van je''' («ик хау фан е»).
# [[Файл:Vöärstelvlagge neadersassisk.svg|22px|border]] [[w:Нижнесаксонские диалекты Нидерландов|Нидерландский нижнесаксонский]]: '''ik hol van die''' («ик хол фан ди»).
# [[Файл:Lower Sorbian Flag.gif|22px|border]] [[w:Нижнелужицкий язык|Нижнелужицкий]]: '''ja śi lubujom''' («я ши лубуйом»).
# [[Файл:Flag igora.svg|22px|border]] [[w:Ижорский язык|Нижнелужский ижорский]]: '''miä suvvan sinno''' («мия сувван синно»).
# [[Файл:Noordlandflagg.svg|22px|border]] [[w:Нижненемецкий язык|Нижненемецкий]]: '''ik hou van di''' («ик хау фан ди»), '''ik heff di leev''' («ик хэфф ди леев»).
# [[Файл:Flag of Lower Saxony.svg|22px|border]] [[w:Нижнесаксонские диалекты|Нижнесаксонский]]: '''ik hou van ju''' («ик хау фан ю»).<p>[[w:Адыгейский язык|Нижнечеркесский]]: см. Адыгейский
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Нижний танана|Нижний танана]]: '''neghw estsen'''' («нёху эстсен»).
# [[Файл:Hassanamisco Nipmuc Flag.png|22px|border]] [[w:en:Loup language|Нипмук]]: '''keȣamanlis''' («кэуамалэс»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Нисгаа|Нисгаа]]: '''siip'iniy' n'iin''' («сиип иний ниин»).
# [[Файл:Flag of Niue.svg|22px|border]] [[w:Ниуэ (язык)|Ниуэ]]: '''ke tohi nei au ki a koe''' («кэ тохи нэи ау ки а коэ»).
# [[Файл:Flag of the County of Nice.svg|22px|border]] [[w:en:Niçard dialect|Ниццкий]]: '''ti vouòli ben''' («ти вуоли бэн»).
# [[Файл:Flag of Guinea.svg|22px|border]] [[w:en:N'Ko language|Нко]]: '''ߌ ߞߏ߫ ߞߊߘߌ߫ ߒ ߦߋ߫''' («и ко кади нг е»).<p>[[w:Ньянколе|Нколе]]: см. Ньянколе
# [[Файл:Flag of Bangladesh.svg|22px|border]] [[w:en:Noakhailla language|Ноакхаилла]]: '''আই তরে ভালাবাষি''' («аай торе балабаши»).<p>[[w:Новиаль|Новиал]]: см. Новиаль
# [[Файл:Flag of Novial.svg|22px|border]] [[w:Новиаль|Новиаль]]: '''me ama vu''' («ме ама ву»).<p>[[w:Новонорвежский язык|Новонорвежский]]: см. Норвежский (нюнорск)<p>[[w:Персидский язык|Новоперсидский]]: см. Персидский
# [[Файл:Nogai Flag.jpg|22px|border]] [[w:Ногайский язык|Ногайский]]: '''мен сени суьемен''' («мэн сэни сюемэн»).
# [[Файл:Flag of Peru.svg|22px|border]] [[w:Номацигенга|Номацигенга]]: '''ninintimíni''' («нининтимини»).
# [[Файл:Flag of Norway.svg|22px|border]] [[w:Букмол|Норвежский (букмол)]]: '''jeg elsker deg''' («йя эльске дай»).
# [[Файл:Flag of Norway.svg|22px|border]] [[w:Новонорвежский язык|Норвежский (нюнорск)]]: '''eg elskar deg''' («э эльске дай»).
# [[Файл:Flag of Normandie.svg|22px|border]] [[w:Нормандский язык|Нормандский]]: '''jè t'anor''' («же танор»), '''j'syis anorta dè tei''' («жьсюи анорта дэ тэй»).<p>[[w:Нормандский язык|Нормандско-французский]]: см. Нормандский<p>[[w:Северный стрейтс|Нортерн-стрейтс]]: см. Северный стрейтс
# [[Файл:Flag of Northumbria.svg|22px|border]] [[w:en:Northumbrian Old English|Нортумбрийский древнеанглийский]]: '''ic lufie þec''' («ик луфиэ сэк»).
# [[Файл:Flag of Norfolk Island.svg|22px|border]] [[w:Норфолкский язык|Норфолкский]]: '''i love yew''' («ай лав еу»).
# [[Файл:Proposed Yi flag.svg|22px|border]] [[w:Носу|Носу]]: '''ꉢꆎꉂ''' («на нин му»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Nso language|Нсо]]: '''m kong wo''' («мконгуо»).
# [[Файл:Niger state flag.png|22px|border]] [[w:en:Nupe language|Нупе]]: '''miye wawe''' («мийе уауэ»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Нутка (язык)|Нутка]]: '''yaʔakukʷah suw̕a''' («йяхакукуах суа»).<p>[[w:Нутка (язык)|Нуу-ча-нульт]]: см. Нутка
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Нухалк|Нухалк]]: '''lhkw'm ts i nu''' («искуимчину»).
# [[Файл:Flag of South Sudan.svg|22px|border]] [[w:Нуэр (язык)|Нуэр]]: '''nhɔa̱kä ji''' («ноако йи»).
# [[Файл:Bandeira de São Gabriel da Cachoeira (AM).png|22px|border]] [[w:Ньенгату|Ньенгату]]: '''ihé uru-aîhu''' («ихе уру аиху»).
# [[Файл:Flag of Tanzania.svg|22px|border]] [[w:en:Nyamwezi language|Ньямвези]]: '''itogwa benekele ne benekele''' («итогва бенекеле не бенекеле»).
# [[Файл:Flag of Malawi.svg|22px|border]] [[w:Ньянджа|Ньянджа]]: '''ndimakukonda''' («ндимакуконда»).
# [[Файл:Flag of Ankole.svg|22px|border]] [[w:Ньянколе|Ньянколе]]: '''ninkukunda''' («нинкукунда»).<p>[[w:Ньянколе|Ньянкоре]]: см. Ньянколе<p>[[w:Новонорвежский язык|Нюнорск]]: см. Норвежский (нюнорск)<p>[[w:Новонорвежский язык|Нюношк]]: см. Норвежский (нюнорск)<p>[[w:Древнерусский язык|Общевосточнославянский]]: см. Древнерусский<p>[[w:Праславянский язык|Общеславянский]]: см. Праславянский<p>[[w:Ошивамбо|Овамбо]]: см. Ошивамбо
# [[Файл:Flag of Ontario.svg|22px|border]] [[w:Оджибве (язык)|Оджибве]]: '''gizaagi’in''' («гизаги ин»).<p>[[w:Ория (язык)|Одия]]: см. Ория<p>[[w:Южноюкагирский язык|Одульский]]: см. Южноюкагирский<p>[[w:Алтайский язык|Ойротский]]: см. Алтайский
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Оканаган (язык)|Оканаган]]: '''kʷhin xmenč''' («кухин хмэнч»).<p>[[w:Ндюка|Оканиси]]: см. Ндюка
# [[Файл:Flag of Okinawa Prefecture.svg|22px|border]] [[w:Окинавский язык|Окинавский]]: '''好ちゅさ''' («шичусаа»).
# [[Файл:Flag of Okinawa Prefecture.svg|22px|border]] [[w:en:Okinawan Japanese|Окинавский японский]]: '''かなさん どお''' («канасан доо»).
# [[Файл:Delta State Flag.gif|22px|border]] [[w:en:Okpe language (Southwestern Edo)|Окпе (юго-западный эдо)]]: '''mi ji vwo ẹguọlọ kpahuọn''' («ми джи вуо эгуоло кпахуон»).
# [[Файл:Flag of Occitania.svg|22px|border]] [[w:Окситанский язык|Окситанский]]: '''t’aimi''' («тэми»).
# [[Файл:Flag de Occidental.svg|22px|border]] [[w:Окциденталь|Окциденталь]]: '''yo ama te''' («йо ама тэ»).<p>[[w:Языки лухья|Олулуйя]]: см. Лухья
# [[Файл:Flag of Nebraska.svg|22px|border]] [[w:Омаха-понка|Омаха-понка]]: '''xcháwithe''' («хчавитхе»).<p>[[w:Воламо|Омета]]: см. Воламо<p>[[w:Онейда (язык)|Онайда]]: см. Онейда
# [[Файл:Flag of the Iroquois Confederacy.svg|22px|border]] [[w:Онейда (язык)|Онейда]]: '''kunolúkhwa̱’''' («кунолукхва»).
# [[Файл:Bandera d'Orissa.svg|22px|border]] [[w:Ория (язык)|Ория]]: '''ମୁଁ ତୁମକୁ ଭଲପାଏ''' («муу тумаку бхалапааэ»).
# [[Файл:Flag of the Oromo Liberation Front.svg|22px|border]] [[w:Оромо (язык)|Оромо]]: '''sin jaalladha''' («син джаалладха»).<p>[[w:Орочский язык|Ороченский]]: см. Орочский
# [[Файл:Flag of Khabarovsk Krai.svg|22px|border]] [[w:Орочский язык|Орочский]]: '''би аяуме сино''' («би аяумэ сино»).<p>[[w:Ньянколе|Оруньянкоре]]: см. Ньянколе
# [[Файл:Flag of the Göktürks Khaganate.svg|22px|border]] [[w:Орхоно-енисейский язык|Орхоно-енисейский]]: '''𐰾𐰃𐰤𐰃∶𐰾𐰋𐰼∶𐰢𐰤・''' («нм рбс инис»).
# [[Файл:Flag of the Osage Nation.svg|22px|border]] [[w:Осейдж (язык)|Осейдж]]: '''wíohta''' («уиота»).
# [[Файл:Flag of South Ossetia.svg|22px|border]] [[w:Осетинский язык|Осетинский]]: '''æз дæ уарзын''' («аз да уарзын»).
# [[Файл:Flag of the Ottoman Empire (1844–1922).svg|22px|border]] [[w:Османский язык|Османский]]: '''سنی سویورم''' («сани соёрам»).<p>[[w:Османский язык|Османско-турецкий]]: см. Османский<p>[[w:Селькупские языки|Остяко-самоедский]]: см. Селькупский<p>[[w:Хантыйский язык|Остяцкий]]: см. Хантыйский<p>[[w:Тетела (язык)|Отетела]]: см. Тетела
# [[Файл:Otomi Nation flag.svg|22px|border]] [[w:Отоми (язык)|Отоми]]: '''hmädi''' («хмади»).<p>[[w:Гереро (язык)|Очигереро]]: см. Гереро
# [[Файл:Flag of Ovamboland.svg|22px|border]] [[w:Ошивамбо|Ошивамбо]]: '''ondiku hole''' («ондику холе»).<p>[[w:Северный паюте|Павиотсо]]: см. Северный паюте<p>[[w:Юэ (язык)|Паква]]: см. Юэ<p>[[w:Покот (язык)|Пакот]]: см. Покот
# [[Файл:Flag of Subulussalam City.png|22px|border]] [[w:en:Pakpak language|Пакпак]]: '''holong do rohangku tu ho''' («холонг до рохангку ту хо»).<p>[[w:Палауский язык|Палау]]: см. Палауский
# [[Файл:Flag of Palau.svg|22px|border]] [[w:Палауский язык|Палауский]]: '''ng betik a renguk er kau''' («нг бетик а ренгук эр кау»).<p>[[w:Капампанганский язык|Пампанго]]: см. Капампанганский<p>[[w:Капампанганский язык|Пампангуэно]]: см. Капампанганский
# [[Файл:Toon pangasinan flag.svg|22px|border]] [[w:Пангасинанский язык|Пангасинанский]]: '''inaru taka''' («инару така»).
# [[Файл:Flag of Punjab.svg|22px|border]] [[w:Панджаби|Панджаби]]: '''ਮੈਂ ਤੈਨੂੰ ਪਿਆਰ ਕਰਦਾ ਹਾਂ''' («майм тайнуум пьяар кардаа хаам») — женщине; '''ਮੈਂ ਤੈਨੂੰ ਪਿਆਰ ਕਰਦੀ ਹਾਂ''' («майм тайнуум пьяар кардии хаам») — мужчине.<p>[[w:Панджаби|Панджабский]]: см. Панджаби<p>[[w:Межславянский язык|Панславянский]]: см. Межславянский
# [[Файл:Flag of the Pa-O National Organisation.svg|22px|border]] [[w:en:Pa'O language|Пао]]: '''ခွေရက်ငါႏနာꩻ''' («хквайраатнгарнар»).
# [[Файл:Flag of Veracruz.svg|22px|border]] [[w:Папантланский тотонакский язык|Папантланский тотонакский]]: '''kpaxkiyan''' («кпакскийан»).<p>[[w:Малайско-португальский креольский язык|Папия-кристанг]]: см. Малайско-португальский креольский
# [[Файл:Flag of the Netherlands Antilles (1986-2010).svg|22px|border]] [[w:Папьяменто|Папьяменто]]: '''mi ta stima bo''' («ми та стима бо»).<p>[[w:Рапануйский язык|Пасхальский]]: см. Рапануйский
# [[Файл:Bandeira do Amazonas.svg|22px|border]] [[w:Паумари|Паумари]]: '''o-nofi-ki 'ira''' («о нофи ки ира»).<p>[[w:Туамоту (язык)|Паумоту]]: см. Туамоту<p>[[w:Пушту|Пашто]]: см. Пушту<p>[[w:Северный сото|Педи]]: см. Северный сото
# [[Файл:Flag of China.svg|22px|border]] [[w:Пекинский диалект|Пекинский]]: '''我爱你''' («во ай ни»).<p>[[w:Панджаби|Пенджаби]]: см. Панджаби<p>[[w:Панджаби|Пенджабский]]: см. Панджаби
# [[Файл:Flag of Pennsylvania Germans.jpg|22px|border]] [[w:Пенсильванско-немецкий диалект|Пенсильванско-немецкий]]: '''ich liebe dich’''' («ихь либэ дихь»).
# [[Файл:State flag of Iran (1964–1980).svg|22px|border]] [[w:Персидский язык|Персидский]]: '''دوستت دارم''' («дустэт дорэм»).
# [[Файл:Flag of Kurdistan.svg|22px|border]] [[w:Пехлевани|Пехлевани]]: '''دوسد توام''' («дусд туам»).<p>[[w:Покот (язык)|Пёкоот]]: см. Покот<p>[[w:Покот (язык)|Пёкот]]: см. Покот<p>[[w:Хири-моту|Пиджин моту]]: см. Хири-моту
# [[Файл:Flag of the Solomon Islands.svg|22px|border]] [[w:Пиджин Соломоновых Островов|Пиджин Соломоновых Островов]]: '''mi lovem iu''' («ми ловем ю»).<p>[[w:Блэкфут (язык)|Пикании]]: см. Блэкфут
# [[Файл:Flag of Picardie.svg|22px|border]] [[w:Пикардский язык|Пикардский]]: '''j’t’ai ker''' («жьтэ кер»).<p>[[w:Филиппинский язык|Пилипино]]: см. Филиппинский
# [[Файл:Flag of Pipil.svg|22px|border]] [[w:Пипиль (язык)|Пипиль]]: '''nimetzneki''' («нимэтцнэки»); '''nimetztasujta''' («нимэтцтасуйта»).
# [[Файл:Sami flag.svg|22px|border]] [[w:Пите-саамский язык|Пите-саамский]]: '''mån iehtsáv duv''' («мон йехтсав дув»).
# [[Файл:Flag of the Pitcairn Islands.svg|22px|border]] [[w:Питкэрнский язык|Питкэрнский]]: '''i love yew''' («ай лав еу»).
# [[Файл:Anangu Traditional Owners Flag.svg|22px|border]] [[w:Питьянтьятьяра|Питьянтьятьяра]]: '''ngayulu nyuntumpa mukuringanyi''' («нгаюлу ньюнтумпа мукуринганьи»).
# [[Файл:Flag of Madagascar.svg|22px|border]] [[w:Малагасийский язык|Плато]]: '''tiako ianao''' («тьяко йанао»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Покомам (язык)|Покомам]]: '''henwa tow hawach''' («энуа тоу ауач»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:Покот (язык)|Покот]]: '''achaminyi''' («ачаминьи»).
# [[Файл:Proposed Polabian Flag.jpg|22px|border]] [[w:Полабский язык|Полабский]]: '''liaibu tibë''' («льяйбу тибэ»).<p>[[w:Хири-моту|Полицейский моту]]: см. Хири-моту
# [[Файл:Flag of Poland.svg|22px|border]] [[w:Польский язык|Польский]]: '''kocham cię''' («кохам че»).
# [[Файл:Flag of the Republic of Tamrash.svg|22px|border]] [[w:Помакский язык|Помакский]]: '''gelyam te''' («гелям те»).<p>[[w:Понпейский язык|Понапе]]: см. Понпейский
# [[Файл:Flag of Pohnpei.svg|22px|border]] [[w:Понпейский язык|Понпейский]]: '''i poakohng uhk''' («и покоон уук»).
# [[Файл:Flag of Pontus.svg|22px|border]] [[w:Понтийский язык|Понтийский]]: '''αγαπώ σε''' («агапо сэ»).<p>[[w:Понтийский язык|Понтийский греческий]]: см. Понтийский
# [[Файл:Flag of Portugal.svg|22px|border]] [[w:Португальский язык|Португальский]]: '''amo-te''' («амотэ»).
# [[Файл:Xa-pokag.gif|22px|border]] [[w:Потаватоми (язык)|Потаватоми]]: '''ktabanIn''' («ктабанин»).
# [[Файл:Flag of Germany.svg|22px|border]] [[w:Прагерманский язык|Прагерманский]]: '''*ek frijō þek''' («эк фрийоо сэк»).
# [[Файл:Flag of Europe.svg|22px|border]] [[w:Праиндоевропейский язык|Праиндоевропейский]]: '''*éǵh₂om lubʰō tué''' («эгхом лубоо туэ»).
# [[Файл:Flag of Italy.svg|22px|border]] [[w:Праиталийский язык|Праиталийский]]: '''*tē amāō''' («тээ амааоо»).
# [[Файл:Banniel Keltia.svg|22px|border]] [[w:Пракельтский язык|Пракельтский]]: '''*tē karū''' («тээ каруу»).
# [[Файл:World Flag (2004).svg|22px|border]] [[w:Праностратический язык|Праностратический]]: '''*mi ṭi q̣urE''' («ми ти курэ»).
# [[Файл:Kolovrat_flag.svg|22px|border]] [[w:Праславянский язык|Праславянский]]: '''*(j)azъ ľubľǫ tę''' («(я)азу люблён тэн»).
# [[Файл:Flag of the Slovene Nation.svg|22px|border]] [[w:Прекмурско-словенский язык|Прекмурско-словенский]]: '''volim te''' («волим тэ»).
# [[Файл:Flag of Abruzzo.svg|22px|border]] [[w:Претароло|Претароло]]: '''t'iamoij''' («тьямоидж»).
# [[Файл:Flag of Tsimshian people.jpg|22px|border]] [[w:Прибрежно-цимшианский язык|Прибрежно-цимшианский]]: '''nsiipn düüt nüün''' («нсиипн дюют нююн»).
# [[Файл:Flag of Sabah.svg|22px|border]] [[w:en:Coastal Kadazan dialect|Прибрежный кадазанский]]: '''guminavo zou diau''' («гуминаво зоу диау»).
# [[Файл:Flag of California.svg|22px|border]] [[w:Прибрежный мивокский язык|Прибрежный мивокский]]: '''kajómu ˀópu míi''' («кайому опу мии»).
# [[Файл:Flag of Provence.svg|22px|border]] [[w:Провансальский диалект|Провансальский]]: '''t’ame''' («т’ам»).
# [[Файл:Flag of England.svg|22px|border]] [[w:Простой английский язык|Простой английский]]: '''i love you''' («ай лав ю»).
# [[Файл:Flag of Baltic Prussian revivalists.svg|22px|border]] [[w:Прусский язык|Прусский]]: '''as mīli tin''' («ас миили тин»), '''as tien milē''' («ас тьен милее»).
# [[Файл:Flag of Poitou.svg|22px|border]] [[w:Пуатевинское наречие|Пуатевинский]]: '''i t’aeme''' («и тэм»).
# [[Файл:Flag of Senegal.svg|22px|border]] [[w:en:Pulaar language|Пулаар]]: '''mi yidi maa’''' («ми йиди маа»).
# [[Файл:Flag of Guinea.svg|22px|border]] [[w:en:Pular language|Пулар]]: '''mbe de yid ma’''' («мбе де йид ма»).<p>[[w:Фула (язык)|Пулар-фульфульде]]: см. Фула
# [[Файл:Flag of Gabon.svg|22px|border]] [[w:en:Punu language|Пуну]]: '''ni u rondi’''' («ни у ронди»).<p>[[w:Авадхи|Пурби]]: см. Авадхи
# [[Файл:Bandera purépecha.png|22px|border]] [[w:Пурепеча (язык)|Пурепеча]]: '''uémbekua''' («уэмбэкуа»).
# [[Файл:Bandeira do estado do Rio de Janeiro.svg|22px|border]] [[w:Пури (язык)|Пури]]: '''ha tl'amatl'i dieh''' («ха тламатли диех»).
# [[Файл:Proposed Flag of Romanchia.svg|22px|border]] [[w:en:Putèr|Путерский романшский]]: '''eau t’am''' («эу тэм»).
# [[Файл:Flag of China.svg|22px|border]] [[w:Путунхуа|Путунхуа]]: '''我愛你''' («уо ай ни»).
# [[Файл:Af pakht3.svg|22px|border]] [[w:Пушту|Пушту]]: '''زه ستا سره مینه لرم''' («за ста сара мина ларам»).
# [[Файл:Bandera del pueblo Günün a künä.svg|22px|border]] [[w:Пуэльче (язык)|Пуэльче]]: '''uük’ǘgü tsashkal''' («уюк'югю тсашкал»).
# [[Файл:Flag of Piedmont.svg|22px|border]] [[w:Пьемонтский язык|Пьемонтский]]: '''t’veuj bin''' («твёй бин»).<p>[[w:Лушуцид|Пьюджетский салишский]]: см. Лушуцид<p>[[w:Лесной энецкий язык|Пэ-бай]]: см. Лесной энецкий<p>[[w:Покот (язык)|Пэкот]]: см. Покот
# [[Файл:Flag of All Assam Tribal Sangha.svg|22px|border]] [[w:en:Rabha language|Рабха]]: '''nana ang pelem mana''' («нана анг пелем мана»).
# [[Файл:Flag of All Assam Tribal Sangha.svg|22px|border]] [[w:en:Rabha language|Рабха (коч)]]: '''nwngo ang mwka''' («нвнго анг мвка»).
# [[Файл:Flag of All Assam Tribal Sangha.svg|22px|border]] [[w:en:Rabha language|Рабха (рондани)]]: '''nango ang nemtata''' («нанго анг немтата»).<p>[[w:Западный кри|Равнинный кри]]: см. Западный кри<p>[[w:Эде (язык)|Раде]]: см. Эде
# [[Файл:Flag of Rajasthan.webp|22px|border]] [[w:Раджастхани|Раджастхани]]: '''hu/mhey thaa-neh prem karu chu/hu''' («ху/мхей тхаа-нэх прэм кару чу/ху»).
# [[Файл:Flag of the Kamtapur Liberation Organisation.svg|22px|border]] [[w:en:Rajbanshi language (Nepal)|Раджбанши]]: '''মুই তোক ভাল পাং''' («му и тока бхала пам»).
# [[Файл:Flag of San Andrés y Providencia.svg|22px|border]] [[w:Райсальский креольский язык|Райсальский креольский]]: '''a lov yu''' («а лов ю»).
# [[Файл:Flag of Bangladesh.svg|22px|border]] [[w:en:Rangpuri language|Рангпури]]: '''mui tok bhal paō''' («муи ток бхал пао»).<p>[[w:Рапануйский язык|Рапа-нуи]]: см. Рапануйский
# [[Файл:Flag of Rapa Nui, Chile.svg|22px|border]] [[w:Рапануйский язык|Рапануйский]]: '''hanga rahi au kia koe''' («ханга рахи ау киа коэ»).
# [[Файл:Proposed flag of Réunion (VAR).svg|22px|border]] [[w:en:Réunion Creole|Реюньонский креольский]]: '''mi aime a ou''' («ми эм а у»).<p>[[w:Русский жестовый язык|РЖЯ]]: см. Русский жестовый
# [[Файл:Flag of Rome.svg|22px|border]] [[w:Римский диалект|Римский итальянский]]: '''te vòjo bbène''' («тэ воджо ббэнэ»).
# [[Файл:Hunsrik language flag.png|22px|border]] [[w:Риограндский хунсрюкский диалект|Риограндский хунсрюкский]]: '''ich lieve dich''' («ихь лиэвэ дихь»).
# [[Файл:Rif Amazigh People Flag.svg|22px|border]] [[w:Рифский язык|Рифский]]: '''tekhsekhchek''' («тэхсэхчек») - мужчине, '''tekhsekhchem''' («тэхсэхчем») - женщине.<p>[[w:Лози (язык)|Рози]]: см. Лози<p>[[w:Римский диалект|Романеско]]: см. Римский итальянский<p>[[w:Цыганский язык|Романи]]: см. Цыганский
# [[Файл:Conlangflag.svg|22px|border]] [[w:Романид|Романид]]: '''yo ama te''' («йо ама тэ»).
# [[Файл:Conlangflag.svg|22px|border]] Романисо: '''mi amoran vi''' («ми аморан ви»).
# [[Файл:Proposed Flag of Romanchia.svg|22px|border]] [[w:Романшский язык|Романшский]]: '''jeu carezel tei''' («жэу карэзэль тэй»), '''jau hai tai gugent''' («жау хай тай гугэнт»).<p>[[w:Романьольский язык|Романьо]]: см. Романьольский
# [[Файл:Flag of Romagna.svg|22px|border]] [[w:Романьольский язык|Романьольский]]: '''a t’ vöi bëin''' («а т вёй бэйн»).
# [[Файл:PH-ROM Flag.png|22px|border]] [[w:en:Romblomanon language|Ромбломанонский]]: '''palangga ta ikaw''' («палангга та икау»).<p>[[w:Понтийский язык|Ромейка]]: см. Понтийский
# [[Файл:Flag of Rotuma (1987-1988).svg|22px|border]] [[w:Ротуманский язык|Ротуманский]]: '''gou hanis ‘e ‘äea''' («гоу ханис э яэа»).
# [[Файл:Rohingya flag.svg|22px|border]] [[w:Рохинджа (язык)|Рохинджа]]: '''𐴀𐴝𐴦𐴛 𐴃𐴡𐴌𐴠𐴥 𐴀𐴝𐴊𐴡𐴌 𐴒𐴡𐴌𐴞𐴥''' («анаи тоаре адоргори»).
# [[Файл:Flag of Rwanda.svg|22px|border]] [[w:Руанда (язык)|Руанда]]: '''ndagukunda''' («ндагукунда»).<p>[[w:Романшский язык|Руманшский]]: см. Романшский
# [[Файл:Flag of Romania.svg|22px|border]] [[w:Румынский язык|Румынский]]: '''te iubesc''' («тэ юбеск»).
# [[Файл:Flag of Burundi.svg|22px|border]] [[w:Рунди (язык)|Рунди]]: '''ndagukunda''' («ндагукунда»).<p>[[w:Ньянколе|Руньянкоре]]: см. Ньянколе
# [[Файл:Flag of Rusyns 2007.svg|22px|border]] [[w:Русинский язык|Русинский]]: '''любля тя''' («люблю тя»).
# [[Файл:Flag of Russia (1991-1993).svg|22px|border]] [[w:Русский язык|Русский]]: '''я тебя люблю''' («я тебя люблю»).
# [[Файл:Flag of Russia (1991-1993).svg|22px|border]] [[w:Русский жестовый язык|Русский жестовый]]: {{YouTube|TiuwnkK8Ejk|видео|start=0m10s}}.<p>[[w:Кяхтинский язык|Русско-китайский пиджин]]: см. Кяхтинский
# [[Файл:Flag of the Rutul People.svg|22px|border]] [[w:Рутульский язык|Рутульский]]: '''зас ву къыргара''' («зас ву къыргара»).
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:en:Ruund language|Руунд]]: '''nikukatin''' («никукатин»).
# [[Файл:Drapeau de la Savoie.svg|22px|border]] [[w:en:Savoyard dialect|Савойский франкопровансальский]]: '''jhe t’amo''' («жэ тьямо»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Сакапультекский язык|Сакапультекский]]: '''otz katnwilan''' («оц катнуилан»).<p>[[w:Варайский язык|Самар-лейте]]: см. Варайский
# [[Файл:Bandera d'Orissa.svg|22px|border]] [[w:en:Sambalpuri language|Самбалпури]]: '''muĩ tumku bhôl paesĩ''' («муи тумку бхол паэси»).
# [[Файл:Flag of Samoa.svg|22px|border]] [[w:Самоанский язык|Самоанский]]: '''ou te alofa ia te oe ''' («оу тэ алофа иа тэ оэ»).
# [[Файл:Flag of North Sulawesi.svg|22px|border]] [[w:en:Sangir language|Сангирский]]: '''iạ makěndagẹ̌ si kau''' («иан макэндаге си кау»).
# [[Файл:Flag of the Central African Republic.svg|22px|border]] [[w:Санго|Санго]]: '''mbi yé mô''' («мби е моо»).
# [[Файл:Flag of Angola.svg|22px|border]] [[w:Конго (язык)|Сан-сальвадорский конго]]: '''ni kou zololo''' («ни коу зололо»), '''zola ikuzolanga''' («зола икузоланга»), '''yitoma kuzolanga''' («йитома кузоланга»).
# [[Файл:Hinduism Flag.webp|22px|border]] [[w:Санскрит|Санскрит]]: '''त्वां कामयामि''' («твам камайами»).
# [[Файл:Santals (India) Flag.gif|22px|border]] [[w:Сантали|Сантали]]: '''ᱫᱩᱞᱟᱲ ᱢᱮ ᱚ ᱤᱝ''' («дулар мэ о инь»).
# [[Файл:Flag of the Zapotec Peoples.svg|22px|border]] [[w:Сапотекские языки|Сапотекский]]: '''nadxiie lii''' («наджиие лии»).
# [[Файл:Flag of Chad.svg|22px|border]] [[w:Сар (язык)|Сар]]: '''m'tari''' («мтари»).
# [[Файл:Flag of Suriname.svg|22px|border]] [[w:Сарамакканский язык|Сарамакканский]]: '''mi lobi i e''' («ми лоби и э»).
# [[Файл:Flag of Sardinia.svg|22px|border]] [[w:Сардинский язык|Сардинский]]: '''t’amo''' («тамо»).<p>[[w:Сардинский язык|Сардский]]: см. Сардинский<p>[[w:Суринамский хиндустани|Сарнами]]: см. Суринамский хиндустани<p>[[w:Суринамский хиндустани|Сарнами-хинди]]: см. Суринамский хиндустани<p>[[w:Суринамский хиндустани|Сарнами-хиндустани]]: см. Суринамский хиндустани
# [[Файл:Flag of Sassari.svg|22px|border]] [[w:Сассарский язык|Сассарский]]: '''ti vogliu bè''' («ти волью бэ»).
# [[Файл:Flag of Washington.svg|22px|border]] [[w:Сахаптин|Сахаптин (якима)]]: '''atawishamash''' («атавишамаш»).
# [[Файл:Flag of the Saho People's Democratic Movement.svg|22px|border]] [[w:Сахо (язык)|Сахо]]: '''ku kixiniyo''' («ку кихинийо»).<p>[[w:Свати|Свази]]: см. Свати
# [[Файл:Banner of the Principality of Svaneti.svg|22px|border]] [[w:Сванский язык|Сванский]]: '''მი სი მალატხი''' («ми си малатхи»).
# [[Файл:Flag of Eswatini.svg|22px|border]] [[w:Свати|Свати]]: '''ngiyakutsandza''' («нгийякутсандза»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Сво|Сво]]: '''me ne tzale weu''' («мэ нэ кяле уо»).
# [[Файл:Flag of the Karen National Union.svg|22px|border]] [[w:en:S'gaw Karen language|Сго]]: '''ယအဲၣ်နၤ''' («яэллуну»).<p>[[w:Себуанский язык|Себуано]]: см. Себуанский
# [[Файл:Flag of Mindanao (Alexander Noble, 1990).svg|22px|border]] [[w:Себуанский язык|Себуанский]]: '''gihigugma ko ikaw''' («гихигугма ко икау»).<p>[[w:Билин (язык)|Северноагавский]]: см. Билин
# [[Файл:Flag of China.svg|22px|border]] [[w:Севернокитайский язык|Севернокитайский]]: '''我愛你''' («уо ай ни»).
# [[Файл:Sami flag.svg|22px|border]] [[w:Северносаамский язык|Северносаамский]]: '''(mun) ráhkistan du''' («(мун) ра́хкистан туу»).
# [[Файл:Flag of Selkup people.svg|22px|border]] [[w:Северноселькупский язык|Северноселькупский]]: '''ман тащинты ӄыӄымпам''' («ман тащинты кхыкхымпам»).
# [[Файл:Nordfriesischeflagge.svg|22px|border]] [[w:Севернофризский язык|Севернофризский]]: '''ik hääw de liif''' («ик хяав дэ лииф»).<p>[[w:Носу|Северный и]]: см. Носу<p>[[w:Бабин-вицувитен|Северный кэрриер]]: см. Бабин-вицувитен
# [[Файл:Flag of Angola.svg|22px|border]] [[w:Северный мбунду|Северный мбунду]]: '''ngakuzulu''' («нгакузулу»).
# [[Файл:Flag of KwaNdbele.svg|22px|border]] [[w:Северный ндебеле|Северный ндебеле]]: '''ngiyakuthanda''' («гиякутанда»).<p>[[w:Северный паюте|Северный пайюте]]: см. Северный паюте
# [[Файл:Flag of Nevada.svg|22px|border]] [[w:Северный паюте|Северный паюте]]: '''nu soopeda u''' («ну соопэда у»).
# [[Файл:Flag of South Africa.svg|22px|border]] [[w:Северный сото|Северный сото]]: '''ke a go rata''' («ке а го рата»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Северный стрейтс|Северный стрейтс]]: '''χaƛnomɛč''' («халномэч»).<p>[[w:Пуэльче (язык)|Северный теуэльче]]: см. Пуэльче
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Хайда (язык)|Северный хайда]]: '''dáng díi ḵuyáadang''' («данг дии куйаданг»).
# [[Файл:Mari Ushem flag.svg|22px|border]] [[w:Северо-западный марийский язык|Северо-западный марийский]]: '''мӹнь тӹньӹм йоратем''' («мынь тыньым яратем»).<p>[[w:Курманджи (диалект курдского языка)|Севернокурдский]]: см. Курманджи
# [[Файл:Flag of Lower Saxony.svg|22px|border]] [[w:Северонижнесаксонский диалект|Северонижнесаксонский]]: '''ik heff di leev''' («ик хефф ди лиив»).<p>[[w:Северносаамский язык|Северосаамский]]: см. Северносаамский<p>[[w:Севернофризский язык|Северофризский]]: см. Севернофризский
# [[Файл:Flag of Seychelles.svg|22px|border]] [[w:Сейшельский креольский язык|Сейшельский креольский]]: '''mon kontan ou''' («мон контан у»).
# [[Файл:Flag of Selkup people.svg|22px|border]] [[w:Селькупские языки|Селькупский]]: '''мат ташэнд надрам’''' («мат та́шэнд на́драм»).
# [[Файл:Flag of Mozambique.svg|22px|border]] [[w:en:Sena language|Сена]]: '''ndisakufuna’''' («ндисакуфуна»).
# [[Файл:Flag of New York.svg|22px|border]] [[w:Сенека (язык)|Сенека]]: '''gönóöhgwa’''' («гоноохгуа»).<p>[[w:Северный сото|Сепеди]]: см. Северный сото
# [[Файл:Flag of Serbia and Montenegro (1992–2006).svg|22px|border]] [[w:Сербохорватский язык|Сербохорватский]]: '''волим те/volim te''' («волим тэ»).<p>[[w:Сербохорватский язык|Сербо-хорватский]]: см. Сербохорватский
# [[Файл:Flag of Serbia.svg|22px|border]] [[w:Сербский язык|Сербский]]: '''волим те/volim te''' («волим тэ»).<p>[[w:Сербохорватский язык|Сербскохорватский]]: см. Сербохорватский<p>[[w:Сербохорватский язык|Сербско-хорватский]]: см. Сербохорватский
# [[Файл:Flag of Senegal.svg|22px|border]] [[w:Серер (язык)|Серер]]: '''mi ngwinda''' («ми нгвинда»).<p>[[w:Серер (язык)|Серер-син]]: см. Серер
# [[Файл:Flag of California.svg|22px|border]] [[w:Серрано (язык)|Серрано]]: '''pihan'in 'emey''' («пиханин эмэй»).<p>[[w:Сейшельский креольский язык|Сеселва]]: см. Сейшельский креольский
# [[Файл:Flag of Lesotho.svg|22px|border]] [[w:Сесото|Сесото]]: '''ke a o rata''' («ке а о рата»), '''kea u rata''' («кеа у рата»).<p>[[w:Тсвана (язык)|Сетсвана]]: см. Тсвана
# [[Файл:Flag of Setomaa.svg|22px|border]] [[w:Сету (диалект)|Сету]]: '''ma sinno sallin'''' («ма си́нно са́ллин»).
# [[Файл:Proposed Flag of Sephardi Jews.svg|22px|border]] [[w:Сефардский язык|Сефардский]]: '''טי אמו''' («тэ амо»).<p>[[w:Сейшельский креольский язык|Сешелва]]: см. Сейшельский креольский
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Сешельт|Сешельт]]: '''x̌áƛ’númičen''' («халнумичен»).
# [[Файл:Flag of Siberia.svg|22px|border]] [[w:uk:Сибірська мова|Сибирский]]: '''я тя дружу''' («я тя дружу»).
# [[Файл:Siberian Tatar Flag.svg|22px|border]] [[w:Сибирско-татарский язык|Сибирско-татарский]]: '''min sine sөyəm''' («мин синэ сойом»).
# [[Файл:Drapeau de la Sidama.png|22px|border]] [[w:Сидамо (язык)|Сидамо]]: '''baxeemmohe''' («батеэммохэ»).<p>[[w:Блэкфут (язык)|Сиксика]]: см. Блэкфут
# [[Файл:Flag of Puebla.svg|22px|border]] [[w:Силакайоапанский миштекский язык|Силакайоапанский миштекский]]: '''ku toulló ñeloosí''' («ку тоулло ньелооси»).
# [[Файл:Flag of Silesians.svg|22px|border]] [[w:Силезский язык|Силезский]]: '''jo ci przaja''' («йо чи прзайя»).<p>[[w:Лози (язык)|СиЛози]]: см. Лози<p>[[w:Силхетский язык|Силоти]]: см. Силхетский <p>[[w:Силхетский язык|Силхети]]: см. Силхетский
# [[Файл:Flag of Bangladesh.svg|22px|border]] [[w:Силхетский язык|Силхетский]]: '''ꠝꠥꠁ ꠔꠥꠝꠣꠞꠦ ꠝꠣꠄꠀ ꠇꠞꠤ''' («муи тумаре маэа шори»), '''ꠀꠝꠤ ꠔꠥꠝꠣꠞꠦ ꠝꠣꠄꠀ ꠇꠞꠤ''' («ами тумаре маэа шори»), '''ꠝꠥꠁ ꠔꠞꠦ ꠝꠣꠄꠀ ꠇꠞꠤ''' («муи торе маэа шори»), '''ꠀꠝꠤ ꠔꠞꠦ ꠝꠣꠄꠀ ꠇꠞꠤ''' («ами торе маэа шори»). <p>[[w:Сильбо гомеро|Сильбо-гомера]]: см. Сильбо гомеро
# [[Файл:Bandera Provincial de Santa Cruz de Tenerife.svg|22px|border]] [[w:Сильбо гомеро|Сильбо гомеро]]: {{YouTube|EskC5-FzXhA|видео|start=5m22s}}.
# [[Файл:King of Kandy.svg|22px|border]] [[w:Сингальский язык|Сингальский]]: '''මම ඔයාට ආදරෙයි''' («мама ойяата аадарэйи»).
# [[Файл:Flag of Singapore.svg|22px|border]] [[w:Сингапурский вариант английского языка|Сингапурский английский]]: '''i lurf you''' («ай лаф ю»).<p>[[w:Сингапурский вариант английского языка|Синглиш]]: см. Сингапурский английский
# [[Файл:Conlangflag.svg|22px|border]] [[w:Синдарин|Синдарин]]: '''gi melin''' («ги мэлин»), '''gen melin''' («гэн мэлин»), '''melon le''' («мэлон ле»), '''te melon''' («тэ мэлон»).<p>[[w:Северный ндебеле|Синдебеле]]: см. Северный ндебеле
# [[Файл:Flag of Sindhudesh.svg|22px|border]] [[w:Синдхи (язык)|Синдхи (лари)]]: '''آئون تو سان پيار ڪيان ٿو''' («аон то сан пьяр кьян то»).
# [[Файл:Flag of Sindhudesh.svg|22px|border]] [[w:Синдхи (язык)|Синдхи (утради)]]: '''مان تو سان پيار ڪيان ٿو''' («маан то сан пьяр кьян то»).
# [[Файл:Flag of the Romani people.svg|22px|border]] [[w:Синти (язык)|Синти]]: '''kamao tut''' («камао тут»).<p>[[w:Сингальский язык|Синхала]]: см. Сингальский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Сипакапенский язык|Сипакапенский]]: '''katnlq’oj''' («катнль к ох»).
# [[Файл:Pk seraiki mov.svg|22px|border]] [[w:Сирайки|Сирайки]]: '''میں تیں نل پیار کرینداں''' («мэн тэн пиар крэндан»).
# [[Файл:Syrian revolution flag.svg|22px|border]] [[w:Сирийский диалект арабского языка|Сирийский арабский]]: '''بحبك''' («бэхиббэк»).<p>[[w:Свати|Сисвати]]: см. Свати<p>[[w:Средневерхненемецкий язык|СВН]]: см. Средневерхненемецкий
# [[Файл:State flag of Iran (1964–1980).svg|22px|border]] [[w:en:Sistani dialect|Систанский персидский]]: '''دل میباله ترا''' («дэл мэ-бале тора»).
# [[Файл:Pine Ridge Flag.svg|22px|border]] [[w:Сиу (язык)|Сиу]]: '''techi 'hila''' («течи хила»).<p>[[w:Итонама (язык)|Сихнипадара]]: см. Итонама
# [[Файл:Flag of Sicily.svg|22px|border]] [[w:Сицилийский язык|Сицилийский]]: '''ti vogghiu''' («ти воггю»), '''t’аmu''' («таму»).<p>[[w:Лушуцид|Скагит-нискволли]]: см. Лушуцид<p>[[w:Шотландский язык (германский)|Скотс]]: см. Шотландский (германский)
# [[Файл:Flag of Slovakia.svg|22px|border]] [[w:Словацкий язык|Словацкий]]: '''ľúbim ťa''' («любим тя»).
# [[Файл:Flag of Slovenia.svg|22px|border]] [[w:Словенский язык|Словенский]]: '''ljubim te''' («любим тэ»).
# [[Файл:Flag of Slovio.svg|22px|border]] [[w:Словио|Словио]]: '''lubovijm te''' («лубовийм тэ»).<p>[[w:Прибрежно-цимшианский язык|Смалгах]]: см. Прибрежно-цимшианский<p>[[w:Прибрежно-цимшианский язык|Смалгиах]]: см. Прибрежно-цимшианский<p>[[w:Арабский литературный язык|Современный стандартный арабский]]: см. Арабский литературный
# [[Файл:Flag of Busoga (royal standard).png|22px|border]] [[w:Сога (язык)|Сога]]: '''nkwendha''' («нквендха»), '''nkwagala''' («нквагала»).
# [[Файл:Flag igora.svg|22px|border]] [[w:Ижорский язык|Сойкинский ижорский]]: '''miä suvvan sinnua''' («мия сувван синнуа»).<p>[[w:Пиджин Соломоновых Островов|Соломонский пиджин]]: см. Пиджин Соломоновых Островов
# [[Файл:Conlangflag.svg|22px|border]] [[w:Сольресоль|Сольресоль]]: '''do-re mi-la-si do-mi''' («до-ре ми-ля-си до-ми»).<p>[[w:Сомалийский язык|Сомали]]: см. Сомалийский
# [[Файл:Flag of Somalia.svg|22px|border]] [[w:Сомалийский язык|Сомалийский]]: '''waan ku jeclahay''' («уан ку джэклахай»).<p>[[w:Тундровый энецкий язык|Сомату]]: см. Тундровый энецкий
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:en:Songe language|Сонге]]: '''ne mukufule''' («нэ мукуфулэ»).
# [[Файл:Flag of Mali.svg|22px|border]] [[w:Сонинке (язык)|Сонинке]]: '''n w'an mulla''' («на мулла»), '''nga m’afini''' («нга мафини»), '''nda xanu''' («нда шану»).
# [[Файл:Flag of Kurdistan.svg|22px|border]] [[w:Сорани|Сорани]]: '''من عاشقتم''' («мэн ашкетэм») , '''xoştim dewê''' («хоштим дэуэ»).<p>[[w:Сусу (язык)|Сосо]]: см. Сусу<p>[[w:Сефардский язык|Спаньоль]]: см. Сефардский<p>[[w:Итонама (язык)|Срама]]: см. Итонама
# [[Файл:Flag of Suriname.svg|22px|border]] [[w:Сранан-тонго|Сранан-тонго]]: '''mi lobi joe''' («ми лоби ю»).
# [[Файл:Flag of Gwynedd.svg|22px|border]] [[w:Средневаллийский язык|Средневаллийский]]: '''mi a’th garaf''' («ми аф гараф»).
# [[Файл:Flag of the First Zionist Congress 1897.svg|22px|border]] [[w:Средневековый иврит|Средневековый иврит]]: '''אני אוהב אותך''' («ани оев асах») — женщине; '''אני אוהבת אותך''' («ани оевес осха») — мужчине.
# [[Файл:Heiliges Römisches Reich - Reichssturmfahne vor 1433 (Nimbierter Adler).svg|22px|border]] [[w:Средневерхненемецкий язык|Средневерхненемецкий]]: '''ich liebe dich''' («ихь лиебэ дихь»).
# [[Файл:Berber flag.svg|22px|border]] [[w:Стандартный марокканский берберский язык|Стандартный марокканский берберский]]: '''ⵃⴰⵎⵍⴰⵖⴽⴻⵎ''' («хэмлэркэм»).<p>[[w:Османский язык|Староанатолийско-тюркский]]: см. Османский
# [[Файл:Pendón heráldico de los Reyes Catolicos de 1475-1492.svg|22px|border]] [[w:Староиспанский язык|Староиспанский]]: '''amo te''' («амо тэ»).<p>[[w:Османский язык|Староосманский]]: см. Османский
# [[Файл:Kolovrat_flag.svg|22px|border]] [[w:Старославянский язык|Старославянский]]: '''азъ люблю тѧ/ⰰⰸⱏ ⰾⱓⰱⰾⱓ ⱅⱔ''' («азу люблю тэн»).
# [[Файл:Flag of France (XII-XIII).svg|22px|border]] [[w:Старофранцузский язык|Старофранцузский]]: '''aim te''' («эм тэ»).<p>[[w:Тупи (язык)|Старый тупи]]: см. Тупи
# [[Файл:Flag of Swahili.gif|22px|border]] [[w:Суахили|Суахили]]: '''nakupenda''' («накупэнда»).<p>[[w:Субанон (язык)|Субанен]]: см. Субанон
# [[Файл:Zamboanga Sibugay Flag.png|22px|border]] [[w:Субанон (язык)|Субанон]]: '''dlelamen hu yaa''' («длеламен ху яа»).
# [[Файл:Flag of Sudan.svg|22px|border]] [[w:Суданский диалект арабского языка|Суданский арабский]]: '''احبك''' («ахбк»).<p>[[w:Алютикский язык|Сук]]: см. Алютикский
# [[Файл:Flag of Tanzania.svg|22px|border]] [[w:Сукума (язык)|Сукума]]: '''nakutogilwe''' («накутогилве»), '''itogwa benekele ne benekele’''' («итогва бенекеле не бенекеле»).
# [[Файл:Late 19th Century Flag of Sulu.svg|22px|border]] [[w:Сулу (язык)|Сулу]]: '''kalasahan’''' («каласахан»).
# [[Файл:Unofficial flag of Nagaland.svg|22px|border]] [[w:en:Sümi language|Суми]]: '''niye no kimiye ani''' («нийе но кимийе ани»), '''nighi okikiye cheni''' («ниги окикийе чени»), '''niye no kimiyecheni''' («нийе но кимийечени»).<p>[[w:Тетела (язык)|Сунгу]]: см. Тетела
# [[Файл:Flag of Pasundan.svg|22px|border]] [[w:Сунданский язык|Сунданский]]: '''abdi bogoh ka anjeun''' («абди богох ка анджеун») - романтически, '''abdi nyaah ka anjeun''' («абди ньяа ка анджеун») - неромантически.<p>[[w:Зуни (язык)|Суньи]]: см. Зуни
# [[Файл:Bihar Government Banner.png|22px|border]] [[w:en:Surjapuri language|Сурджапури]]: '''mi tok pyār korchi''' («ми ток пьяр корчи»).<p>[[w:Ассирийский новоарамейский язык|Сурет]]: см. Ассирийский новоарамейский
# [[Файл:Flag of Surigao del Norte.svg|22px|border]] [[w:en:Surigaonon language|Суригаонон]]: '''taghigugma ta kaw''' («тагхигугма та кау»).<p>[[w:Сранан-тонго|Суринамский]]: см. Сранан-тонго
# [[Файл:Flag of Suriname.svg|22px|border]] [[w:Суринамский хиндустани|Суринамский хиндустани]]: '''hum toke chahila''' («хум токе чахила»).
# [[Файл:Proposed Flag of Romanchia.svg|22px|border]] [[w:en:Surmiran dialect|Сурмиранский романшский]]: '''ia at carez''' («йяат карэз»).
# [[Файл:Proposed Flag of Romanchia.svg|22px|border]] [[w:en:Sursilvan|Сурсильванский романшский]]: '''jeu carezel tei''' («жэу карэзэль тэй»).
# [[Файл:Flag of Guinea.svg|22px|border]] [[w:Сусу (язык)|Сусу]]: '''ira fan ma''' («ира фан ма»).<p>[[w:Сесото|Суто]]: см. Сесото
# [[Файл:Proposed Flag of Romanchia.svg|22px|border]] [[w:de:Sutselvische Sprache|Сутсильванский романшский]]: '''jou t’am''' («жу тэм»).<p>[[w:Алютикский язык|Сухпиак]]: см. Алютикский<p>[[w:Алютикский язык|Сухстстун]]: см. Алютикский<p>[[w:Носу|Сычуаньский и]]: см. Носу<p>[[w:Крио|Сьерра-леонский креольский]]: см. Крио
# [[Файл:Flag of China.svg|22px|border]] [[w:Сян (язык)|Сян]]: '''你很好''' («ни хэн хао»).
# [[Файл:Ethnic flag of Tabasarans.svg|22px|border]] [[w:Табасаранский язык|Табасаранский]]: '''узуз уву ккунжазуз''' («увуз уву ккунжазуз»).<p>[[w:Северный ндебеле|Табеле]]: см. Северный ндебеле<p>[[w:Нганасанский язык|Тавгийский]]: см. Нганасанский<p>[[w:Нганасанский язык|Тавгийско-самоедский]]: см. Нганасанский<p>[[w:Нганасанский язык|Тавгинский]]: см. Нганасанский<p>[[w:Тагальский язык|Тагалог]]: см. Тагальский<p>[[w:Тагальский язык|Тагалогский]]: см. Тагальский
# [[Файл:Flag of the Tagalog people.svg|22px|border]] [[w:Тагальский язык|Тагальский]]: '''mahal kita''' («махаль кита»), '''iniibig kita''' («инибиг кита»).
# [[Файл:Flag of Sabah.svg|22px|border]] [[w:en:Tagol language|Тагол-мурут]]: '''asiha au riun''' («асиха ау риун»).
# [[Файл:Flag of Tajikistan.svg|22px|border]] [[w:Таджикский язык|Таджикский]]: '''ман туро дӯст медорам''' («ман туро дёст медорам»).<p>[[w:Таджикский язык|Таджикский фарси]]: см. Таджикский
# [[Файл:Flag of CARICOM.svg|22px|border]] [[w:Таино (языки)|Таино]]: '''dak'ro buk''' («дакро бук»).<p>[[w:Таитянский язык|Таити]]: см. Таитянский
# [[Файл:Flag of French Polynesia.svg|22px|border]] [[w:Таитянский язык|Таитянский]]: '''ua here vau ia oe''' («уа херэ вау иа оэ»).<p>[[w:Тайваньский хокло|Тайваньский]]: см. Тайваньский хокло
# [[Файл:Flag of the Republic of China.svg|22px|border]] [[w:Тайваньский хокло|Тайваньский хокло]]: '''我愛你''' («гоа ай ли»).<p>[[w:Шанский язык|Тай-дэхун]]: см. Шанский
# [[Файл:Flag of Thailand.svg|22px|border]] [[w:Тайский язык|Тайский]]: '''ผมรักคุณ''' («пом рак кун») — женщине; '''ฉันรักคุณ''' («чан рак кун») — мужчине.
# [[Файл:Flag of the Kingdom of Talossa.svg|22px|border]] [[w:Талосский язык|Талосский]]: '''t'améu''' («т'амэу»).
# [[Файл:Flag of Yukon.svg|22px|border]] [[w:Талтан|Талтан]]: '''nedishcha''' («нэдишча»).
# [[Файл:Flag of the Talysh National Movement.svg|22px|border]] [[w:Талышский язык|Талышский]]: '''mı tıni pidәme''' («мы тыни пидамэ»), '''mı tibә pıdәm''' («мы тибэ пидэм»).<p>[[w:Стандартный марокканский берберский язык|Тамазигхт]]: см. Стандартный марокканский берберский<p>[[w:Западнотамахакский язык|Тамахак]]: см. Западнотамахакский
# [[Файл:Touareg People Flag.svg|22px|border]] [[w:Тамашек|Тамашек]]: '''riqqim''' («рикким»).<p>[[w:Тамашек|Тамашекин]]: см. Тамашек
# [[Файл:Bicolor flag of Tamil Eelam.svg|22px|border]] [[w:Тамильский язык|Тамильский]]: '''நான் உன்னை நேசிக்கிறேன்''' («наан уннай неесиккиреен»).
# [[Файл:Flag of Gombe State.svg|22px|border]] [[w:Тангале (язык)|Тангале]]: '''ni lessigo''' («ни лессиго»).<p>[[w:Тангале (язык)|Тангле]]: см. Тангале<p>[[w:Тасе-нага|Тангса]]: см. Тасе-нага<p>[[w:Тараумара (язык)|Таракумара]]: см. Тараумара<p>[[w:Пурепеча (язык)|Тараскский]]: см. Пурепеча<p>[[w:Тараумара (язык)|Тарахумара]]: см. Тараумара
# [[Файл:Flag of Chihuahua.svg|22px|border]] [[w:Тараумара (язык)|Тараумара]]: '''ni nígare''' («ни нигаре»).
# [[Файл:Unofficial flag of Nagaland.svg|22px|border]] [[w:Тасе-нага|Тасе-нага]]: '''ngiz räq ümznäq mäx lungvii täkängx''' («нгиз ряк юмзняк мякс лунгвии тякянгкс»).
# [[Файл:Tatar Nationalist Flag.svg|22px|border]] [[w:Татарский язык|Татарский]]: '''мин сине яратам''' («мин сине яратам»).
# [[Файл:Ethnic flag of Tat people (Caucasus).svg|22px|border]] [[w:Татский язык|Татский]]: '''мя туна мхостанум''' («мя туна мхостанум»), '''мя туря бахостанум''' («мя туря бахостанум»).<p>[[w:Западнотамахакский язык|Тахаггарк]]: см. Западнотамахакский<p>[[w:Чви|Тви]]: см. Чви<p>[[w:Северный ндебеле|Тебеле]]: см. Северный ндебеле<p>[[w:Тектитекский язык|Теко]]: см. Тектитекский<p>[[w:Тектитекский язык|Тектитек]]: см. Тектитекский<p>[[w:Тектитекский язык|Тектитеко]]: см. Тектитекский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Тектитекский язык|Тектитекский]]: '''nwan a’ich''' («нуан а ич»).
# [[Файл:Flag of Kemerovo Oblast.svg|22px|border]] [[w:Телеутское наречие|Телеутский]]: '''мен сени сӱӱп jадым''' («мен сэни сююп ядым»).
# [[Файл:Flag of Telangana.svg|22px|border]] [[w:Телугу|Телугу]]: '''నేను నిన్ను ప్రేమిస్తున్నాను''' («неэну нинну преэмистуннаану»).
# [[Файл:Flag of Togo (3-2).svg|22px|border]] [[w:Тем (язык)|Тем]]: '''mɔɔzɔɔlɛ́ɛ nya''' («моозоолеэ нья»).
# [[Файл:Flag of Sierra Leone.svg|22px|border]] [[w:Темне (язык)|Темне]]: '''eborithimu''' («эборитхиму»).
# [[Файл:Flag of Malaysia.svg|22px|border]] [[w:en:Temuan language|Темуанский]]: '''akuk sukak ajih''' («акук сукак аджих»).
# [[Файл:Flag of the Democratic Republic of the Congo.svg|22px|border]] [[w:Тетела (язык)|Тетела]]: '''dimi kolangaka''' («дими колангака»), '''dimi kokaka ngandji''' («дими кокака нганджи»), '''dimi nyolangaka''' («дими ньолангака»).
# [[Файл:Flag of East Timor.svg|22px|border]] [[w:Тетум|Тетум]]: '''hau hadomi o''' («хау хадомио»).<p>[[w:Тетум|Тетун]]: см. Тетум
# [[Файл:Flag of Dili.svg|22px|border]] [[w:Тетум|Тетун-дили]]: '''haʼu hadomi ó''' («ха у хадоми о»).<p>[[w:Тетум|Тетун-праса]]: см. Тетум
# [[Файл:Flag of the Tehuelche People.svg|22px|border]] [[w:Теуэльче (язык)|Теуэльче]]: '''inchepoyeneimi''' («инчепойенэйми»).
# [[Файл:Flag of Tibet.svg|22px|border]] [[w:Тибетский язык|Тибетский]]: '''ང་ཁྱེད་རང་ལ་དགའ་པོ་ཡོད་''' («нга каирангла гавпо йо»).
# [[Файл:Flag of Biafra.svg|22px|border]] [[w:Тив|Тив]]: '''u doom ishima''' («у доом ишима»).
# [[Файл:Flag of Eritrea.svg|22px|border]] [[w:Тигре (язык)|Тигре]]: '''ana enti efete''' («ана энти эфете»).
# [[Файл:Flag of the Tigray Region.svg|22px|border]] [[w:Тигринья|Тигринья]]: '''ይፈትወካ`የ''' («йфетуэкайе») — женщине; '''ይፈትወኪ’የ''' («йфетуэкийе») — мужчине.<p>[[w:Тамашек|Тимбукту]]: см. Тамашек
# [[Файл:Conlangflag.svg|22px|border]] [[w:en:Timerio|Тимерио]]: '''1-80-17''' («1-80-17»).<p>[[w:Тиндинский язык|Тиндальский]]: см. Тиндинский<p>[[w:Тиндинский язык|Тиндийский]]: см. Тиндинский
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Тиндинский язык|Тиндинский]]: '''де̄ мӣ гьелӯ''' («дэ ми гьелу»).<p>[[w:Ладинский язык|Тирольский ретороманский]]: см. Ладинский<p>[[w:Алютикский язык|Тихоокеанский юпик]]: см. Алютикский
# [[Файл:Flag of Canton of Ticino.svg|22px|border]] [[w:Тичинский диалект ломбардского языка|Тичинский ломбардский]]: '''ta vöri ben''' («та вэри бэн»).
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Тлингитский язык|Тлингитский]]: '''ixhsixhán''' («иххсиххан»).<p>[[w:Тлингитский язык|Тлинкитский]]: см. Тлингитский
# [[Файл:Flag of North Sumatra.svg|22px|border]] [[w:Тоба (язык)|Тоба]]: '''holong do rohangku tu ho''' («холонг до рохангку ту хо»).<p>[[w:Тоба (язык)|Тоба-батакский]]: см. Тоба
# [[Файл:Toki Pona flag.svg|22px|border]] [[w:Токипона|Токипона]]: '''mi olin e sina''' («ми олин э сина»).
# [[Файл:Flag of Papua New Guinea.svg|22px|border]] [[w:Ток-писин|Ток-писин]]: '''mi lavim yu''' («ми лавим ю»).
# [[Файл:Flag of Tokelau.svg|22px|border]] [[w:Токелау (язык)|Токелау]]: '''ko au e alofa atu''' («ко ау э алофа ату»).
# [[Файл:Nlakapamux Nation Flag.png|22px|border]] [[w:Томпсон (язык)|Томпсон]]: '''yaquindawe''' («якуиндауэ»).
# [[Файл:Flag of Rhodesia (1968-1979).svg|22px|border]] [[w:en:Tonga language (Zambia and Zimbabwe)|Тонга (Замбия/Зимбабве)]]: '''ndilakuyanda''' («ндилакуйанда»).
# [[Файл:Flag of Malawi.svg|22px|border]] [[w:en:Tonga language (Malawi)|Тонга (Малави)]]: '''nditikuyanja''' («ндитикуйанджа»).
# [[Файл:Flag of Tonga.svg|22px|border]] [[w:Тонганский язык|Тонганский]]: ''''oku ou 'ofa 'ia koe''' («оку оу офа иа коэ»).
# [[Файл:Flag of Los Angeles, California.svg|22px|border]] [[w:en:Tongva language|Тонгва]]: '''wiishmenokre''' («виишменокре»).<p>[[w:Туника (язык)|Тоника]]: см. Туника
# [[Файл:Flag of Toro, Uganda.svg|22px|border]] [[w:en:Tooro language|Тооро]]: '''ninkugonza''' («нинкугонза»).
# [[Файл:Flag of Nizhneudinsky District.png|22px|border]] [[w:Тофаларский язык|Тофаларский]]: '''мен сенi эъккісіндыр мен''' («мэн сэни эккисиндыр мэн»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Тохолабальский язык|Тохолабальский]]: '''wa xkʼanawa''' («ва шканава»).<p>[[w:Ладинский язык|Трентинский]]: см. Ладинский
# [[Файл:Flag of Chuuk.svg|22px|border]] [[w:Трукский язык|Трукский]]: '''‘ai tong ngonuk''' («аи тонг нгонук») — формально, '''‘u pe reom''' («у пэ рэом») — неформально.<p>[[w:Ангами (язык)|Тсангло]]: см. Ангами
# [[Файл:Flag of Mozambique.svg|22px|border]] [[w:en:Tswa language|Тсва]]: '''nza ku ranza''' («нза ку ранза»).
# [[Файл:Flag of Botswana.svg|22px|border]] [[w:Тсвана (язык)|Тсвана]]: '''ke a go rata''' («ке а го рата»).<p>[[w:Ангами (язык)|Тсогами]]: см. Ангами
# [[Файл:Flag of Gazankulu.svg|22px|border]] [[w:Тсонга (язык)|Тсонга]]: '''ndza ku rhandza''' («ндза ку рандза»).<p>[[w:Ангами (язык)|Тсугуми]]: см. Ангами
# [[Файл:Flag of Tuamotu Archipelago.svg|22px|border]] [[w:Туамоту (язык)|Туамоту]]: '''ua here au ia koe''' («уа херэ ау иа коэ»).<p>[[w:Туамоту (язык)|Туамотуанский]]: см. Туамоту
# [[Файл:Flag of Tuvalu.svg|22px|border]] [[w:Тувалу (язык)|Тувалу]]: '''au e alofa ki a koe''' («ау э алофа ки а коэ»).
# [[Файл:Flag of Tuva.svg|22px|border]] [[w:Тувинский язык|Тувинский]]: '''мен сенээ ынак мен''' («мэн сэнээ ынак мэн»).
# [[Файл:Flag of Kenya.svg|22px|border]] [[w:en:Tugen language|Туген]]: '''achamin''' («атшамин»).<p>[[w:Гвичин|Тукуд]]: см. Гвичин
# [[Файл:Снимок экрана 2024-10-21 в 15.30.55.png|22px|border]] [[w:Тулу|Тулу]]: '''ಯಾನ್ ಈರೆನ್ ಮೋಕೆ ಮಲ್ಪುವೆ''' («яан иирэн мооке малпувэ»).
# [[Файл:Flag of Malawi.svg|22px|border]] [[w:Тумбука (язык)|Тумбука]]: '''nkhukutemwa''' («нкукутэмуа»).<p>[[w:Эвенкийский язык|Тунгусский]]: см. Эвенкийский
# [[Файл:Flag of Taymyr Autonomous Okrug.svg|22px|border]] [[w:Тундровый энецкий язык|Тундровый энецкий]]: '''модь тоди комитазʼʼ''' («модь тоди комита́з»).
# [[Файл:Flag of Louisiana.svg|22px|border]] [[w:Туника (язык)|Туника]]: '''ma ihkmahka''' («ма ихкмахка») - мужчине, '''hɛma ihkmahka''' («хема ихкмахка») - женщине.
# [[Файл:Flag of Tunisia.svg|22px|border]] [[w:Тунисский диалект арабского языка|Тунисский арабский]]: '''نحبك''' («нхэббик»).
# [[Файл:Flag of Brazil.svg|22px|border]] [[w:Тупи (язык)|Тупи]]: '''oroaûsub''' («ороауусуб»).<p>[[w:Тупи (язык)|Тупинамба]]: см. Тупи
# [[Файл:Flag of Chad.svg|22px|border]] [[w:en:Tupuri language|Тупури]]: '''ndi damo''' («нди дамо»).
# [[Файл:Flag of Turkey.svg|22px|border]] [[w:Турецкий язык|Турецкий]]: '''seni seviyorum''' («сени севиёрум»).
# [[Файл:Flag of Turin.svg|22px|border]] [[w:Пьемонтский язык|Туринский пьемонтский]]: '''ët veui bin''' («эт веуй бин»).
# [[Файл:Flag of Turkmenistan.svg|22px|border]] [[w:Туркменский язык|Туркменский]]: '''men seni söýýärin''' («мэн сэни сёйярин»).
# [[Файл:Flag of China.svg|22px|border]] [[w:У (язык)|У]]: '''我爱侬''' («нгу э нон»).
# [[Файл:Flag of the Isle of Wight.svg|22px|border]] Уайтский: '''íy ímbróðorlufu fa ðu''' («ий имбросорлуфу фа су»).<p>[[w:Воламо|Уаламо]]: см. Воламо
# [[Файл:Flag of Veracruz.svg|22px|border]] [[w:Уастекский науатль|Уастекский науатль]]: '''nimitstlasotla''' («нимицтласотла»).<p>[[w:Воламо|Уба]]: см. Воламо
# [[Файл:Circassian flag.svg|22px|border]] [[w:Убыхский язык|Убыхский]]: '''tʂʼanə wəzbja''' («тс ана уазбйа»).<p>[[w:Уоллисский язык|Увеа]]: см. Уоллисский<p>[[w:Эякский язык|Угаленцский]]: см. Эякский
# [[Файл:Flag of Rostov Oblast.svg|22px|border]] [[w:Удинский язык|Удинский]]: '''зу ва чуресса''' («зу ва чуресса»).
# [[Файл:Flag of Udmurtia.svg|22px|border]] [[w:Удмуртский язык|Удмуртский]]: '''мон тонэ яратӥсько''' («мон тонэ яратыщко»).
# [[Файл:Flag of Primorsky Krai.svg|22px|border]] [[w:Удэгейский язык|Удэгейский]]: '''би синова аюми''' («би синова аюми»).<p>[[w:Удэгейский язык|Удэйский]]: см. Удэгейский
# [[Файл:Flag of Uzbekistan.svg|22px|border]] [[w:Узбекский язык|Узбекский]]: '''men sizni sevaman''' («мэн сизни сэваман»).
# [[Файл:Kokbayraq flag.svg|22px|border]] [[w:Уйгурский язык|Уйгурский]]: '''مەن سېنى ياخشى كۆرىمەن''' («сизни яхши корман»).
# [[Файл:Flag of Ukraine.svg|22px|border]] [[w:Украинский язык|Украинский]]: '''я тебе кохаю''' («я тэбэ кохаю»).
# [[Файл:Flag of Khabarovsk Krai.svg|22px|border]] [[w:Ульчский язык|Ульчский]]: '''би симбэ улэсии''' («би симбэ улэсии»).<p>[[w:Южный мбунду|Умбунду]]: см. Южный мбунду
# [[Файл:Sami flag.svg|22px|border]] [[w:Уме-саамский язык|Уме-саамский]]: '''månna ráhkistan du''' («монна рахкистан ду»).
# [[Файл:Flag of Wallis and Futuna.svg|22px|border]] [[w:Уоллисский язык|Уоллисский]]: '''ʼe ʼau ʼofa ia koe''' («э ау офа иа коэ»).
# [[Файл:Flag of Pakistan.svg|22px|border]] [[w:Урду|Урду]]: '''میں آپ سے محبت کَرتا ہوں''' («мэйн ап сай мухабат карта хун») — женщине; '''میں آپ سے محبت کرتی ہوں''' («мэйн ап сай мухабат карти хун») — мужчине.
# [[Файл:Flag of Uropi.svg|22px|border]] [[w:en:Uropi|Уропи]]: '''i liam ta''' («и лиам та») - неформально; '''i liam va''' («и лиам ва») - формально.
# [[Файл:Bandeira do Maranhão.svg|22px|border]] [[w:Урубу-каапор (язык)|Урубу-каапор]]: '''rê-putare dê''' («рээ путарэ дээ»).
# [[Файл:Delta State Flag.gif|22px|border]] [[w:en:Urhobo language|Урхобо]]: '''mi vwo ẹguọnọ wẹn''' («ми вуо эгуоно вэн»).<p>[[w:Тувинский язык|Урянхайский]]: см. Тувинский<p>[[w:Успантекский язык|Успантек]]: см. Успантекский<p>[[w:Успантекский язык|Успантеко]]: см. Успантекский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Успантекский язык|Успантекский]]: '''at chwaaj''' («ат чуах»).<p>[[w:Алабугатско-татарский язык|Утарский]]: см. Алабугатско-татарский<p>[[w:Валлийский язык|Уэльский]]: см. Валлийский
# [[Файл:FlagOfWessex.svg|22px|border]] [[w:Уэссекский диалект древнеанглийского языка|Уэссекский древнеанглийский]]: '''ic lufie þe''' («ик луфиэ сэ»).<p>[[w:Уоллисский язык|Фака'увеа]]: см. Уоллисский
# [[Файл:Flag of Chin State.svg|22px|border]] [[w:en:Falam language|Фалам]]: '''ka lo duh tuk''' («ка ло дух тук»), '''ka lo ngai''' («ка ло нгаи»).
# [[Файл:Flag of Equatorial Guinea.svg|22px|border]] [[w:Фанг (язык)|Фанг]]: '''ma dzing wa''' («ма дзинг ва»), '''ma gnôre wa''' («ма гноре ва»).
# [[Файл:Flag of Ghana.svg|22px|border]] [[w:en:Fante dialect|Фанте]]: '''me dowapaa''' («мэ довапаа»).<p>[[w:Фарерский язык|Фарейский]]: см. Фарерский
# [[Файл:Flag of the Faroe Islands.svg|22px|border]] [[w:Фарерский язык|Фарерский]]: '''eg elski teg''' («э эльши тэ»).<p>[[w:Персидский язык|Фарси]]: см. Персидский<p>[[w:Дари|Фарси-кабули]]: см. Дари<p>[[w:Фарерский язык|Ферейский]]: см. Фарерский
# [[Файл:Bubi nationalist flag.svg|22px|border]] [[w:Фернандо-по (диалект)|Фернандо-по]]: '''mí lɛ́k yú''' («ми лэк ю»).
# [[Файл:Flag of Fiji.svg|22px|border]] [[w:Фиджийский язык|Фиджийский]]: '''au domoni iko''' («ондомони ико»), '''au lomani iko''' («онломани ико»).
# [[Файл:Flag of Fiji.svg|22px|border]] [[w:Фиджийский хинди|Фиджийский хинди]]: '''main bhi tumko pyaar karti hoon''' («маин бхи тумко пьяар карти хоон»).<p>[[w:Фиджийский хинди|Фиджийский хиндустани]]: см. Фиджийский хинди<p>[[w:Филиппинский язык|Филипино]]: см. Филиппинский
# [[Файл:Flag of Philippines.svg|22px|border]] [[w:Филиппинский язык|Филиппинский]]: '''mahal kita''' («махал кита»).
# [[Файл:Phoenician Language Flag.svg|22px|border]] [[w:Финикийский язык|Финикийский]]: '''𐤀𐤇𐤌𐤃𐤕𐤊''' («ахмдтк»).
# [[Файл:Flag of Finland.svg|22px|border]] [[w:Финский язык|Финский]]: '''minä rakastan sinua''' («ми́ня ра́кастан си́нуа»).
# [[Файл:Flag of Benin.svg|22px|border]] [[w:Фон (язык)|Фон]]: '''un yí wǎn nú we''' («уньи ван ну ве»).
# [[Файл:Greek flag (black cross).svg|22px|border]] [[w:Фракийский язык|Фракийский]]: '''az lopt ti''' («аз лопт ти»).
# [[Файл:Drapeau arpitan.svg|22px|border]] [[w:Франкопровансальский язык|Франкопровансальский]]: '''je t’amo''' («жэ тьямо»).
# [[Файл:Flag of France.svg|22px|border]] [[w:Французский язык|Французский]]: '''je t’aime''' («жё тэм»)
# [[Файл:Bandiere dal Friûl.svg|22px|border]] [[w:Фриульский язык|Фриульский]]: '''ti vuei ben''' («ти вуэй бэн»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Фула (язык)|Фула]]: '''mbe de yid ma''' («мбе дэ йид ма»).
# [[Файл:Flag of Uganda.svg|22px|border]] [[w:en:Fuliiru language|Фулииру]]: '''nakusima''' («накусима»).<p>[[w:Фриульский язык|Фурланский]]: см. Фриульский
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Fut language|Фут]]: '''ma khoŋe gho''' («ма кхонэ гхо»).
# [[Файл:Flag of Lesotho.svg|22px|border]] [[w:en:Phuthi language|Фути]]: '''giyakutshadza''' («гийякутшадза»).
# [[Файл:Bandera de Meghalaya.svg|22px|border]] [[w:en:Hajong language|Хаджонг]]: '''môy tôge bhala pae''' («мой тоге бхала паэ»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Хайда (язык)|Хайда]]: '''dang dii kuyadang''' («данг дии куйаданг»).<p>[[w:Чинский язык|Хака]]: см. Чинский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Хакальтекский язык|Хакальтекский]]: '''chach woche''' («чачуоче»).
# [[Файл:Khakas ethnic flag.svg|22px|border]] [[w:Хакасский язык|Хакасский]]: '''мин син хынара''' («мин син хынара»).<p>[[w:Сефардский язык|Хакетия]]: см. Сефардский
# [[Файл:Flag of the Republic of China.svg|22px|border]] [[w:Хакка (язык)|Хакка]]: '''𠊎愛你''' («на ой ни»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Халкомелем|Халкомелем (халкуминум)]]: '''nu stl’i’ ch''' («на стлич»).
# [[Файл:Flag of Yugra.svg|22px|border]] [[w:Хантыйский язык|Хантыйский]]: '''ма ԓӑӊӄԓәм нӱӊат''' («ма ланклэм нунат»).
# [[Файл:Occ. Mindoro Flag.png|22px|border]] [[w:en:Hanunoo language|Ханунуо]]: '''ᜫᜱᜮ᜴ ᜣᜯᜳ ᜨᜲᜣᜳ''' («махал каво нико»).<p>[[w:Хариани|Харианви]]: см. Хариани
# [[Файл:..Haryana Flag(INDIA).png|22px|border]] [[w:Хариани|Хариани]]: '''ma tanna pyaar karun hun''' («ма танна пьяар карун хун»), '''tu mane ghane achi lage''' («ту манэ гханэ ачи лаге»).
# [[Файл:Flag of Mauritania.svg|22px|border]] [[w:Хассания|Хассания]]: '''kanebgheek''' («канебгхеек»).
# [[Файл:Flag of the Hausa people.svg|22px|border]] [[w:Хауса (язык)|Хауса]]: '''ina son ka''' («ина сон ка») - мужчине, '''ina son ki''' («ина сон ки») - женщине.
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Хваршинский язык|Хваршинский]]: '''дил мо гокъше''' («дил мо гокше»).<p>[[w:Сефардский язык|Хебронео]]: см. Сефардский
# [[Файл:Hittite Culture Flag.webp|22px|border]] [[w:Хеттский язык|Хеттский]]: '''𒉡𒌓𒋫 𒀸𒅆𒅀𒈪''' («нуутта ассияни»).
# [[Файл:Flag of Hejaz (1917).svg|22px|border]] [[w:Хиджазский диалект арабского языка|Хиджазский арабский]]: '''أحبك''' («ахуббак») — мужчине, '''أحبك''' («ахуббик») — женщине.
# [[Файл:Flag of Ilolio (1886-1898).svg|22px|border]] [[w:Хилигайнон (язык)|Хилигайнон]]: '''palangga ta ka''' («палангга та ка»), '''palangga ko ikaw''' («палангга ко ико»), '''guina higugma ko ikaw''' («гуина хигугма ко ико»).
# [[Файл:Flag of India.svg|22px|border]] [[w:Хинглиш|Хинглиш]]: '''mujhe aapako pasand hai''' («муджэ аапако пасанд хай»).<p>[[w:Хиндустани|Хиндави]]: см. Хиндустани
# [[Файл:Flag of India.svg|22px|border]] [[w:Хинди|Хинди]]: '''मैं तुमसे प्यार करता हुँ''' («майн тумсе пьяар картаа хуун») — женщине; '''मैं तुमसे प्यार करती हुँ''' («майн тумсе пьяар картии хуун») — мужчине.
# [[Файл:Flag of India.svg|22px|border]] [[w:Хиндустани|Хиндустани]]: '''मैं तुमसे प्यार करता हुँ''' («майн тумсе пьяар картаа хуун»), '''میں آپ سے محبت کَرتا ہوں''' («мэйн ап сай мухабат карта хун») — женщине; '''मैं तुमसे प्यार करती हुँ''' («майн тумсе пьяар картии хуун»), '''میں آپ سے محبت کرتی ہوں''' («мэйн ап сай мухабат карти хун») — мужчине.
# [[Файл:Flag of Papua New Guinea.svg|22px|border]] [[w:Хири-моту|Хири-моту]]: '''oi lau lalokau henia''' («ои лау лалокау хэниа»).<p>[[w:Микасуки (язык)|Хитчити-микасуки]]: см. Микасуки
# [[Файл:Bandeira do Amazonas.svg|22px|border]] [[w:Хишкарьяна (язык)|Хишкарьяна]]: '''kɨxirohimayaha''' («кышироимайяа»).
# [[Файл:Hmong Flag (UNPO).svg|22px|border]] [[w:Хмонг (язык)|Хмонг]]: '''kuv hlub koj''' («кув хлуб кой»).
# [[Файл:Hmong Flag (UNPO).svg|22px|border]] [[w:Хмонг (язык)|Хмонг-даы]]: '''kuv hlub koj''' («кув хлуб кой»).
# [[Файл:Flag of Jharkhand.svg|22px|border]] [[w:Хо (язык)|Хо]]: '''amań sukua tana''' («аман сукуа тана»), '''abeneń sukua tana''' («абенен сукуа тана»).<p>[[w:Хокло (язык)|Хоккен]]: см. Хокло<p>[[w:Хокло (язык)|Хоккиен]]: см. Хокло
# [[Файл:Flag of the Republic of China.svg|22px|border]] [[w:Хокло (язык)|Хокло]]: '''我爱你''' («гоа ай ли»).
# [[Файл:Flag of Arizona.svg|22px|border]] [[w:Хопи (язык)|Хопи]]: '''nu’ umi unangwa’ta''' («ну уми унангуа та»).
# [[Файл:Fictitious flag of Lazistan.png|22px|border]] [[w:Лазский язык|Хопский лазский]]: '''ma si p'qorop''' («ма сип гороп»).<p>[[w:Сербохорватский язык|Хорватосербский]]: см. Сербохорватский<p>[[w:Сербохорватский язык|Хорвато-сербский]]: см. Сербохорватский
# [[Файл:Flag of Croatia.svg|22px|border]] [[w:Хорватский язык|Хорватский]]: '''volim te''' («волим тэ»).<p>[[w:Сербохорватский язык|Хорватскосербский]]: см. Сербохорватский<p>[[w:Сербохорватский язык|Хорватско-сербский]]: см. Сербохорватский<p>[[w:Сефардский язык|Худесмо]]: см. Сефардский<p>[[w:Сян (язык)|Хунаньский]]: см. Сян
# [[Файл:Hunsrik language flag.png|22px|border]] [[w:Хунсрюкский диалект|Хунсрюкский]]: '''ich liipe tich''' («ихь лиэпэ тихь»).<p>[[w:Истмусский сапотекский язык|Хучитанский сапотекский]]: см. Истмусский сапотекский
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Хэн (язык)|Хэн]]: '''nihtsį̀'''' («нитси»).
# [[Файл:Цахурский флаг Tsakhur flag علم تساخور.svg|22px|border]] [[w:Цахурский язык|Цахурский]]: '''зас гъу йикканна''' («зас г у йикканна») — женщине; '''зас гъу ыкканна''' («зас г у ыкканна») — мужчине.
# [[Файл:Flag of Dagestan.svg|22px|border]] [[w:Цезский язык|Цезский]]: '''даьр ми йетих''' («дар ми етих») — женщине; '''даьр ми этих''' («дар ми этих») — мужчине.<p>[[w:Цельтальский язык|Цельталь]]: см. Цельтальский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Цельтальский язык|Цельтальский]]: '''kʼuxat ta koʼtan''' («кушат та котан»).<p>[[w:Сорани|Центральнокурдский]]: см. Сорани
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Центрально-юпикский язык|Центрально-юпикский (чупик)]]: '''piniqamken''' («беникаамкен»).
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Центрально-юпикский язык|Центрально-юпикский (юпик)]]: '''kenkamken''' («кенкамкен»).
# [[Файл:PH-CAS Flag.png|22px|border]] [[w:Центральный бикольский язык|Центральный бикольский]]: '''namumutan ta ka''' («намумутан та ка»).<p>[[w:Луба-катанга|Центральный луба]]: см. Луба-катанга<p>[[w:Качинский язык|Цзинпо]]: см. Качинский
# [[Файл:Banner of the Federation of the Sette Comuni.svg|22px|border]] [[w:Цимбрский язык|Цимбрский]]: '''ich liibe-dich''' («ихь лиибэ-дихь»).<p>[[w:Бацбийский язык|Цоватский]]: см. Бацбийский<p>[[w:Бацбийский язык|Цова-тушинский]]: см. Бацбийский
# [[Файл:Conlangflag.svg|22px|border]] [[w:en:Tsolyáni language|Цольяни]]: '''lúm tupmér tsámmeri''' («лум тупмэр цаммэри») - любовнику или любимому человеку; '''lúm tupmér eyúltùsmi''' («лум тупмэр эюлтусми») - мужу или жене; '''lúm tupmér ìluntsám''' («лум тупмэр илунцам») - наложнице; '''lúm tupmér tsinéntùsmi''' («лум тупмэр цинэнтусми») - отцу или матери.
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Цоциль (язык)|Цоциль]]: '''jk'anojot''' («джкханоджот»).<p>[[w:Цоциль (язык)|Цоцильский]]: см. Цоциль
# [[Файл:Даргинский флаг (неофициальный, одна из народных вариаций).jpg|22px|border]] [[w:Цудахарский язык|Цудахарский]]: '''дам гIу риккутте''' («дам гу рикутэ»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Цутухильский язык|Цутухильский]]: '''natwajo'''' («натуахо»).
# [[Файл:Flag of the Romani people.svg|22px|border]] [[w:Цыганский язык|Цыганский]]: '''kamav tut''' («камав тут»), '''me mangav tut''' («мэ мангав тут»).
# [[Файл:Purported flag of the Republic of Zamboanga.svg|22px|border]] [[w:Чабакано|Чабакано (замбоангеньо)]]: '''ama yo contigo''' («ама йо контиго»).
# [[Файл:Flag of Cavite City.svg|22px|border]] [[w:Чабакано|Чабакано (кавитеньо)]]: '''ta ama yo cuntigo''' («та ама йо кунтиго»).<p>[[w:Чабакано|Чавакано]]: см. Чабакано
# [[Файл:Flag of Chad.svg|22px|border]] [[w:Чадский диалект арабского языка|Чадский арабский]]: '''ni ridiki''' («ни ридики»).
# [[Файл:Chakma Flag.jpg|22px|border]] [[w:Чакма (язык)|Чакма]]: '''𑄟𑄪𑄭 𑄖𑄧𑄬𑄢 𑄇𑄮𑄌𑄴 𑄛𑄋𑄴''' («муи таре коц паань»).
# [[Файл:Flag of the Northern Mariana Islands.svg|22px|border]] [[w:Чаморро (язык)|Чаморро]]: '''hu guiaya hao''' («ю куайца хуо»).
# [[Файл:Bandera Front Alliberament Cham.svg|22px|border]] [[w:Чамский язык|Чамский]]: '''ai ranam dai''' («аи ранам даи») — женщине; '''dai ranam ai''' («даи ранам аи») — мужчине.<p>[[w:Лазский язык|Чанский]]: см. Лазский<p>[[w:Тсвана (язык)|Чвана]]: см. Тсвана
# [[Файл:Flag of Ashanti.svg|22px|border]] [[w:Чви|Чви]]: '''medɔ wo''' («медэ во»).<p>[[w:Акан|Чви-фанти]]: см. Акан<p>[[w:Ньянджа|Чева]]: см. Ньянджа<p>[[w:Шайенский язык|Чейенский]]: см. Шайенский
# [[Файл:Flag of Altai Republic.svg|22px|border]] [[w:Челканское наречие|Челканский]]: '''мен сени колинтим''' («мен сэни колинтим»).
# [[Файл:Flag of Montenegro.svg|22px|border]] [[w:Черногорский язык|Черногорский]]: '''volim te''' («волим тэ»).
# [[Файл:Flag of the Cherokee Nation.svg|22px|border]] [[w:Чероки (язык)|Чероки]]: '''ᎬᎨᏳᎢ''' («гегею и»).
# [[Файл:Flag of Chechen Republic of Ichkeria.svg|22px|border]] [[w:Чеченский язык|Чеченский]]: '''суна хьо йеза''' («суна хьо йеза») — женщине; '''суна хьо веза''' («суна хьо веза») — мужчине.
# [[Файл:Flag of the Czech Republic.svg|22px|border]] [[w:Чешский язык|Чешский]]: '''miluji tě''' («милуйи те»).
# [[Файл:Flag of the Zhuang people.svg|22px|border]] [[w:Чжуанский язык|Чжуанский]]: '''gou gyaez mwngz''' («гоу гьяэз мвунгз»).
# [[Файл:Flag of Lordship of Butung (Buton).svg|22px|border]] [[w:Чиа-чиа|Чиа-чиа]]: '''indau pe`elu iso`o''' («индау пе элу исо о»).<p>[[w:Венда (язык)|Чивенда]]: см. Венда
# [[Файл:Flag of Oklahoma.svg|22px|border]] [[w:Чикасавский язык|Чикасавский]]: '''chĩholloli''' («чинхоллоли»).<p>[[w:Чикасавский язык|Чикасо]]: см. Чикасавский<p>[[w:Луба (язык)|Чилуба]]: см. Луба
# [[Файл:Bandera de Mizoram.svg|22px|border]] [[w:Чинский язык|Чинский]]: '''kan dawt tuk''' («кан доут тук»).
# [[Файл:Flag of Oregon.svg|22px|border]] [[w:Чинукский жаргон|Чинукский]]: '''nayka tiki mayka ''' («найка тики майка»).<p>[[w:Ньянджа|Чиньянджа]]: см. Ньянджа
# [[Файл:Flag of Saskatchewan.svg|22px|border]] [[w:Чипевайан (язык)|Чипевайан]]: '''neghąnighitą''' («неганигтха»).
# [[Файл:Flag of Bangladesh.svg|22px|border]] [[w:en:Chittagonian language|Читтагонгский]]: '''আঁই তুয়ানরে বেশি গোম লাগে''' («аами туйяанарээ бееси гоома лаагеэ»).<p>[[w:Ньянджа|Чичева]]: см. Ньянджа
# [[Файл:Flag of Angola.svg|22px|border]] [[w:Чокве (язык)|Чокве]]: '''yami nakukuzanga''' («ями накукузанга»).
# [[Файл:Choctaw flag.svg|22px|border]] [[w:Чоктавский язык|Чоктавский]]: '''chi hollo li''' («чи холло ли»).<p>[[w:Чоктавский язык|Чокто]]: см. Чоктавский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Чорти (язык)|Чорти]]: '''ink’anye’t''' («инк анье т»).<p>[[w:Тсвана (язык)|Чуана]]: см. Тсвана
# [[Файл:Flag of Chuvashia 1918.jpg|22px|border]] [[w:Чувашский язык|Чувашский]]: '''эпĕ сана юрататăп''' («эбэ сана юрададэп»).
# [[Файл:Flag of Chukotka.svg|22px|border]] [[w:Чукотский язык|Чукотский]]: '''гымнан гыт ыʼԓгу тыԓгыркынигыт''' («гымнан гыт ыьлгу тыьлгыркынигыт»).<p>[[w:Трукский язык|Чуукский]]: см. Трукский<p>[[w:Чухский язык|Чух]]: см. Чухский
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Чухский язык|Чухский]]: '''tzach wochk’olej''' («цач уочк олех»).
# [[Файл:..Chhattisgarh Flag(INDIA).png|22px|border]] [[w:Чхаттисгархи|Чхаттисгархи]]: '''main tor se maya karatho''' («маин тор се майя каратхо»).
# [[Файл:Flag of Northern Cheyenne.svg|22px|border]] [[w:Шайенский язык|Шайенский]]: '''nemehotatse''' («немехотист»).<p>[[w:Тсонга (язык)|Шангаан]]: см. Тсонга
# [[Файл:Flag of the Shan State.svg|22px|border]] [[w:Шанский язык|Шанский]]: '''ႁိဝ်းႁၵ်ႉသူ''' («хау ха су»).
# [[Файл:Proposed National Flag of Shanghai.svg|22px|border]] [[w:Шанхайский диалект|Шанхайский]]: '''侬爱你''' («нгу э нон»).<p>[[w:Шауйя (язык)|Шауйа]]: см. Шауйя
# [[Файл:Flag of Chaouia.svg|22px|border]] [[w:Шауйя (язык)|Шауйя]]: '''ttexseɣ-cem''' («ттэкссэх кем») - женщине, '''tehibighichek''' («тэхибигичек») - мужчине.
# [[Файл:Flag of Baden-Württemberg (state, lesser arms).svg|22px|border]] [[w:Швабский диалект|Швабский]]: '''i mog di''' («и мог ди»), '''i han di oifach gern''' («и хан ди ойфах гэрн»).
# [[Файл:Flag of Sweden.svg|22px|border]] [[w:Шведский язык|Шведский]]: '''jag älskar dig''' («я эльскар дэй»).
# [[Файл:Civil Ensign of Switzerland.svg|22px|border]] [[w:Швейцарский диалект|Швейцарский немецкий]]: '''ich liib dich''' («ихь лииб дихь»), '''i ha di gärn''' («и ха ди герн»), '''i liäbä di ''' («и льебе ди»).<p>[[w:Романшский язык|Швейцарский ретороманский]]: см. Романшский<p>[[w:Шайенский язык|Шейенский]]: см. Шайенский
# [[Файл:Irish Traveller Movement flag.svg|22px|border]] [[w:Шелта|Шелта]]: '''gra a mo gris''' («гра а мо грис»).
# [[Файл:Masmuda Flag.svg|22px|border]] [[w:Шильхские языки|Шильхский]]: '''ar kʷn ttiriɣ''' («ар кун ттиригх»).
# [[Файл:Flag of Azad Kashmir.svg|22px|border]] [[w:Шина (язык)|Шина]]: '''mas tut khush thamus''' («мас тут хуш тамус»).<p>[[w:Чадский диалект арабского языка|Шоа]]: см. Чадский арабский
# [[Файл:Flag of Rhodesia (1968-1979).svg|22px|border]] [[w:Шона (язык)|Шона]]: '''ndinokuda''' («ндинокуда»).
# [[Файл:Флаг Шорцев.svg|22px|border]] [[w:Шорский язык|Шорский]]: '''мен саға кӧленчам''' («мэн саха кёленчам»).
# [[Файл:Flag of Scotland.svg|22px|border]] [[w:Шотландский язык (германский)|Шотландский (германский)]]: '''ah loove ye''' («ах лоов йе»).
# [[Файл:Flag of Scotland.svg|22px|border]] [[w:Шотландский язык (кельтский)|Шотландский (кельтский)]]: '''tha gaol agam ort''' («хах геул ах-кум оршт»), '''tha gràdh agam dhuibh''' («хах грай ах-кум хуий»).<p>[[w:Пикардский язык|Шти]]: см. Пикардский<p>[[w:Чадский диалект арабского языка|Шува]]: см. Чадский арабский
# [[Файл:Pamiri Flag.webp|22px|border]] [[w:Шугнанский язык|Шугнанский]]: '''uzum tu žīwj''' («узум ту живдьж»).
# [[Файл:Ea (Babilonian) - EnKi (Sumerian).jpg|22px|border]] [[w:Шумерский язык|Шумерский]]: '''𒍝𒂊𒆠𒉘𒈬''' («за э(г) ки аг му»).
# [[Файл:Flag of British Columbia.svg|22px|border]] [[w:Шусвап|Шусвап]]: '''xwexwistsín''' («хуохуистсин»).<p>[[w:Карабахский диалект армянского языка|Шушинский]]: см. Карабахский армянский
# [[Файл:Flag of the Ewe people.svg|22px|border]] [[w:Эве (язык)|Эве]]: '''melɔ̃ wò''' («мэлэ во»).
# [[Файл:Flag of Evenks.svg|22px|border]] [[w:Эвенкийский язык|Эвенкийский]]: '''би синэ аявдем''' («би синэ аявдем»).<p>[[w:Эвенкийский язык|Эвенкский]]: см. Эвенкийский
# [[File:Flag of Severo-Evensky rayon (Magadan oblast).png|22px|border]] [[w:Эвенский язык|Эвенский]]: '''би ину аяврым''' («би ину аяврым»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:Эвондо|Эвондо]]: '''ma ding wa''' («ма динг ва»).<p>[[w:Мегрельский язык|Эгерский]]: см. Мегрельский
# [[Файл:Bandera Front Alliberament Cham.svg|22px|border]] [[w:Эде (язык)|Эде]]: '''khăp ih''' («кхап их»).
# [[Файл:Edo State Flag.png|22px|border]] [[w:Эдо (язык)|Эдо]]: '''i rue mwen we''' («и уэ муэн уэ»).
# [[Файл:Elfdalian flag.png|22px|border]] [[w:Эльвдальский диалект|Эльвдальский]]: '''ig tyttjer um dig''' («иг тюттья ум дэй»), '''ig elsker dig''' («иг эльска дэй»).
# [[Файл:Flag of Alsace.svg|22px|border]] [[w:Эльзасский диалект|Эльзасский]]: '''ich hàb lieb fir dich''' («ихь хаб либ фир дихь»).<p>[[w:Синдарин|Эльфийский]]: см. Синдарин
# [[Файл:Flag of Emilia-Romagna (de facto).svg|22px|border]] [[w:Эмилиано-романьольский язык|Эмилиано-романьольский]]: '''a t vói bän''' («а т вои бэн»).
# [[Файл:Emoji u1f3f3.svg|24px]] [[w:Эмодзи|Эмодзи]]: '''👤💖👆''' (один из множества вариантов).<p>[[w:Энецкие языки|Энецкий]]: см. Лесной энецкий, тундровый энецкий<p>[[w:Эрзянский язык|Эрзя-мордовский]]: см. Эрзянский
# [[Файл:Erzya Flag.svg|22px|border]] [[w:Эрзянский язык|Эрзянский]]: '''мон тонь вечктян''' («мон тонь вечктян»).
# [[Файл:Flag of Esperanto.svg|22px|border]] [[w:Эсперанто|Эсперанто]]: '''mi amas vin''' («ми амас вин»).
# [[Файл:Flag of Estonia.svg|22px|border]] [[w:Эстонский язык|Эстонский]]: '''ma armastan sind''' («ма армастан син»).
# [[Файл:Flag of Extremadura, Spain (with coat of arms).svg|22px|border]] [[w:Эстремадурский язык|Эстремадурский]]: '''te quieru''' («тэ куеру»).
# [[Файл:Flag of Cameroon.svg|22px|border]] [[w:en:Eton language|Этон]]: '''me te wa ding''' («мэ тэ ва дин»).
# [[Файл:Etruscan e peculiar form.svg|22px|border]] [[w:Этрусский язык|Этрусский]]: '''𐌕𐌄 𐌄𐌕𐌀''' («тэ эта»).
# [[Файл:Cross River State Flag.svg|22px|border]] [[w:en:Efik language|Эфик]]: '''mme ama fi''' («мме ама фи»).<p>[[w:Геэз|Эфиопский]]: см. Геэз<p>[[w:Эякский язык|Эяк]]: см. Эякский
# [[Файл:Flag of Alaska.svg|22px|border]] [[w:Эякский язык|Эякский]]: '''ilah qe`xleh''' («илах ке'шлех»).
# [[Файл:Flag of Dinka people.svg|22px|border]] [[w:Динка (язык)|Юго-западный динка]]: '''yïn nhiaar''' («йиин ниаар»).<p>[[w:Сербохорватский язык|Югославский]]: см. Сербохорватский
# [[Файл:Flag of South Azerbaijan.svg|22px|border]] [[w:Азербайджанский язык|Южноазербайджанский]]: '''من سنی سویرم''' («мэн сэны сэвырым»).<p>[[w:Алтайский язык|Южноалтайский]]: см. Алтайский
# [[Файл:Flag of Madagascar.svg|22px|border]] [[w:Малагасийский язык|Южнобецимисаракский малагасийский]]: '''tiako iha''' («тьяко иха»).<p>[[w:Чиа-чиа|Южно-бутонский]]: см. Чиа-чиа<p>[[w:Чиа-чиа|Южно-бутунгский]]: см. Чиа-чиа
# [[Файл:Flag of South Yemen.svg|22px|border]] [[w:Южнойеменский диалект арабского языка|Южнойеменский арабский]]: '''احبك''' («ахибук»).<p>[[w:Пехлевани|Южнокурдский]]: см. Пехлевани
# [[Файл:Flag of Israel.svg|22px|border]] [[w:en:South Levantine Arabic|Южнолевантийский арабский]]: '''بحبك''' («бихаббики»).<p>[[w:Хокло (язык)|Южноминьский]]: см. Хокло<p>[[w:Лимбургский язык|Южнонижнефранкский]]: см. Лимбургский
# [[Файл:Sami flag.svg|22px|border]] [[w:Южносаамский язык|Южносаамский]]: '''manne datnem eahtsam''' («маннэ датнэм эхцам»).
# [[Файл:Flag of Selkup people.svg|22px|border]] [[w:Селькупский язык|Южноселькупский (нарымский)]]: '''мат ташэнд надрам''' («мат та́шэнд на́драм»).
# [[Файл:Flag of Selkup people.svg|22px|border]] [[w:Селькупский язык|Южноселькупский (чулымский)]]: '''ман ста суоранг''' («ман ста суоранг»).
# [[Файл:Flag of Sakha.svg|22px|border]] [[w:Южноюкагирский язык|Южноюкагирский]]: '''мэт тэтэк амудьиимэҥ''' («мэт тэтэк амудьиимэн»).
# [[Файл:Flag of Sierra Leone.svg|22px|border]] [[w:en:Kissi language|Южный кисси]]: '''i kaala num''' («и каала нум»).
# [[Файл:Flag of Angola.svg|22px|border]] [[w:Южный мбунду|Южный мбунду]]: '''ndu ku sole''' («нду ку соле»).<p>[[w:Мамаинде|Южный намбиквара]]: см. Мамаинде
# [[Файл:Flag of KwaNdbele.svg|22px|border]] [[w:Южный ндебеле|Южный ндебеле]]: '''ngiyakuthanda''' («нгийякутханда»).
# [[Файл:Af pakht3.svg|22px|border]] [[w:en:Southern Pashto|Южный пушту]]: '''زه له تا سره مينه لرم''' («за сатá сарá миналарáм»).<p>[[w:Сесото|Южный сото]]: см. Сесото
# [[Файл:Zamboanga Sibugay Flag.png|22px|border]] [[w:Субанон (язык)|Южный субанон]]: '''dlelamen hu yaa''' («длеламен ху яа»).
# [[Файл:Flag of New Mexico.svg|22px|border]] [[w:Южный тива|Южный тива]]: '''еee-peinoom''' («эээ-пеиноом»).
# [[Файл:Flag of the Mayan People.svg|22px|border]] [[w:Юкатекский язык|Юкатекский]]: '''in yaabilmech''' («ин яабилмэч»).
# [[Файл:Flag of China.svg|22px|border]] [[w:Ю-мьен|Ю-мьен]]: '''yie hnamv meih''' («йиа хнум май»).
# [[Файл:Flag of California.svg|22px|border]] [[w:Юрок (язык)|Юрок]]: '''pyerwerkseechek'''' («пьервэрксичек»).<p>[[w:Туника (язык)|Юрон]]: см. Туника<p>[[w:Юэ (язык)|Ютский]]: см. Юэ
# [[Файл:Flag of China.svg|22px|border]] [[w:Юэ (язык)|Юэ]]: '''我愛你''' («уо ай ни»).<p>[[w:Юэ (язык)|Юэский]]: см. Юэ
# [[Файл:Flag of Yogyakarta.svg|22px|border]] [[w:Яванский язык|Яванский]]: '''ꦏꦸꦭꦠꦽꦱ꧀ꦤꦥꦚ꧀ꦗꦼꦤꦼꦁꦔꦤ꧀''' («кула трэсна пандженган»), '''ꦲꦏꦸꦱꦼꦤꦼꦁꦏꦺꦴꦮꦺ''' («аку сэнэнг коуэ»).
# [[Файл:Flag of the Pascua Yaqui Tribe of Arizona.svg|22px|border]] [[w:Яки (язык)|Яки]]: '''inepo enchi ne waa’ta''' («инепо энчи не уаа та»).
# [[Файл:Flag of Sakha.svg|22px|border]] [[w:Якутский язык|Якутский]]: '''мин эйиигин таптыыбын''' («мин эйиигин таптыбын»).<p>[[w:Ямайский креольский язык|Ямайский креол]]: см. Ямайский креольский
# [[Файл:Flag of Jamaica.svg|22px|border]] [[w:Ямайский креольский язык|Ямайский креольский]]: '''mi love yuh''' («ми лав юх»).<p>[[w:Ямайский креольский язык|Ямайский патуа]]: см. Ямайский креольский
# [[Файл:Flag of Magallanes y la Antártica Chilena, Chile.svg|22px|border]] [[w:Ямана|Ямана]]: '''hai kur ske''' («хэй кур шькэ»).
# [[Файл:Flag of Gibraltar.svg|22px|border]] [[w:Янито|Янито]]: '''te quiero''' («тэ керо»).
# [[Файл:Bandeira do Amazonas.svg|22px|border]] [[w:Яномами (язык)|Яномами]]: '''ya pihi irakema''' («я пихи иракема»).
# [[Файл:Flag of Malawi.svg|22px|border]] [[w:en:Yao language|Яо]]: '''ngusamnonyela''' («нгусамноньела»).
# [[Файл:Flag of Japan.svg|22px|border]] [[w:Японский язык|Японский]]: '''愛してる''' («айшитэру»).<p>[[w:Япский язык|Яп]]: см. Япский
# [[Файл:Flag of Yap.svg|22px|border]] [[w:Япский язык|Япский]]: '''gab t’uf rog''' («габ туф рог»).
# [[Файл:Sudovian Flag.png|22px|border]] [[w:Ятвяжский язык|Ятвяжский]]: '''aʃ tawi miłdu''' («аш тави милду»).<p>[[w:Эвондо|Яунде]]: см. Эвондо
<!--
# [[w:|язык]]:
-->
[[Категория:Языки]]
[[Категория:Учебники без шаблона]]
n8hmxb9h4aoolts4gr5ghq8yprzysb7
АОН/ТО
0
22011
261826
231226
2025-07-03T16:58:05Z
Leksey
3027
Добавление
261826
wikitext
text/x-wiki
{{Внимание|Данная статья требует актуализации в связи с [[АОН/Регуляторная гильотина|реформой регулирования 2021 года]]}}
ТО (Техническое обслуживание) в части воздушных судов имеет определённую специфику, присущую только авиации.
В части регулирования конкретно АОН, техническое обслуживание это один из самых запутанных вопросов. Который усугубляется произошедшей в 2021 году [[АОН/Регуляторная гильотина|реформой регулирования]]. Которая достаточно сильно изменила нормативную базу. И даже не в части правил обслуживания непосредственно, а в части правил получения [[АОН/СЛГ]], для чего воздушное судно должно быть надлежащим образом обслужено.
== Техническое обслуживание аэростатов и планеров ==
Для аэростатов не предусмотрено соответствующих отметок в [[АОН/ФАП-147|ФАП-147]], где описывается свидетельство специалиста по техническому обслуживанию воздушных судов. И пилоты свободного аэростата выполняет ТО самостоятельно согласно "Обладатель свидетельства может осуществлять техническое обслуживание воздушного судна, на котором он выполняет полеты.".
В свидетельстве специалиста по ТО есть упоминания только о дирижаблях (определение. "Летательный аппарат легче воздуха, приводимый в движение силовой установкой.") - "с квалификационной отметкой "A5" или "B1.5" может выполнять функции по техническому обслуживанию дирижаблей;"
==Как было до 2021 года==
Вынести в отдельную статью-архив.
Техническое обслуживание осуществлялось посредством двух подходов и зависело от того, является ли воздушное судно ЕЭВС или ЭВС (типовое).
=== ЕЭВС ===
Для самостоятельного обслуживания своего ВС (собственником которого вы являетесь или выполняете полеты) вы можете пройти обучение в [[АОН/АУЦ]] на авиационного [[АОН/Техник|техника]] и получиться свидетельство. Это актуально в случае ЕЭВС. В таком свидетельстве будет внесена единственная квалификационная отметка B1.6
При наличии технического образования, получить такое свидетельство проще, можно пройти обучение по программе переучивания инженерно-технического персонала к обслуживанию легких ВС.
Обучение производится в АУЦ, которых несколько штук в России. Список их есть на странице [[АОН/АУЦ]]. Долгое время и, возможно и на текущий момент, эти занимались всего в двух АУЦ - АУЦ Авиатор и АУЦ Рустехник.
=== Типовое ВС ===
{{Основная статья|АОН/Типовое ВС}}
{{Основная статья|АОН/Сертификат типа}}
В случае типового воздушного судна, его обслуживание должно проводиться в организации, сертифицированной в соответствии с ФАП № 285. В приложении к сертификату у которых имеется тип вашего воздушного судна. Перечень таких организаций ведется на сайте Росавиации. Там они приведены без разделения на "большие" и "маленькие", поэтому, АТ имеющие в приложении воздушные суда АОН явно тут перечислены (внимание, может быть уже неактуально): [[АОН/АТБ|Список АТБ]]
Производитель воздушного судна (иностранный, обычно) требования ФАП автоматически не соответствует (ФАП №285), соответственно не может обслуживать свое же воздушное судно (если не проведет сертификацию согласно ФАП№285). И если при этом, среди российских АТБ нет такого, что может обслуживать ваше ВС, возникает формально ситуация, что никто не может его обслужить.
== Подготовка техников ==
{{Основная статья|АОН/Техник}}
== Ссылки ==
* [https://www.favt.ru/dejatelnost-podderzhanie-letnoj-godnosti-perechen-orgaizaciy-po-teh-obslujivaniyu/ Перечень сертифицированных АТБ] на сайте Росавиации
== См. также ==
*[[АОН/СЛГ]]
*[[АОН/Директива летной годности]]
*[[АОН/Техник]]
*[[АОН/АТБ|АТБ]]
== Нормативная база ==
*[http://ivo.garant.ru/#/document/74843875 ФАП-273] сертификация не типовых ПГВС
*[http://ivo.garant.ru/#/document/400212467 ФАП-519] сертификация типовых
*[http://ivo.garant.ru/#/document/70407648 АР N 175 (ФАП-175)] административный регламент по сертификации
*[http://ivo.garant.ru/#/document/71227460 ФАП-285] - сертификация АТБ
{{АОН}}
9o7gp20mk6jg1s2tyg0ev9exajmabqv
Викиучебник:GUS2Wiki
4
30040
261833
261701
2025-07-04T06:50:51Z
Alexis Jazz
57039
Updating gadget usage statistics from [[Special:GadgetUsage]] ([[phab:T121049]])
261833
wikitext
text/x-wiki
{{#ifexist:Project:GUS2Wiki/top|{{/top}}|This page provides a historical record of [[Special:GadgetUsage]] through its page history. To get the data in CSV format, see wikitext. To customize this message or add categories, create [[/top]].}}
{|style="width:100%; color:#606000; background-color: #FFFFE0; border:1px solid #EEEE80; padding:2px; margin-bottom:1em" cellpadding=0
|-
|<imagemap>Image:Clock and warning.svg|20px
rect 100 100 100 100 [[##]]
desc none</imagemap>
| Следующие данные '''взяты из кеша''', последний раз он обновлялся в '''2025-07-01T09:54:10Z'''.
|}
{| class="sortable wikitable"
! Гаджет !! data-sort-type="number" | Количество участников !! data-sort-type="number" | Активные участники
|-
|DotsSyntaxHighlighter || 26 || 2
|-
|HotCat || 78 || 2
|-
|ImageAnnotator || 24 || 1
|-
|contribsrange || 66 || 1
|-
|directLinkToCommons || 28 || 1
|-
|edittop || data-sort-value="Infinity" | По умолчанию || data-sort-value="Infinity" | По умолчанию
|-
|exlinks || 97 || 2
|-
|extWikiLinksMarker || 14 || 2
|-
|histcomb || 155 || 2
|-
|markadmins || 49 || 3
|-
|markblocked || 39 || 2
|-
|popups || 36 || 3
|-
|preview || 117 || 2
|-
|referenceTooltips || data-sort-value="Infinity" | По умолчанию || data-sort-value="Infinity" | По умолчанию
|-
|removeAccessKeys || 24 || 1
|-
|urldecoder || 29 || 1
|}
* [[Служебная:Использование гаджетов]]
* [[m:Meta:GUS2Wiki/Script|GUS2Wiki]]
<!-- data in CSV format:
DotsSyntaxHighlighter,26,2
HotCat,78,2
ImageAnnotator,24,1
contribsrange,66,1
directLinkToCommons,28,1
edittop,default,default
exlinks,97,2
extWikiLinksMarker,14,2
histcomb,155,2
markadmins,49,3
markblocked,39,2
popups,36,3
preview,117,2
referenceTooltips,default,default
removeAccessKeys,24,1
urldecoder,29,1
-->
mux6ex6t27mgt0wrb356esyebmn19xg
Некоторые сведения о Perl 5/Ввод и вывод
0
30402
261814
258481
2025-07-03T13:35:33Z
ScriptMaster
15708
261814
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент<ref>Поддержка такого синтаксиса появилась не сразу, поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
k88155z8vol4dkqqgium4solyrlstvj
261815
261814
2025-07-03T13:38:03Z
ScriptMaster
15708
/* Файловые дескрипторы */
261815
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу, поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
ceiaspatfm7y851bukvsaijtiec0sz6
261816
261815
2025-07-03T13:49:14Z
ScriptMaster
15708
/* Файловые дескрипторы */
261816
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
2dcspqf2w31h4lvcwy6756o0tk98t76
261817
261816
2025-07-03T14:55:48Z
ScriptMaster
15708
/* Чтение/запись файлов */
261817
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, она размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
9iakzeb9si78s761c1qzchqra07ar65
261818
261817
2025-07-03T14:57:01Z
ScriptMaster
15708
/* Тонкости буферизации */
261818
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
bozqmlzt5fsoijcrz08499e8rwb6pbm
261820
261818
2025-07-03T15:47:12Z
ScriptMaster
15708
/* Не буферизированное чтение/запись */
261820
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе заступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
j8s2gywhejfx8hror86hytfg4b5lezw
261821
261820
2025-07-03T16:08:01Z
ScriptMaster
15708
/* Оформление конца строки в разных системах */
261821
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе заступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
jux0jckudcq84bah03qxgsyw7x7v86v
261822
261821
2025-07-03T16:09:41Z
ScriptMaster
15708
/* Обнаружение конца чтения */
261822
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
10qwt0f1yymaf8y8em9dwaaj9yyab19
261823
261822
2025-07-03T16:15:46Z
ScriptMaster
15708
/* Операции с файлами */
261823
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
bpti2bmqaw52rdws3by8gsexgi1vjep
261824
261823
2025-07-03T16:38:51Z
ScriptMaster
15708
/* Текстовый и двоичный подходы чтения */
261824
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторого продолжительного действия с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
0fae8zoz93e4yu02w462b8zdronqpt4
261827
261824
2025-07-03T18:14:46Z
ScriptMaster
15708
/* Тонкости буферизации */
261827
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторых продолжительных действий с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLER); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLER каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
oypd85g8gkxqwceh2szw8lez60a9tnh
261828
261827
2025-07-03T18:18:17Z
ScriptMaster
15708
/* Тонкости буферизации */
261828
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторых продолжительных действий с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLE); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLE каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующий функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
n8t55444mup0taljur77x9s230lxjje
261829
261828
2025-07-03T18:25:59Z
ScriptMaster
15708
/* Не буферизированное чтение/запись */
261829
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторых продолжительных действий с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLE); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLE каждая запись по нему будет сбрасывать данные в
# буфер операционной системы.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующие функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
ah15f2ywhknrn0yuxemlf9tj4bhr73e
261830
261829
2025-07-04T03:29:58Z
ScriptMaster
15708
/* Тонкости буферизации */
261830
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторых продолжительных действий с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLE); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLE каждая запись в STDIO по этому дескриптору будет сбрасывать данные в
# буфер операционной системы мгновенно. Другими словами, буферизация STDIO отключается.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующие функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
m1m8vp7yjudbejmhtginub6e9msmu49
261831
261830
2025-07-04T03:54:56Z
ScriptMaster
15708
/* Текстовый и двоичный подходы чтения */
261831
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторых продолжительных действий с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLE); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLE каждая запись в STDIO по этому дескриптору будет сбрасывать данные в
# буфер операционной системы мгновенно. Другими словами, буферизация STDIO отключается.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующие функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Функция <code>open()</code> позволяет указать на необходимость двоичного ввода (и даже кодировку) в момент открытия дескриптора. Для этого нужно указать флаг рядом с режимом открытия:
<source lang="perl">
open (FH, '<:raw', $filename); # открыть в двоичном режиме чтения
open (FH, '>:raw', $filename); # открыть в двоичном режиме записи
open (FH, '<:encoding(UTF-8)', $filename); # интерпретировать входящую кодировку как UTF-8
</source>
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
cuav5vtfx3ewohyt1dwy3jysty1w6x7
261832
261831
2025-07-04T03:55:22Z
ScriptMaster
15708
/* Текстовый и двоичный подходы чтения */
261832
wikitext
text/x-wiki
{{Навигация учебника}}
Любая программа работает с некоторыми данными: обычно программа получает на вход данные (из файлов, от пользователя, по сети), анализирует и преобразует их и выводит на некоторое устройство вывода или в файл.
Язык Perl предоставляет весь необходимый функционал для чтения потоков данных и записи их в файлы. В этой главе мы рассмотрим как реализуется ввод/вывод в Perl.
== Операции ввода ==
Данные в Perl программу можно вводить следующими стандартными инструментами:
* через стандартный поток ввода <code>STDIN</code> с помощью операции <code><></code>;
* через файлы системы, открывая их и получая дескрипторы;
* через операцию <code>qx{}</code> или наклонные кавычки <code>``</code>.
В этом разделе мы рассмотрим различные подходы к вводу, кроме файлов (которые будут рассмотрены позже). Начнем с операции <code>qx{}</code>.
<code>qx{}</code> (или более короткая ее форма <code>``</code>) позволяет исполнить строковые литералы как команды для командной оболочки операционной системы. После того как команды выполнятся, весь вывод можно присвоить переменной, причем команда различает скалярный и списковый контексты. В скалярном контексте весь вывод сохраняется одной строкой. В списковом контексте каждая строка вывода будет записываться отдельно.
<source lang="perl">
# Скалярный контекст
$date = `date`;
$out = qx {
ps
};
# Списковый контекст
@out = qx {
ps
};
print $date;
print $out;
foreach (@out) {
print "line: " . $_;
}
</source>
<source lang="perl">
9 янв 2023 г. 11:12:33
PID PPID PGID WINPID TTY UID STIME COMMAND
1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
2476 2475 2475 11076 cons0 1052188 10:58:03 /usr/bin/ps
line: PID PPID PGID WINPID TTY UID STIME COMMAND
line: 2477 2475 2475 19128 cons0 1052188 10:58:03 /usr/bin/ps
line: 1495 1 1495 17984 cons0 1052188 Jan 5 /usr/bin/bash
line: 2475 1495 2475 16208 cons0 1052188 10:58:03 /usr/bin/perl
</source>
В списковом контексте строки будут разбиваться по символу разделителя, записанному во встроенной переменной <code>$/</code>, в которой по умолчанию записан символ новой строки <code>\n</code>. Управляя этим значением, можно управлять формированием вывода. Разделителем не обязательно может быть один символ: им может быть целая строка.
Выражение <code>qx{}</code> вычисляется каждый раз когда встречается интерпретатором perl. Код возврата исполняемой команды будет записан в переменной <code>$?</code> (подобно командной оболочке Bourne Shell).
Второй операцией ввода является <code><></code> (''угловые скобки''). Обычно она используется, чтобы читать по дескриптору (когда он указан между скобками), но когда его нет, весь ввод осуществляется через дескриптор <code>STDIN</code>. Дескриптор <code>STDIN</code> может быть связан с различными устройствами ввода (все зависит от того, как была вызвана Perl программа), но обычно он связывается с клавиатурой, поэтому попытка чтения по этому дескриптору обычно всегда приостанавливает исполнение в ожидании, когда пользователь что-нибудь введет и нажмет {{Клавиша|Enter}}.
Чтобы прочитать что-нибудь из дескриптора, достаточно присвоить результат операции переменной. Операция <code><></code> понимает скалярный и списковый контексты.
<source lang="perl">
# Файл: main.pl
# Простейшее чтение из STDIN. В данном примере программа
# работает как echo, печатая на устройстве вывода все, что
# ввел пользователь.
while ($line = <>) {
print $line;
}
</source>
Операция <code><></code> читает до тех пор, пока не получит символ перевода строки или символ конца потока <code>EOF</code>. В программе выше мы зациклили ввод за счет того, что операция ввода будет возвращать ИСТИНУ до тех пор, пока не получит <code>EOF</code>. Чтобы послать этот символ с клавиатуры, нужно нажать сочетание {{Клавиша|Ctrl}} + {{Клавиша|d}}.
Если вызвать эту же программу следующим образом
<source lang="bash">
$ perl main.pl <<END
hello
world
END
# то получим
hello
world
</source>
В данном случае командная оболочка связывает <code>STDIN</code> не с клавиатурой, а с некоторым файлом (предоставляемым оболочкой), в который она записывает две строки. Эти строки мы и прочитали в программе.
Операция <code><></code> по умолчанию пишет в переменную <code>$_</code>. Пользуясь этим, программу можно написать еще проще.
<source lang="perl">
while(<>) { # Пишет в $_
print; # Печатает $_
}
</source>
=== Массив аргументов <code>@ARGV</code> ===
Любой программе Perl командная оболочка может передать аргументы. Все аргументы командной оболочки записываются в предопределенный массив <code>@ARGV</code> так, как они были записаны в оболочке.
Обычно вам не следует парсить входящие аргументы самостоятельно: лучше всего использовать встроенный модуль <code>Getopt::Long</code> (что такое модули мы подробно разберем в отдельной главе). Данный модуль позволит вам легко парсить длинные и короткие опции командной оболочки.
Для примера мы просто покажем, как выглядит массив <code>@ARGV</code>.
<source lang="perl">
#!/usr/bin/perl
# Файл: main.pl
foreach (@ARGV) {
print $_ . "\n";
}
</source>
Вызовем программу например так:
<source lang="bash">
$ ./main.pl --help -s 34 param1 param2
--help
-s
34
param1
param2
</source>
=== Переменные окружения ===
Perl, как и любой другой процесс, может принимать из среды исполнения переменные окружения, которые автоматически складываются в хеш <code>%ENV</code>. Этот хеш можно считать ещё одним каналом для приема данных в программу Perl.
Следующая команда демонстрирует печать переданного в программу ключа при вызове интерпретатора из консоли:
<source lang="bash">
$ MYVAR=test perl -e 'print "$_ $ENV{$_}\n" for (keys %ENV);' | grep "MYVAR"
</source>
=== Лексемы __END__ и __DATA__ ===
В язык Perl включены две специальные лексемы, которые позволяют хранить данные прямо в исходном файле программы.
Лексема <code>__END__</code> определяет конец исходного кода программы и на этом слове интерпретация принудительно завершается. Разработчики Perl обычно размещают после этой лексемы документацию к исходному коду, обычно на языке разметки POD (''Plain Old Documentation''), которая может быть прочитана стандартными средствами чтения документации Perl.
<source lang="perl">
#!/usr/bin/perl
print "Hello, World!";
__END__
Эти строки не будут интерпретированы perl.
</source>
Вторая лексема <code>__DATA__</code> похожа на <code>__END__</code> с той разницей, что данные после нее могут быть прочитаны из программы. Использование этого самое разное. Самое простое это хранение какой-то стандартной конфигурации к модулю; текста для сообщений и другое.
Чтобы обратиться к этим данным, нужно использовать файловый дескриптор <code><имя-пакета>::DATA</code>.
<source lang="perl">
#!/usr/bin/perl
while (my $data = <DATA>) {
print "$data";
}
__DATA__
line 1
line 2
line 3
line 4
</source>
== Операции вывода ==
Для вывода в Perl предусмотрено несколько встроенных функций, многие из которых копируют одноименные из стандартной библиотеки Си. Наиболее ходовыми для печати на терминал или в файл являются следующие:
<source lang="perl">
print [<дескриптор>] <список>
print <дескриптор>
printf [<дескриптор>] <формат>, <список>
# Начиная с версии 5.10
say [<дескриптор>] <список>
say <дескриптор>
</source>
Всем функциям вывода можно указать куда печатать через дескриптор в первом аргументе. Если он не указан, то по умолчанию печать производится в дескриптор <code>STDOUT</code>, который обычно связывается с некоторым терминальным устройством TTY. Именно поэтому вы видите всю печать на экране.
Функции печатают переменные, передаваемые списками. Если они не указаны явно, то используется переменная <code>$_</code>.
Далее, функции разделяются по способу вывода на печатающие по ''формату'' и ''списком''. Среди трех перечисленных функций, форматированный вывод осуществляет только функция <code>printf</code>. Для этого вы должны вместе со списком переменных составить, так называемую, ''форматную строку'' (или ''формат''), которая определяет сколько знакомест на строке должна занимать та или иная переменнная, выводимая на печать. О форматной печати мы поговорим в отдельной главе, так как это довольно обширная тема.
Функции <code>print</code> и <code>say</code> просто печатают переменные, переданные списком, в указанном порядке. Разница между <code>print</code> и <code>say</code> состоит в том, что <code>say</code> сама подставляет перевод строки в конец (что очень удобно), тогда как <code>print</code> выводит строку в точности, как вы ей скажете.
Функция <code>say</code> появилась относительно недавно, поэтому, чтобы ее использовать, необходимо в начале включить возможность ее вызова:
<source lang="perl">
use feature qw{ say }; # Включаем функцию say()
say "Hello, World!"
</source>
Начиная с версии интерпретатора 5.10 можно просто указать версию с помощью <code>use()</code>, и тогда возможность использовать функцию включается неявно:
<source lang="perl">
use v5.10; # С 5.10 и выше функция включается неявно
say "Hello, World!"
</source>
Все приведенные функции принимают на печать списки (т.е. являются списковыми операциями). Это может приводить к ошибочным ситуациям.
<source lang="perl">
# НЕВЕРНО
print (2 + 2) ** 2;
# Напечатает 4, а не 16
</source>
В вышеприведенном примере на печать будет выведено 4, когда ожидалось 16. Это происходит потому, что выражение в скобках является термом. Далее функция <code>print</code> будет рассматривать выражение в скобках, как список своих параметров, а так как конструкция <code>print (2 + 2)</code> является термом, то сначала напечатается сумма; затем результат печати вернет 1, которая будет в конце возведена в квадрат.
Чтобы исправить эту ошибку, в подобных ситуациях нужно всегда указывать дескриптор явно:
<source lang="perl">
# ПРАВИЛЬНО
print STDOUT (2 + 2) ** 2;
# Напечатает 16
</source>
Другими словами, когда список вычисляется прямо в вызове, всегда указывайте дескриптор явно.
В функциях печати вы можете быстро печатать массивы. Простой массив можно распечатать как минимум тремя способами:
<source lang="perl">
@arr = (1,2,3);
print "@arr\n"; # Всегда используйте кавычки, чтобы элементы разделялись пробелами
# Перебором в цикле
foreach (@arr) {
print "$_\n";
}
# Функцией join. Преимущество здесь в том, что можно менять разделитель.
print join(" ",@arr),"\n";
</source>
С хеш-массивами немного сложнее, так как стандартные функции печати не умеют их печатать:
<source lang="perl">
%person = (name => "Larry", surname => "Wall");
while ( ($k,$v) = each %person ) { # Функция each итеративно парами извлекает элементы
print "$k => $v\n";
}
print map { "$_ => $person{$_}\n" } keys %person; # Преобразуем хеш-массив в список и печатаем его
print "@{[ %person ]}\n"; # Строим новый массив из элементов хеш-массива
{
my @temp = %person;
print "@temp\n";
}
</source>
== Ввод и вывод в файлы ==
Простые файлы позволяют хранить данные программы и результаты работы на диске компьютера. Кроме того, в *nix системах существуют еще специальные файлы, работая с которыми в пространстве пользователя, мы можем взаимодействовать с другими процессами (через pipe-файлы), отправлять сообщения подключенным устройствам (взаимодействуя с файлами устройств), контактировать с системными сервисами и другое.
Во всех этих случаях мы должны открыть файл в некотором режиме и связать его с ''дескриптором''. ''Дескриптор'' — это специальный программный объект, который предоставляет интерфейс ввода/вывода системы, через который мы пишем/читаем файл. Одна программа может открыть за раз много дескрипторов (все зависит от ограничений, накладываемых системой на процесс), причем один дескриптор в один момент может быть связан только с одним файлом. Тем не менее, допускается один и тот же дескриптор перепривязывать на другие файлы.
В этой главе мы рассмотрим основные вопросы записи и чтения в файлы. Эти вопросы являются базовыми для любых типов файлов.
=== Файловые дескрипторы ===
С точки зрения Perl, дескриптор — это символическое имя в программе, представляющее простой файл, файл устройства, сокета или программного канала. В программе Perl дескриптор файла чаще всего открывается функцией <code>open()</code>, которой передается имя дескриптора, режим доступа и путь к файлу в файловой системе. Например
<source lang="perl">
open(FILE, "> /tmp/file.data");
</source>
создает дескриптор <code>FILE</code> и присоединяет его к файлу <code>/tmp/file.data</code>. В данном случае файл открывается только на запись, о чем говорит символ <code>></code>. Символ режима открытия файла не обязательно записывать во втором аргументе, как показано в примере<ref>Поддержка такого синтаксиса появилась не сразу (с версии 5.6), поэтому в очень старых модулях вы будете встречать исключительно вариант, в котором режим открытия дескриптора записан вместе с путевым именем файла. Однако, вариант с тремя аргументами позволяет вам открывать файлы с необычными именами, начинающимися на специальные символы.</ref>: вообще говоря вы передаете функции список, а дальше он будет проверен на наличие нужных частей:
<source lang="perl">
# Символ режима можно вынести в отдельный аргумент.
open(FILE, '<', "test.txt");
# или так
open FILE, '<', "test.txt"; # Потому что это именованная списковая операция.
</source>
Дескриптор должен представлять правильный идентификатор и не должен совпадать с зарезервированными словами Perl. Имя файла <code>"-"</code> является специальным. При открытии дескриптора в режиме <code><</code> с передачей этого пути служит указанием открыть стандартное устройство ввода, а в режиме <code>></code> — стандартное устройство вывода.
Созданный дескриптор попадает в таблицу символов пространства имен.
Строго говоря, дескриптор не является переменной, поэтому ему не нужен идентификатор типа. По этой причине дескриптор нельзя напрямую использовать в операциях (которые требуют знания контекста). При передаче дескриптора куда-либо используется символ звездочка (например, <code>*FILE</code>), которая дает ссылку на дескриптор. Получив ссылку, ее можно присвоить переменной, которую можно использовать в операциях:
<source lang="perl">
$ref_file = *FILE;
print $ref_file "Hello\n"; # Печатаем в файл
</source>
Дескрипторы принято записывать прописными буквами, хотя язык не требует этого. Такая практика сложилась исторически, так как это выделяет их среди переменных.
Любая программа Perl неявно открывает три дескриптора:
* <code>STDIN</code> — связывается со стандартным устройством ввода;
* <code>STDOUT</code> — связывается со стандартным устройством вывода;
* <code>STDERR</code> — связывается со стандартным устройством вывода (как правило вывод через него не буферизуется).
По умолчанию <code>STDIN</code> пользуется операция <code><></code>, простые функции вывода используют <code>STDOUT</code>, а <code>STDERR</code>, например, пользуется функция <code>warn()</code>.
Стандартные дескрипторы допускается переоткрывать, но сделать это можно только один раз. При этом восстановить предыдущее состояние через <code>open()</code> будет невозможно. Чтобы восстанавливать дескрипторы, их можно дублировать, о чем будет рассказано ниже.
=== Открытие/закрытие дескриптора ===
Дескриптор файла можно открыть одним из следующих вызовов:
<source lang="perl">
open <идентификатор-дескриптора>, <имя-файла>;
open <идентификатор-дескриптора>;
</source>
Идентификатор дескриптора может вычисляться в выражении. По умолчанию файл открывается на чтение, если не указан конкретный режим. Если имя файла задано без префикса пути, то Perl будет пытаться искать файл в рабочем каталоге. Имя может зависеть от операционной системы: так, в Windows нужно указывать метку логического диска для абсолютных путей, однако слеши не обязательно менять на обратные.
Вместо пути можно указать знак тире <code>-</code>, тогда открываемый файл соответствует <code>STDIN</code>, т.е. <code>>-</code> соответствует записи в <code>STDIN</code>. Если стандартный дескриптор до это уже был перенаправлен в некоторый файл, то такой вызов будет ассоциироваться с ним.
Если не указан путь к файлу, то предполагается путь в переменной <code>$<идентификатор-дескриптора></code>, причем ее имя становится идентификатором дескриптора:
<source lang="perl">
$FILE = "file.dat";
open FILE; # Предполагается, что путь есть в переменной $FILE.
</source>
Любой файл может быть открыт в одном из режимов:
* <code><</code> (чтение);
* <code>></code> (запись);
* <code>>></code> (дозапись).
При открытии файла на запись, предыдущие данные будут удалены. При дозаписи, указатель будет находиться в конце файла, а значит записываемые данные будут попадать в конец файла. Чтобы открыть файл в требуемом режиме, процесс, в котором запустилась Perl-программа, должен иметь соответствующие на это права, иначе файл не удастся открыть. Для режима чтения файл всегда должен существовать. Для режимов записи файл может не существовать: в этом случае он будет создан.
Чтобы открыть файл одновременно на чтение и на запись, перед знаком режима нужно поставить плюс <code>+</code>, т.е. <code>+></code>, <code>+>></code>, <code>+<</code>.
Обычно функционала <code>open()</code> достаточно для большинства ситуаций, тем не менее Perl имеет более сложную функцию открытия, которая позволяет учесть разные ситуации.
<source lang="perl">
sysopen <идентификатор-дескриптора>, <имя-файла>, <флаги> [, <umask>];
</source>
Эта функция похожа на <code>open()</code>, но она позволяет гибко выставить режимы с помощью флагов, определенных в модуле <code>Fcntl</code>. Набор флагов может зависеть от операционной системы, но нижеперечисленные поддерживаются во всех:
* <code>O_RDONLY</code> — только чтение;
* <code>O_WRONLY</code> — только запись;
* <code>O_RDWR</code> — чтение и запись;
* <code>O_CREAT</code> — создать файл, если не существует;
* <code>O_EXCL</code> — завершить вызов с ошибкой, если файл существует;
* <code>O_APPEND</code> — режим дописывания;
* <code>O_TRUNC</code> — очистить содержимое файла перед записью.
Читателю, программировавшему для *nix, данные флаги могут быть знакомы. Дело в том, что Perl не придумывает велосипед и практически в неизменном виде копирует стандартную библиотеку ввода/вывода Си и *nix. Эти флаги представлены двоичными числами, которые можно объединять поразрядным И (<code>|</code>).
Последний не обязательный аргумент передает umask-маску, задающую флаги доступа для новых файлов. Данная маска задается восьмеричным числом. Если она не указана, то используется число <code>0666</code>.
Функции <code>open()</code> и <code>sysopen()</code> возвращают 0, если удалось открыть дескриптор, и неопределенное значение <code>undef</code>, в противном случае.
Чтобы закрыть дескриптор, следует вызвать функцию
<source lang="perl">
close [<дескриптор>];
</source>
Функция возвращает ИСТИНУ, если дескриптор удалось закрыть и выполнить всю системную работу, связанную с ним. Если опустить аргумент, то функция закроет дескриптор, на который последний раз указывала функция <code>select()</code>.
Следует отметить, что закрывать дескрипторы явно иногда не обязательно. Если вы пытаетесь открыть новый файл по дескриптору, который уже используется, то перед открытием нового файла <code>close()</code> для старого будет вызвана неявно. Также при завершении Perl-программы все открытые дескрипторы закрываются автоматически. С другой стороны, явное закрытие дескрипторов позволяют выявлять очень редкие системные ошибки, например, когда буферы не сбрасываются на диск, потому что на нем закончилось место. Явное закрытие очищает переменную <code>$.</code>, которая хранит последнюю прочитанную запись, тогда как неявное закрытие ее не очищает.
Если функция <code>close()</code> завершается с ошибкой, то ее причина записывается в переменную <code>$!</code>.
Примеры
<source lang="perl">
use Fcntl; # для sysopen
# Примеры с функцией open
open FILE, "< example.txt" or warn "Warning: $!"; # на чтение. Файл должен существовать.
open FILE, "> example.txt" or warn "Warning: $!"; # на запись. Файл будет создан, если его нет.
open FILE, ">> example.txt" or warn "Warning: $!"; # на дозапись. Файл будет создан, если его нет.
open FILE, "+< example.txt" or warn "Warning: $!"; # на чтение и запись. Файл должен существовать.
open FILE, "+> example.txt" or warn "Warning: $!"; # на чтение и запись. Файл очищается.
open FILE, "+>> example.txt" or warn "Warning: $!"; # на чтение и на дозапись.
# Примеры с функцией sysopen
sysopen FILE1, "example1.txt", O_RDONLY, 0666 or warn "Warning: $!"; # на чтение. Файл должен существовать.
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_TRUNC or warn "Warning: $!"; # на запись
sysopen FILE1, "example1.txt", O_WRONLY | O_CREAT | O_APPEND or warn "Warning: $!"; # на дозапись
sysopen FILE1, "example1.txt", O_RDWR or warn "Warning: $!"; # на чтение и запись. Файл должен существовать
sysopen FILE1, "example1.txt", O_RDWR | O_CREAT | O_TRUNC or warn "Warning: $!"; # на чтение и запись. Файл очищается.
# Примеры с close
close FILE or warn "Warning: $!";
close FILE1 or warn "Warning: $!";
</source>
=== Дескриптор-дубликат ===
Файловый дескриптор можно продублировать, т.е. создать независимую копию. Оба дескриптора будут связаны с одним и тем же файлом, иметь общий файловый указатель, но разные буферы ввода/вывода. Закрытие одного дескриптора не будет влиять на работу другого.
Чтобы продублировать дескриптор, в аргументе с именем файла нужно указать его идентификатор с амперсандом <code>&</code>:
<source lang="perl">
# Без пробелов !
open(NEWSTDOUT, ">&STDOUT"); # NEWSTDOUT - дубликат STDOUT
</source>
Дублирование дескрипторов зачастую используется, чтобы сохранить предыдущее состояние. Например так следует поступать со стандартными дескрипторами <code>STDIN</code>, <code>STDOUT</code> и <code>STDERR</code>.
<source lang="perl">
open OLDSTDOUT, ">&STDOUT";
open STDOUT, "> example.txt" or die "$!";
# Временно весь вывод будет направляться в файл
print "Hello, World\n";
# Восстанавливаем дескриптор STDOUT
close STDOUT or warn "$!";
open STDOUT, ">&OLDSTDOUT" or die "$!";
close OLDSTDOUT or warn "$!";
</source>
В остальных ситуациях дублирование в общем то бесполезно.
=== Чтение/запись файлов ===
Чтение и запись файлов очень напоминает то, как это делается в обычной программе на языке Си. Можно выделить два подхода:
* буферизированное чтение/запись. Здесь операции осуществляются высокоуровневыми функциями, использующими буферы ввода/вывода. Использование буферов позволяет уменьшить нагрузку на систему, так как данные не сбрасываются на устройства хранения, пока буфер не заполнится. Кроме того, функции чтения/записи контролируют некоторые нюансы, например что данные действительно записались или прочитались, благодаря чему код становится компактнее;
* не буферизированное чтение/запись позволяет полностью контролировать всю процедуру чтения/записи. Обычно такой подход используется для записи/чтения двоичных файлов.
==== Тонкости буферизации ====
Буферизация существует по причине того, что вывод на некоторые устройства может потребовать некоторых продолжительных действий с их стороны, которые выливаются в реальные задержки времени. Например, запись на устройство хранения с магнитным принципом требует правильного физического позиционирования записывающей головки над нужным сектором; если вывод осуществляется на сетевое устройство, может потребоваться продолжительная процедура со стороны протокола передачи.
В этих случаях, чтобы устройство работало эффективнее, разумно нагрузить устройство сразу большим количеством данных, вместо того, чтобы передавать их малыми порциями. Для этого операционная система вводит буфер между приложением и устройством ввода/вывода, который читает асинхронно. Удобно представлять буфер как непрерывную память с двумя указателями, один из которых указывает позицию, до которой осуществилась запись, а другой — позицию, до которого произошло чтение. Когда приложение делает операцию записи в буфер, оно размещает данные в его свободном пространстве, а затем смещает указатель записи буфера на следующий свободный адрес. Когда ядро операционной системы получает процессорное время, оно увидит что в буфере есть разница между адресами указателей записи и чтения, возьмет данные между адресами указателей, сделает системный вызов для записи их на устройство, а когда оно закончит, сместит указатель чтения на тот же адрес, на котором был указатель записи в момент операции, и так до бесконечности. Этот алгоритм описывает исходящий буфер с точки зрения приложения, когда указатель чтения находится в позиции догоняющего, однако, входящий буфер работает похоже, просто стороны меняются местами.
Обратите внимание, что память реального компьютера конечна, поэтому буферы обычно периодически очищаются, а если им не хватает на текущий момент памяти для записи, то операционная система обычно блокирует приложение до тех пор, пока буфер не станет более свободным.
Интерпретатор perl при реализации буферизованных операций обычно использует ещё один логический буфер, называемый STDIO, который находится выше буфера операционной системы. Таким образом, прежде чем данные запишутся в буфер, выделенный ядром, они сначала запишутся в логический буфер, а когда он заполнится, то только тогда произойдет сброс данных в буфер операционной системы. Perl разрешает вам управлять буфером STDIO с помощью специального скаляра <code>$|</code> (по умолчанию имеет значение ЛОЖЬ), который управляет автоматическим сбросом. Если он установлен в ИСТИНУ, то будет работать режим автоматического сброса для каждой записи для дескриптора, на который указывает последний вызов функции <code>select()</code>. Иногда это может быть необходимо, например когда данные записываются синхронно порциями, много меньшими размера буфера, и без автоматического сброса приложение может просто зависнуть. В этих случаях буферизацию нужно отключать через установку значения скаляра <code>$|</code>. Обычно это делают так
<source lang="perl">
my $prevHandler = select(FILE_HANDLE); # Вернет дескриптор, который был до установки текущего.
$| = 1;
select($prevHandler); # Восстанавливаем предыдущий дескриптор.
# Теперь для FILE_HANDLE каждая запись в STDIO по этому дескриптору будет сбрасывать данные в
# буфер операционной системы мгновенно. Другими словами, буферизация STDIO отключается.
</source>
Использование <code>sysread()</code> и <code>syswrite()</code> дают вам полную свободу и не используют логический буфер STDIO. Далее по тексту мы говорим буферизованное чтение/запись, когда используется логический буфер STDIO.
==== Буферизированное чтение/запись ====
Операция <code><></code> и следующие функции используют буферы ввода/вывода: <code>print</code>, <code>read</code>, <code>seek</code>.
Простейшее чтение файлов реализуется через операцию <code><></code>, в которой между скобками нужно указать дескриптор файла. В скалярном контексте операция возвращает очередную прочитанную строку до разделителя, указанного в переменной <code>$/</code>, со смещением внутреннего файлового указателя на следующий символ после разделителя. Операция возвращает прочитанную строку вместе с разделителем, либо возвращает пустую строку. Пользуясь этим, можно реализовать построчное чтение файла от начала до конца.
Пусть у нас есть файл <code>example.txt</code> со следующим содержимым:
<source lang="">
line 1
line 2
line 3
line 4
line 5
</source>
За пятой строкой нет перевода строки, а идет сразу конец файла.
<source lang="perl">
open FILE, "example.txt" or die "$!";
$line = <FILE>; # Прочитана первая строка (вместе с символом перевода строки)
print "$line"; # line 1\n
$line = <FILE>; # Прочитана вторая строка
print "$line"; # line 2\n
# Внутренний файловый указатель смещен на первый символ третьей строки
# Дочитываем файл
while (<FILE>) {
print;
}
# line 3\n
# line 4\n
# line 5\0
# Вернет пустую строку, так как читать больше нечего: внутренний файловый указатель находиться в конце файла.
$line = <FILE>;
print "Empty: $line";
</source>
Теперь пусть файл имеет такое содержимое:
<source lang="">
a:b:c:d:e:f
</source>
Мы поменяли разделитель на знак двоеточия и хотим прочитать данные с новым разделителем. Если файл относительно небольшого размера, то можно прочитать его сразу в массив:
<source lang="perl">
open FILE, "example.txt" or die "$!";
{ # Используем блок, чтобы поменять $/ в локальной области видимости.
local $/ = ':'; # Меняем разделитель.
@lines = <FILE>; # Читаем файл прямо в массив.
}
foreach (@lines) {
print "$_\n";
}
</source>
Результат
<source lang="">
a:
b:
c:
d:
e:
f
</source>
Обратите внимание, что строки читаются вместе с разделителем на конце. К сожалению разделитель автоматически таким способом чтения удалить невозможно: обычно прибегают у услугам функции <code>split()</code>, например так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
@chunks = split(/:/, <FILE>);
foreach (@chunks) {
print "$_\n";
}
# a
# b
# c
# d
# e
# f
</source>
Также хотим упомянуть, что файл можно прочитать во временный анонимный массив и выводить прочитанное на ходу:
<source lang="perl">
...
foreach (@{[<FILE>]}) {
print "$_\n";
}
</source>
Эта конструкция может показаться на данный момент не понятной, так как конструирование анонимных массивов мы будем обсуждать в отдельной главе. Здесь в анонимный массив, конструируемый операцией <code>[]</code>, помещается содержимое файла, читаемое операцией <code><></code>. Чтобы операции производились в списковом контексте, мы помещаем конструкцию в еще одни скобки <code><nowiki>@{}</nowiki></code>: разыменовывание анонимного массива, в который мы ранее записали содержимое файла.
Если файл имеет очень большой размер, его следует читать порциями. Для этого прибегают к функции
<source lang="perl">
read <дескриптор>, <переменная>, <длина> [, <смещение>];
</source>
Функция <code>read()</code>, читает из файла ровно столько байт, сколько указано в параметре <code><длина></code>, и помещает прочитанное в <code><переменная></code>. В отличие от <code><></code>, эта функция возвращает действительное значение прочитанных байт; 0 — если чтение достигло конца файла, и неопределенное значение в случае ошибки. Параметр <code><смещение></code> указывает, сколько байт нужно отсчитать в <code><переменная></code> и начать вставлять прочитанные данные. Так, отрицательное смещение <code>-n</code> говорит, что в <code><переменная></code> нужно отсчитать последние <code>n</code> байтов и к оставшемуся результату добавить прочитанную строку. Положительное смещение, большее чем длина текущей строки в <code><переменная></code>, будет создавать пробел из символа <code>\0</code>, после которого будет вставляться прочитанная строка. По умолчанию смещение равно нулю, что означает затирание предыдущих данных после чтения.
На практике файлы с помощью <code>read()</code> читаются примерно так:
<source lang="perl">
open FILE, "example.txt" or die "$!";
$buffer_size = 1024;
while (read (FILE, $data, $buffer_size) > 0) {
print "$data";
}
</source>
Иногда приходится читать файл по одному символу. Для этого можно воспользоваться функцией <code>getc()</code>, которая возвращает один символ в текущей позиции внутреннего указателя файла и неопределенное значение в случае конца файла или ошибки.
<source lang="perl">
open FILE, "example.txt" or die "$!";
while ($data = getc(FILE)) {
print "$data"; # Читаем файл символ за символом
}
</source>
Мы уже не один раз упомянули о внутреннем указателе файла, который является подобием виртуальной каретки (как в печатной машинке). Любая операция чтения/записи над файлом приводит к смещению этого указателя на число прочитанных/записанных байт. Этим указателем можно управлять с помощью функции <code>seek()</code> из модуля <code>IO::Seekable</code>:
<source lang="perl">
seek <дескриптор>, <смещение-указателя>, <точка-отсчета>;
# Под точкой отсчета понимается одна из предопределенных констант:
# SEEK_SET (0) — от начала файла.
# SEEK_CUR (1) — с текущей позиции.
# SEEK_END (2) — с конца файла.
</source>
В следующем примере мы читаем посимвольно один файл с единственной строкой <code>"abcdef"</code> справа налево и записываем прочитанные символы в другой файл.
<source lang="perl">
use IO::Seekable; # Для seek
# Генерируем входящий файл таким нехитрым способом.
qx {
echo -n "abcdef" > "in.txt"
};
open IN, "in.txt" and open OUT, ">out.txt" or die "$!"; # Открываем файлы
seek IN, -1, SEEK_END; # Смещаем указатель файла на позицию перед последним символом.
while ($character = getc(IN) and tell(IN) > 1) { # Читаем файл справа налево, пока не упремся в начало файла.
print OUT $character; # Печатаем в другой файл прочитанный символ.
seek IN, -2, SEEK_CUR; # Смещаем указатель на две позиции налево относительно текущей позиции.
}
print OUT $character; # Печатаем первый символ, так как условие выхода в цикле составлено так.
</source>
Здесь впервые встречается функция <code>tell()</code>, которая возвращает текущую позицию файлового указателя, начиная с нуля.
При записи по дескриптору, вы обычно указываете его в вызове, однако, когда операций вывода очень много для одного и того же дескриптора, становится утомительным постоянно его писать. С помощью функции <code>select(<дескриптор>)</code>, можно указать дескриптор по умолчанию, тогда любые вызовы <code>print</code> и <code>write</code> без явного дескриптора будут направлены на дескриптор по умолчанию. Вызов функции возвращает предыдущее значение, поэтому, если нужно, можно скопировать это значение, чтобы позже восстановить старый дескриптор по умолчанию.
В заключении к этому разделу хотелось бы отметить, что на практике в простых ситуациях не обязательно прибегать к ручному чтению файлов: обычно можно найти подходящую функцию в стандартной библиотеке, которая умеет работать с дескриптором, которая выполнит чтение и запись в зависимости от того, что вам нужно. Некоторые приемы мы рассмотрим в поздних главах.
==== Обнаружение конца чтения ====
Обычно при чтении файла программа ожидает наступления условия <code>EOF</code> ({{lang-en|End-Of-File}}), чтобы закрыть дескриптор и освободить ресурсы системы. Это условие может наступать по-разному. Например, в интерактивном режиме работы терминала вы можете закрыть <code>STDIN</code>, послав сигнал на терминал сочетанием {{Клавиша|Ctrl}}+{{Клавиша|d}}.
При выполнении операции чтения с помощью <code>read()</code> и <code>sysread()</code>, они возвращают <code>undef</code> при наступлении события <code>EOF</code>, и устанавливают значение 0 в специальной переменной <code>$!</code>. Таким образом, вы можете безопасно читать дескриптор, например, таким циклом
<source lang="perl">
while (1) {
my $bytes = read(STDIN, $buffer, 100);
die "Error" unless defined($bytes);
last unless $bytes > 0;
}
</source>
С другой стороны, операция <code><></code> возвращает <code>undef</code> и при наступлении события <code>EOF</code> и в случае ошибки, поэтому для перехвата ошибки следует всегда проверять <code>$!</code>:
<source lang="perl">
...
undef $!;
while (my $line = <STDIN>) {
$data .= $line;
}
die "Abnormal reading" if defined ($!);
</source>
Для проверки того, что в дескрипторе наступило условие <code>EOF</code>, можно использовать функцию <code>eof(FH)</code>, которая возвращает ИСТИНУ, если условие наступило. Без аргументов проверяет дескриптор, из которого последний раз выполнялось чтение. Обычно не рекомендуется использовать <code>eof()</code> в больших программах, так как она имеет очень много скрытых правил. Если эта функция используется, обычно это говорит о проблемах в структуре программы.
==== Тонкости контроля ошибок чтения/записи ====
Для контроля ошибок в Perl используется специальная переменная <code>$!</code>, которая имеет ненулевое значение в случае ошибки. Эта переменная работает по-разному в разном контексте:
* Если переменная используется в операции, которая ожидает число, то она хранит числовую константу (например, <code>EACCES</code>), определенную в заголовочных файлах операционной системы.
* Если переменная используется в операции, которая ожидает строку, то она хранит сообщение об ошибке, например <code>"Permission denied"</code>.
Чтобы программы были портируемыми, обычно используется стандартный модуль <code>Errno</code>, чтобы импортировать отдельные константы ошибок и использовать имена, а не числовые значения.
<source lang="perl">
use Errno qw { EACCES ENOENT };
my $result = open (FH, ">/etc/passwd");
if (!$result) {
if ($! == EACCES) {
warn "You do not have permission to open this file.";
} elsif ($! == ENOENT) {
warn "File not found.";
} else {
warn "Other error.";
}
}
</source>
После наступления ошибочной ситуации переменная <code>$!</code> никогда автоматически не сбрасывается, поэтому нужно помнить, что её нужно проверять сразу же после наступления ошибки, а не много инструкций позже, иначе вы рискуете получить трудно уловимую ошибку в неожиданном месте, в котором эта переменная проверяется.
==== Оформление конца строки в разных системах ====
При выполнении построчного чтения следует помнить, что ни в одной системе нет единого подхода к оформлению конца строки. Например, в Unix используется символ <code>LF</code>, который соответствует восьмеричному <code>\012</code> в ASCII кодировке, а в Windows используются два символа <code>CRLF</code> или <code>\015\012</code>.
Чтобы программа оставалась переносимой между системами, следует использовать при необходимости скаляр <code>$/</code>, который хранит признак конца строки.
==== Не буферизированное чтение/запись ====
Следующие функции реализуют не буферизированное чтение/запись:
<source lang="perl">
# Чтение
sysread <дескриптор>, <переменная>, <длина> [, <смещение>];
# Запись
syswrite <дескриптор>, <переменная>, <длина> [, <смещение>];
# Управление файловым указателем
sysseek <дескриптор>, <смещение>, <точка-отсчета>;
</source>
* Значения всех аргументов аналогичны их буферизованным аналогам. Основной разницей является то, что для реализации <code>tell()</code>, нужно использовать <code>sysseek()</code>:<source lang="perl">
$position = sysseek <дескриптор>, 0, 1; # Вернет текущую позицию
</source>
* Операции чтения/записи при не буферизованном чтении/записи не всегда могут завершаться успешно. Вы всегда должны сверяться с тем, что возвращает функция чтения/записи (напомним, что они возвращают число прочитанных/записанных байтов).
* Нельзя смешивать буферизованное и не буферизованное чтение/запись для одного и того же файла. Подобная практика может приводить к непредсказуемым коллизиям.
Пример
<source lang="perl">
$total_read = 0;
open FILE, "< example.txt";
$bufsize = 1024;
while (my $read = sysread(FILE, $buffer, $bufsize)) {
print "$read\n";
$total_read += $read;
}
print "Total read: $total_read\n";
</source>
==== Текстовый и двоичный подходы чтения ====
Обычно, когда операция чтения ориентируется не на размер передаваемых данных, а на байты, которые передаются, такой подход чтения называют текстовым. Напротив, когда операции известно сколько максимально байт она может прочитать/записать за один раз, говорят о двоичном подходе чтения.
Как мы уже сказали выше, стандартная библиотека ввода-вывода при текстовом чтении использует символ(ы) конца строки, чтобы остановить операцию чтения. Некоторые символы при текстовом чтении могут нести особый смысл. Такие символы называются управляющими. Обычно при текстовом чтении стандартная библиотека сама умеет преобразовывать их и удалять из потока.
Однако, текстовый подход неприемлем, когда вы читаете двоичные данные, которые не имеют никаких предусмотренных заранее разделителей или управляющих символов, из-за чего приходится в любом случае читать данные порциями.
Стандартное буферизированное чтение в Perl может работать в любом из этих режимов, но по умолчанию используется текстовый. Чтобы открытый дескриптор читался/писался в двоичном режиме, необходимо использовать на нём функцию <code>binmode(FILEHANDLE, [, $discp])</code>, которая делает переключения в буферизированном чтении, заставляя читать/писать дескриптор как двоичный файл. Начиная с Perl 5.6, вторым аргументом вы можете передавать дисциплину, например, значение <code>:raw</code> заставляет читать/писать двоично, отключая разделитель строк и интерпретацию управляющих символов, а значение <code>:crlf</code> снова включает текстовый режим. Функция <code>binmode()</code> выполняет реальную работу только в системах, в которых разделитель состоит более чем из одного символа, как в Windows.
Функция <code>open()</code> позволяет указать на необходимость двоичного ввода/вывода (и даже кодировку) в момент открытия дескриптора. Для этого нужно указать флаг рядом с режимом открытия:
<source lang="perl">
open (FH, '<:raw', $filename); # открыть в двоичном режиме чтения
open (FH, '>:raw', $filename); # открыть в двоичном режиме записи
open (FH, '<:encoding(UTF-8)', $filename); # интерпретировать входящую кодировку как UTF-8
</source>
Обычно на практике, если у программиста есть четкое намерение читать/писать в двоичном режиме, он пользуется исключительно функциями <code>sysread()</code> и <code>syswrite()</code>.
=== Операции с файлами ===
В Perl встроено множество функций, позволяющих работать с файлами подобно тому, как вы делаете это в командной оболочке. Из программы Perl вы можете создавать файлы, ссылки на файлы, изменять флаги доступа, запрашивать статистику файла и другое. В этом разделе мы перечислим некоторые наиболее используемые функции для работы с файлами.
<source lang="perl">
chmod <флаги-доступа>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chmod</code>: выставляет флаги доступа для владельца файла, группы пользователей и всех остальных. Флаги доступа обычно задаются восьмеричным числом (именно числом, а не строкой) в первом аргументе, после чего нужно передать список файлов. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
chown <идентификатор-пользователя>, <идентификатор-группы>, <список-файлов>;
</source>
::Аналогична *nix утилите <code>chown</code>: меняет владельца и группу файла. В отличие от системной утилиты, идентификаторы нужно указывать числом. Значение -1 интерпретируется многими системами, как ничего не менять. Возвращает число, которое означает число файлов, для которых процедура прошла успешно.
<source lang="perl">
link <исходный-файл>, <новый-файл>;
symlink <исходный-файл>, <новый-файл>;
</source>
::Создают соответственно жесткую и символическую файловую ссылку. В случае жесткой ссылки возвращается ИСТИНА или ЛОЖЬ, в зависимости от успеха операции. В случае символической ссылки, возвращается 1 в случае успеха и 0 — в случае провала. Удалить созданные ссылки на файлы можно функцией <code>unlink()</code>, которой нужно передавать список из файлов. Данная функция удаляет одну ссылку на каждый файл, переданный в списке. Если ссылки на файл не существует, то удаляется сам файл. Функция <code>unlink()</code> возвращает число, которое выражает для скольки файлов из переданного списка операция удалась успешно.
<source lang="perl">
rename <старое-имя>, <новое-имя>;
</source>
::Переименовывает файл, заданный первым аргументом, на имя, во-втором аргументе. В случае успеха функция возвращает 1 и 0 — в случае провала.
<source lang="perl">
truncate <дескриптор>, <длина>;
truncate <имя-файла>, <длина>;
</source>
::Усекает файл до заданной длины. Значение длины может быть как меньше текущего размера файла, так и больше. В случае успешного усечения возвращается ИСТИНА, иначе — неопределенное значение <code>undef</code>.
<source lang="perl">
stat <дескриптор>;
</source>
::Возвращает структуру индексного дескриптора (<code>inode</code>) для файла.
::<source lang="perl">
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
# Здесь:
# dev — номер устройства в файловой системе.
# ino — номер индексного дескриптора.
# mode — тип и права доступа для файла.
# nlink — число жестких ссылок. Если нет, то равно 1.
# uid — числовой идентификатор владельца файла.
# gid — числовой идентификатор группы.
# rdev — идентификатор устройства (только для специальных файлов).
# size — размер файла в байтах.
# atime — время последнего обращения к файлу с начала эпохи.
# mtime — время последнего изменения файла с начала эпохи.
# ctime — время изменения индексного дескриптора с начала эпохи.
# blksize — предпочтительный размер блока для операций ввода/вывода.
# blocks — фактическое количество выделенных блоков для размещения файла.
</source>
::Отметим, что не все поля структуры поддерживаются в разных операционных системах. В списковом контексте функция возвращает не пустой список в случае успеха. Если вместо дескриптора передать нижнее подчеркивание <code>stat(_)</code>, то функция возвращает закэшированное значение последнего удачного вызова. Данную функцию можно использовать для файлов, ссылок и каталогов. Для получения информации о символических ссылках используется функция <code>lstat()</code>, для которой все аналогично. Если система не поддерживает символические ссылки, то вызов <code>lstat()</code> аналогичен вызову <code>stat()</code>.
Кроме этих функций, для файлов предусмотрены следующие унарные операции, которые используются, как правило, в условных конструкциях:
* <code>-r</code> — файл может читаться эффективным uid/gid;
* <code>-w</code> — в файл может записывать эффективный uid/gid;
* <code>-x</code> — файл может быть исполнен эффективным uid/gid;
* <code>-o</code> — эффективный uid/gid является владельцем файла;
* <code>-R</code> — файл может читаться действительным uid/gid;
* <code>-W</code> — в файл может писать действительный uid/gid;
* <code>-X</code> — файл может быть исполнен действительным uid/gid;
* <code>-O</code> — действительный uid/gid является владельцем файла;
* <code>-e</code> — файл существует;
* <code>-z</code> — размер файла равен нулю;
* <code>-s</code> — размер файла не равен нулю (размер возвращается как результат);
* <code>-f</code> — файл является обычным;
* <code>-d</code> — файл является каталогом;
* <code>-l</code> — файл является символической ссылкой;
* <code>-p</code> — файл является именованным FIFO каналом или дескриптор связан с таким каналом;
* <code>-S</code> — файл является сокетом;
* <code>-b</code> — файл является блочным устройством;
* <code>-c</code> — файл является символьным устройством;
* <code>-t</code> — дескриптор связан с терминалом;
* <code>-u</code> — у файла поднят флаг setuid;
* <code>-g</code> — у файла поднят флаг setgid;
* <code>-k</code> — у файла установлен sticky bit;
* <code>-T</code> — файл является текстовым;
* <code>-B</code> — файл является бинарным (двоичным);
* <code>-M</code> — возраст файла в днях на момент выполнения программы;
* <code>-A</code> — возраст файла в днях с последнего обращения к файлу;
* <code>-C</code> — возраст файла в днях для времени последней модификации индексного дескриптора файла.
Унарные операции применяются к строке, содержащей имя файла; к выражению, которое вычисляет имя файла, или к файловому дескриптору. Если параметр не задан, то проверяется значение в переменной <code>$_</code>. В случае ИСТИНЫ возвращается 1. В случае ЛЖИ возвращается пустая строка, а в случае, если файла не существует, возвращается значение <code>undef</code>.
Операции <code>-T</code> и <code>-B</code> работают следующим образом. Если первый блок файла содержит более 30% необычных символов (управляющие последовательности или байты с установленными старшими битами), то файл признается двоичным, иначе он признается текстовым. Если эти операции применяются к дескриптору, то проверяется не файл, а буфер ввода/вывода. Обе операции возвращают ИСТИНА, если связанный с дескриптором файл пуст или внутренний указатель находится в конце файла.
При выполнении унарных текстовых операций всегда неявно вызывается функция <code>stat()</code>, причем ее результат всегда кэшируется.
=== Операции с каталогами ===
Каталоги в *nix являются специальными файлами, помеченными в <code>rdev</code> как каталоги. Каталоги хранят пары, состоящие из объекта каталога и значения его индексного дескриптора.
Для работы с каталогами в Perl предусмотрены следующие функции.
<source lang="perl">
opendir <дескриптор>, <имя каталога>;
closedir <дескриптор>;
readdir <дескриптор>;
</source>
::Соответственно открывает/закрывает/читает каталог. Допускается, чтобы дескрипторы каталогов и файлов имели одинаковые имена в одно и то же время (внутри Perl разберется что к чему), однако, вряд ли вы захотите так сделать, так как это запутает того, кто будет читать программу. Функция <code>readdir()</code> для открытого каталога в списковом контексте возвращает список имен всех файлов или пустой список, если имена уже были прочитаны. В скалярном контексте функция возвращает имя очередного файла в каталоге или неопределенное значение <code>undef</code>, если все имена уже прочитаны. Функцией <code>rewinddir(<дескриптор>)</code> можно установить текущую позицию каталога в начало, чтобы повторно запросить имена файлов в нем. Функция <code>readdir()</code> возвращает всегда относительное имя файла: абсолютное имя вы должны формировать самостоятельно.
<source lang="perl">
mkdir <имя-каталога>, <флаги-доступа>;
</source>
::Создаёт каталог с указанным именем и флагами доступа (в восьмеричной форме). Если задаётся не полное имя каталога, то новый каталог будет создан в текущем рабочем каталоге. Если каталог был создан, возвращает ИСТИНУ, иначе — ЛОЖЬ. В случае ошибки в специальной переменной <code>$!</code> будет записана причина.
<source lang="perl">
chdir [<дескриптор>];
</source>
::Делает указанный каталог рабочим. Если каталог опущен, то рабочем становится каталог указанный в переменной окружения под именем <code>HOME</code> или <code>LOGDIR</code>, когда <code>HOME</code> не определена.
<source lang="perl">
rmdir [<имя-каталога>];
</source>
::Удаляет указанный каталог. Если переменная не указана, то берется значение из переменной <code>$_</code>. В случае успеха возвращает ИСТИНУ и ЛОЖЬ в противном случае, записывая причину ошибки в переменную <code>$!</code>. Эта функция удаляет только пустые каталоги. Чтобы удалить не пустой каталог, нужно его сначала очистить ото всех файлов.
Пример работы с каталогом.
<source lang="perl">
opendir DIR, "/bin" or die "$!";
# Печатает все файлы не являющиеся каталогами.
while ($name = readdir DIR) {
next if -d $name;
print "$name\n";
}
closedir DIR;
</source>
== Примечания ==
{{примечания}}
ipurg1rxt8gcbys2sovhdjhjvqjsq7k
АОН/ФАП-147
0
30908
261825
257363
2025-07-03T16:40:29Z
Leksey
3027
/* Структура */ специалист
261825
wikitext
text/x-wiki
'''ФАП-147''' — нумерованный документ (на сленге именующийся [[АОН/ФАП|фапом]]), где описано как выдаются [[АОН/Пилотское свидетельство|свидетельства пилотов]], а также [[АОН/Техник|техников]]. Также там описаны свидетельства бортпроводников и прочего персонала, но в АОН никто кроме пилотов и техников не встречается.
==Структура==
Если упрощенно описывать структуру ФАП-147, то у каждого вида свидетельств есть свой раздел, где описаны специфичные для него моменты. Сколько часов нужно налетать минимум при обучении, какие это должны быть полеты, какой минимальны возраст для того чтобы получить пилотское. [[АОН/АУЦ|АУЦ]] исходя из этих требований разрабатывают свои программы подготовки.
ФАП-147 похож в достаточной степени на похожие документы в других странах.
В части АОН разделы по типам пилотских следующие:
*III. Требования к частному пилоту
*IX. Требования к пилоту планера
*X. Требования к пилоту свободного аэростата
*XI. Требования к пилоту сверхлегкого воздушного судна ([[АОН/СВС|СВС]]) - сюда входят автожиры и дельталеты и пр.
Для специалиста по техническому обслуживанию воздушных судов ([[АОН/Техник|авиатехников]]) тоже предусмотрен раздел в ФАП-147 под номером XVII.
Требования по налету для выдачи свидетельств:
;Самолет
{{Цитата|а) должен иметь налет на самолете не менее 40 ч или 35 ч в ходе прохождения курса подготовки по утвержденной программе в качестве пилота самолета, в который засчитывается не более 5 ч налета на тренажере;}}
{{Цитата|должен иметь не менее 10 ч самостоятельного налета на самолетах под руководством пилота-инструктора в соответствии с запрашиваемой квалификационной отметкой, включая 5 ч самостоятельного налета по маршруту, при этом, по крайней мере, один полет по маршруту протяженностью не менее 270 км с посадкой до полной остановки на двух различных аэродромах;
должен иметь не менее 1 ч налета по приборам;
должен иметь 3 часа налета ночью, включая выполнение пяти взлетов и посадок ночью, выполняя обязанности командира воздушного судна.}}
{{Цитата|В общий налет может засчитываться 10% налета на других видах воздушных судов, кроме дирижабля и свободного аэростата, но суммарно не более 10 ч;}}
;Вертолет
{{Цитата|а) должен иметь налет на вертолете не менее 40 ч или 35 ч в ходе прохождения курса подготовки по утвержденной программе в качестве пилота вертолета, в который засчитывается не более 5 ч налета на тренажере;
должен иметь не менее 10 ч самостоятельного налета на вертолетах под руководством пилота-инструктора, включая 5 ч самостоятельного налета по маршруту, в который входит, как минимум, один полет по маршруту протяженностью не менее 180 км с посадками в двух различных пунктах;
должен иметь не менее 1 ч налета по приборам;
должен иметь 3 часа налета ночью, включая выполнение пяти взлетов и посадок ночью, выполняя обязанности командира воздушного судна. В общий налет засчитывается 10% налета на других видах воздушных судов, кроме дирижабля, но не более 10 ч;}}
;Планер
{{Цитата|в) иметь налет не менее 6 ч в качестве пилота планера, включая 2 ч самостоятельного налета, в течение которых он выполнил не менее 20 взлетов и посадок;}}
{{Цитата|Время полета планера. Общее время нахождения в полете на буксире или без буксира с момента начала движения планера при взлете и до момента его остановки по окончании полета.}}
;Аэростат
{{Цитата|в) иметь налет не менее 6 ч в качестве пилота планера, включая 2 ч самостоятельного налета, в течение которых он выполнил не менее 20 взлетов и посадок;}}
;СВС
{{Цитата|в) иметь налет:
на сверхлегких воздушных судах, оборудованных силовой установкой, - не менее 25 ч в качестве пилота сверхлегкого воздушного судна, включая 12 ч самостоятельного налета, 5 ч - по маршруту, не менее 30 самостоятельных взлетов и посадок, из которых 6 посадок с задросселированным (выключенным) двигателем;
на сверхлегких воздушных судах без силовой установки - общий налет 12 ч, 12 полетов продолжительностью более 10 мин. каждый и 5 полетов более 30 мин. каждый, 3 полета по маршруту на дальность не менее 1,5 км;}}
== Самостоятельный==
{{Цитата|Самостоятельный налет. Время полета, в течение которого пилот-курсант является единственным лицом на борту воздушного судна.}}
==См. также==
{{Префиндекс}} <!--Шаблон позволяет вывести списком все подстраницы данной страницы.-->
*[[АОН/Законодательство]]
*[[АОН/ФАП|ФАПы]]
*[[АОН/Пилотское свидетельство]]
== Ссылки ==
*[http://ivo.garant.ru/#/document/194352 Актуальное состояние ФАП-147] в правовой базе Гарант
==Примечания==
{{Примечания}}
{{АОН}}
lu0xqxbc1nzwrj12bwdqqclexaynf7y