perlopentut
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- Открытие текстовых файлов
- Открытие двоичных файлов
- Открытие каналов
- СМОТРИТЕ ТАКЖЕ
- АВТОР И АВТОРСКИЕ ПРАВА
ИМЯ
perlopentut - простые рецепты для открытия файлов и каналов в Perl
ОПИСАНИЕ
Всякий раз, когда вы выполняете ввод-вывод с файлом в Perl, вы делаете это через то, что в Perl называется дескриптором файла. Дескриптор файла — это внутреннее имя внешнего файла. Функция open отвечает за установление связи между внутренним и внешним именем, а функция close — за разрыв этой связи.
Для удобства Perl настраивает несколько специальных дескрипторов файлов, которые уже открыты при запуске. К ним относятся STDIN, STDOUT, STDERR, и ARGV. Поскольку они предварительно открыты, вы можете использовать их сразу, без необходимости открывать их самостоятельно:
print STDERR "This is a debugging message.\n";
print STDOUT "Please enter something: ";
$response = <STDIN> // die "how come no input?";
print STDOUT "Thank you!\n";
while (<ARGV>) { ... } Как вы видите из этих примеров, STDOUT и STDERR являются дескрипторами вывода, а STDIN и ARGV — дескрипторами ввода. Они написаны заглавными буквами, потому что зарезервированы для Perl, так же как массив @ARGV и хэш %ENV.
Ваши оболочки установили их внешние ассоциации.
Вам нужно будет самостоятельно открыть все остальные дескрипторы файлов. Несмотря на множество вариантов, наиболее распространенный способ вызова функции open() Perl — с тремя аргументами и одним возвращаемым значением:
OK = open(HANDLE, MODE, PATHNAME)
Где:
- OK
-
будет определенным значением, если открытие прошло успешно, но
undefв случае неудачи; - HANDLE
-
должен быть неопределенной скалярной переменной, которая заполняется функцией
openпри успехе; - MODE
-
это режим доступа и формат кодировки для открытия файла;
- PATHNAME
-
— внешнее имя файла, который вы хотите открыть.
Большинство сложностей функции open() заключаются во множестве возможных значений параметра MODE.
Еще одна вещь перед тем, как мы покажем, как открыть файлы: открытие файлов (обычно) не блокирует их автоматически в Perl. Смотрите perlfaq5 для получения информации о блокировке.
Открытие текстовых файлов
Открытие текстовых файлов для чтения
Если вы хотите читать текстовый файл, сначала откройте его в режиме только для чтения следующим образом:
my $filename = "/some/path/to/a/textfile/goes/here";
my $encoding = ":encoding(UTF-8)";
my $handle = undef; # this will be filled in on success
open($handle, "< $encoding", $filename)
|| die "$0: can't open $filename for reading: $!"; Как и в оболочке, в Perl для открытия файла в режиме только для чтения используется "<". Если это успешно, Perl выделяет для вас новый дескриптор файла и заполняет аргумент $handle ссылкой на этот дескриптор.
Теперь вы можете использовать такие функции, как readline, read, getc, и sysread с этим дескриптором. Вероятно, наиболее распространенная функция ввода похожа на оператор:
$line = readline($handle);
$line = <$handle>; # same thing Поскольку функция readline возвращает undef в конце файла или при ошибке, вы иногда увидите ее использование таким образом:
$line = <$handle>;
if (defined $line) {
# do something with $line
}
else {
# $line is not valid, so skip it
} Вы также можете быстро die неопределенное значение таким образом:
$line = <$handle> // die "no input found"; Однако, если достижение конца файла (EOF) является ожидаемым и нормальным событием, вам не нужно завершать выполнение просто потому, что закончился ввод. Вместо этого вы, скорее всего, хотите выйти из цикла ввода. Затем вы можете проверить, была ли ошибка причиной выхода из цикла, и действовать соответствующим образом:
while (<$handle>) {
# do something with data in $_
}
if ($!) {
die "unexpected error while reading from $filename: $!";
} Примечание о кодировках: необходимость указывать кодировку текста каждый раз может показаться немного обременительной. Чтобы установить кодировку по умолчанию для open, чтобы вам не нужно было ее указывать каждый раз, можно использовать псевдоним open:
use open qw< :encoding(UTF-8) >; После этого вы можете безопасно опустить часть кодировки в режиме открытия:
open($handle, "<", $filename)
|| die "$0: can't open $filename for reading: $!"; Но никогда не используйте голый "<" без предварительного установки кодировки по умолчанию. В противном случае Perl не сможет узнать, какой из многих, многих, многих возможных вариантов текстового файла у вас есть, и Perl не будет знать, как правильно отобразить данные вашего файла в действительные символы, с которыми он может работать. Другие распространенные форматы кодировки включают "ASCII", "ISO-8859-1", "ISO-8859-15", "Windows-1252", "MacRoman", и даже "UTF-16LE". Смотрите perlunitut для получения дополнительной информации о кодировках.
Открытие текстовых файлов для записи
Когда вы хотите записать в файл, сначала нужно решить, что делать с существующим содержимым этого файла. У вас есть два основных варианта: сохранить или перезаписать.
Если вы хотите сохранить существующее содержимое, вам нужно открыть файл в режиме добавления. Как и в оболочке, в Perl вы используете ">>" для открытия существующего файла в режиме добавления. ">>" создает файл, если он еще не существует.
my $handle = undef;
my $filename = "/some/path/to/a/textfile/goes/here";
my $encoding = ":encoding(UTF-8)";
open($handle, ">> $encoding", $filename)
|| die "$0: can't open $filename for appending: $!"; Теперь вы можете записывать в этот дескриптор файла, используя любые из print, printf, say, write, или syswrite.
Как отмечалось выше, если файла не существует, открытие в режиме добавления создаст его для вас. Но если файл уже существует, его содержимое безопасно, поскольку вы будете добавлять новый текст после конца старого.
С другой стороны, иногда вы хотите перезаписать все, что может там быть. Чтобы очистить файл перед записью в него, вы можете открыть его в режиме записи:
my $handle = undef;
my $filename = "/some/path/to/a/textfile/goes/here";
my $encoding = ":encoding(UTF-8)";
open($handle, "> $encoding", $filename)
|| die "$0: can't open $filename in write-open mode: $!"; Здесь снова Perl работает так же, как оболочка, в том, что ">" перезаписывает существующий файл.
Как и в режиме добавления, при открытии файла в режиме записи вы можете записывать в этот дескриптор файла, используя любые из print, printf, say, write, или syswrite.
Что насчет режима чтения-записи? Вы, вероятно, должны предположить, что он не существует, потому что открытие текстовых файлов в режиме чтения-записи вряд ли сделает то, что вы хотите. Смотрите perlfaq5 для получения подробностей.
Открытие двоичных файлов
Если открываемый файл содержит двоичные данные вместо текстовых символов, то аргумент MODE для open немного отличается. Вместо указания кодировки вы сообщаете Perl, что ваши данные представляют собой сырые байты.
my $filename = "/some/path/to/a/binary/file/goes/here";
my $encoding = ":raw :bytes"
my $handle = undef; # this will be filled in on success А затем открывайте, как и раньше, выбирая "<", ">>", или ">" по мере необходимости:
open($handle, "< $encoding", $filename)
|| die "$0: can't open $filename for reading: $!";
open($handle, ">> $encoding", $filename)
|| die "$0: can't open $filename for appending: $!";
open($handle, "> $encoding", $filename)
|| die "$0: can't open $filename in write-open mode: $!"; В качестве альтернативы вы можете изменить режим на двоичный для существующего дескриптора таким образом:
binmode($handle) || die "cannot binmode handle"; Это особенно полезно для дескрипторов, которые Perl уже открыл для вас.
binmode(STDIN) || die "cannot binmode STDIN";
binmode(STDOUT) || die "cannot binmode STDOUT"; Вы также можете передать binmode явную кодировку, чтобы изменить ее на лету. Это не совсем режим «двоичный», но мы все равно используем binmode для этого:
binmode(STDIN, ":encoding(MacRoman)") || die "cannot binmode STDIN";
binmode(STDOUT, ":encoding(UTF-8)") || die "cannot binmode STDOUT"; После того, как вы правильно откроете двоичный файл в нужном режиме, вы можете использовать все те же функции ввода-вывода Perl, что и для текстовых файлов. Однако вы, возможно, захотите использовать read с фиксированным размером вместо readline с переменным размером для вашего ввода.
Вот пример того, как скопировать двоичный файл:
my $BUFSIZ = 64 * (2 ** 10);
my $name_in = "/some/input/file";
my $name_out = "/some/output/flie";
my($in_fh, $out_fh, $buffer);
open($in_fh, "<", $name_in)
|| die "$0: cannot open $name_in for reading: $!";
open($out_fh, ">", $name_out)
|| die "$0: cannot open $name_out for writing: $!";
for my $fh ($in_fh, $out_fh) {
binmode($fh) || die "binmode failed";
}
while (read($in_fh, $buffer, $BUFSIZ)) {
unless (print $out_fh $buffer) {
die "couldn't write to $name_out: $!";
}
}
close($in_fh) || die "couldn't close $name_in: $!";
close($out_fh) || die "couldn't close $name_out: $!"; Открытие каналов
Perl также позволяет открыть дескриптор файла в стороннюю программу или команду оболочки вместо файла. Вы можете сделать это, чтобы передать данные из вашей программы Perl в стороннюю команду для дальнейшей обработки или получить данные из другой программы для обработки вашей собственной программой Perl.
Дескрипторы файлов в команды также известны как каналы, поскольку они работают на основе аналогичных принципов межпроцессного взаимодействия, как конвейеры Unix. Такой дескриптор файла имеет активную программу вместо статического файла на внешнем конце, но в остальном он работает так же, как обычный дескриптор файла, со всеми техниками, обсуждавшимися ранее в этой статье.
Таким образом, вы открываете канал, используя тот же вызов open, что и для открытия файлов, установив второй (MODE) аргумент в специальные символы, указывающие либо на канал ввода, либо на канал вывода. Используйте "-|" для дескриптора файла, который позволит вашей программе Perl читать данные из сторонней программы, и "|-" для дескриптора файла, который будет отправлять данные этой программе вместо этого.
Открытие канала для чтения
Предположим, вам нужно, чтобы ваша программа Perl обрабатывала данные, хранящиеся в близлежащей директории, названной unsorted, которая содержит ряд текстовых файлов. Вы также хотите, чтобы ваша программа отсортировала все содержимое этих файлов в один отсортированный по алфавиту список уникальных строк перед началом их обработки.
Вы могли бы сделать это, открыв обычный дескриптор файла в каждый из этих файлов, постепенно создавая массив во временной памяти всего содержимого загружаемых файлов таким образом, и, наконец, отсортировать и отфильтровать этот массив, когда закончились файлы для загрузки. Или, вы могли бы переложить всю эту работу по объединению и сортировке в команду sort вашей операционной системы, открыв канал непосредственно в ее вывод, и начать работу значительно быстрее.
Вот как это может выглядеть:
open(my $sort_fh, '-|', 'sort -u unsorted/*.txt')
or die "Couldn't open a pipe into sort: $!";
# And right away, we can start reading sorted lines:
while (my $line = <$sort_fh>) {
#
# ... Do something interesting with each $line here ...
#
} Второй аргумент к open, "-|", делает его каналом чтения в отдельную программу, а не обычным дескриптором файла в файл.
Обратите внимание, что третий аргумент к open — это строка, содержащая имя программы (sort) плюс все ее аргументы: в данном случае, -u для указания сортировки по уникальным значениям, а затем образец файлов для сортировки. Результирующий дескриптор файла $sort_fh работает как дескриптор чтения только для чтения ("<"). Ваша программа может затем читать данные из него, как если бы она была открыта для обычного отдельного файла.
Открытие канала для записи
Продолжая предыдущий пример, предположим, что ваша программа завершила обработку, и результаты находятся в массиве, называемом @processed. Вы хотите вывести эти строки в файл под названием numbered.txt с аккуратно отформатированным столбцом номеров строк.
Конечно, вы могли бы написать свой собственный код для этого — или, еще раз, вы могли бы передать эту задачу другой программе. В этом случае, cat, работая с опцией -n для активации нумерации строк, должно справиться с задачей:
open(my $cat_fh, '|-', 'cat -n > numbered.txt')
or die "Couldn't open a pipe into cat: $!";
for my $line (@processed) {
print $cat_fh $line;
} Здесь мы используем второй аргумент open со значением "|-", означающим, что дескриптор файла, назначенный $cat_fh, должен быть каналом записи. Затем мы можем использовать его так же, как и обычный дескриптор файла для записи, включая основную функцию print данных в него.
Обратите внимание, что третий аргумент, определяющий команду, которую мы хотим передать по каналу, настраивает cat на перенаправление своего вывода через этот символ ">" в файл numbered.txt. Это может показаться немного сложным, поскольку тот же символ имел бы совершенно другое значение, если бы он был во втором аргументе функции open! Но здесь, в третьем аргументе, он просто является частью командной строки, в которую Perl откроет канал, и сам Perl не придаёт ему никакого особого значения.
Выражение команды как списка
Для открытия каналов Perl предлагает возможность вызова open со списком, содержащим желаемую команду и все её аргументы в качестве отдельных элементов, а не объединяя их в одну строку, как в примерах выше. Например, мы могли бы сформулировать вызов open в первом примере следующим образом:
open(my $sort_fh, '-|', 'sort', '-u', glob('unsorted/*.txt'))
or die "Couldn't open a pipe into sort: $!"; При таком вызове Perl напрямую вызывает данную команду, минуя оболочку. Таким образом, оболочка не будет пытаться интерпретировать никакие специальные символы в списке аргументов команды, что могло бы иметь нежелательные последствия. Это может привести к более безопасным и менее подверженным ошибкам вызовам open, полезным в таких случаях, как передача переменных в качестве аргументов или даже просто ссылка на имена файлов с пробелами в них.
Однако, когда вы хотите передать значимый метасимвол в оболочку, например символ "*" внутри этого последнего аргумента unsorted/*.txt, вы не можете использовать этот альтернативный синтаксис. В этом случае мы обошли это с помощью удобной встроенной функции Perl glob, которая оценивает свой аргумент в список имён файлов — и мы можем безопасно передать этот результирующий список в open, как показано выше.
Также обратите внимание, что представление аргументов команд с каналами в виде списка таким образом не работает на всех платформах. Оно будет работать на любой основанной на Unix ОС, предоставляющей реальную функцию fork (например, macOS или Linux), а также на Windows при запуске Perl 5.22 или более поздних версий.
См. также
Полное описание open предоставляет подробную справку по этой функции, выходящую за рамки базовых рекомендаций, рассмотренных здесь.
Автор и авторские права
Copyright 2013 Tom Christiansen; теперь поддерживается Perl5 Портерами
Эта документация бесплатна; вы можете перераспределять и/или изменять её в тех же условиях, что и Perl сам.
© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.32.0/perlopentut