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: $!"; Когда вы вызываете open таким образом, Perl непосредственно выполняет заданную команду, минуя оболочку. Таким образом, оболочка не будет пытаться интерпретировать какие-либо специальные символы в списке аргументов команды, что могло бы вызвать нежелательные эффекты. Это может привести к более безопасным и менее подверженным ошибкам вызовам open, полезным в случаях, таких как передача переменных в качестве аргументов или даже просто ссылок на имена файлов с пробелами в них.
Однако, когда вы хотите передать значимый метасимвол оболочке, такой как символ "*" внутри этого конечного аргумента unsorted/*.txt, вы не можете использовать этот альтернативный синтаксис. В этом случае мы обошли эту проблему, используя удобную встроенную функцию Perl glob, которая оценивает свой аргумент в список имён файлов — и мы можем безопасно передать этот результирующий список прямо в open, как показано выше.
Также обратите внимание, что представление аргументов команд, передаваемых через канал, в виде списка таким образом не работает на всех платформах. Оно будет работать на всех Unix-подобных операционных системах, которые предоставляют истинную функцию fork (например, macOS или Linux), а также на Windows при использовании Perl 5.22 или более поздних версий.
См. также
Полная документация для open предоставляет подробную справку по этой функции, выходящую за рамки базовых рекомендаций, описанных здесь.
Автор и авторские права
Copyright 2013 Tom Christiansen; сейчас поддерживается Perl5 Porters
Данная документация является свободной; вы можете распространять и/или изменять её на тех же условиях, что и Perl сам по себе.
© 1993–2023 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.38.0/perlopentut