Spec-Zone.ru › Perl 5.36

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";

Однако, если достижение конца файла является ожидаемым и нормальным событием, вы не хотите выходить просто потому, что у вас закончился ввод. Вместо этого вы, вероятно, просто хотите выйти из цикла ввода. Затем вы можете проверить, произошла ли ошибка, вызвавшая завершение цикла, и действовать в соответствии с этим:

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 предоставляет исчерпывающую справку по этой функции, выходящую за рамки лучших практик, рассмотренных здесь.

Автор и авторские права

Авторские права 2013 г. Тома Кристиансена; сейчас поддерживается Perl5 Портерами

Эта документация бесплатна; вы можете распространять и/или изменять ее на тех же условиях, что и Perl сам по себе.

© 1993–2021 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.36.0/perlopentut

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API