Spec-Zone.ru › Perl 5.34

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

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

Copyright 2013 Tom Christiansen; теперь поддерживается Perl5 Porters

Эта документация является бесплатной; вы можете перераспределять и/или изменять ее на тех же условиях, что и сам 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.34.0/perlopentut

Spec-Zone.ru

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