Михаил Шохирев - Язык программирования Perl
Из этой лекции вы узнали о регулярных выражениях далеко не все, но достаточно, чтобы начать свободно пользоваться ими. По мере накопления опыта применения языка Perl, регулярные выражения станут вашим привычным и надежным инструментом. И тогда задачи, при другом подходе требующие много времени и больших усилий, с помощью регулярных выражений будут решаться быстро, элегантно и эффективно.
Лекция 9. Средства ввода-вывода
В этой лекции разбирается организация ввода-вывода данных в Perl. Рассмотрены средства работы с каталогами, файлами и содержимым файлов. Материалы этой лекции позволят вам писать полноценные программы, "общающиеся с внешним миром".
Цель лекции: познакомиться с возможностями подсистемы ввода-вывода в Perl и освоить основные приемы чтения и записи внешних данных, а также научиться работать со средствами манипулирования файлами и каталогами.
Система ввода-вывода Perl основана на принципах, заложенных в системе Unix и распространившихся на все современные операционные системы. Одним из основных понятий работы в программе с внешними данными являются потоки ввода-вывода. В программе обращение к потоку ввода-вывода производится через файловый манипулятор (file handle), иногда неправильно называемый дескриптором файла. При запуске любой программы автоматически открывается три потока: стандартный ввод (stdin), стандартный вывод (stdout) и стандартный протокол (stderr). Поток стандартного ввода в диалоговой операционной среде связывается с клавиатурной, а потоки стандартного вывода и стандартного протокола - с дисплейной частью консоли операционной системы. Со стандартными потоками в Perl связываются три предопределенных файловых манипулятора: соответственно STDIN, STDOUT и STDERR. Связывание имени файла с пользовательским файловым манипулятором в программе выполняется с помощью операции open(), открывающей поток обмена данными с указанным файлом. Требования надежности рекомендуют обязательно проверять все операции ввода-вывода на успешное завершение. Поэтому в случае возникновения ошибки при открытии файла программа обычно аварийно завершается с помощью функции die(), которая может выводить диагностическое сообщение в стандартный поток протокола. Например, так открывается файл и создается файловый манипулятор FILE_HANDLE:
# открыть для чтения файл по имени, взятом из $file_name open(FILE_HANDLE, $file_name) # или аварийно завершить программу с выдачей сообщения or die("Ошибка открытия файла $file_name: $!n"); #
В случае успешного открытия файла функция open() помещает в свой первый аргумент готовый к использованию файловый манипулятор. Имя файлового манипулятора записывается без разыменовывающего префикса и по традиции выделяется заглавными буквами. Рекомендуется при открытии файла сохранять файловый манипулятор в скалярной переменной, что позволяет локализовать файловый манипулятор для использования только в текущем блоке или подпрограмме. Кроме того, скалярную переменную с файловым манипулятором можно удобно передавать в подпрограммы для выполнения в них операций ввода-вывода. Итак, вот предпочтительный способ открытия файла:
open my $file_handle, $file_name or die "Ошибка открытия файла $file_name: $!n";
(Как и при вызове других функций в Perl, если не возникает неоднозначности, программист решает, заключать аргументы функций в круглые скобки или нет. Среди пишущих на Perl широко распространен стиль программирования без использования круглых скобок.)
При открытии файла функции, помимо файлового манипулятора и имени файла в файловой системе (абсолютного или относительного), указывается режим открытия файла. Он обозначается такими же символами, как переназначение потоков ввода-вывода в командном интерпретаторе операционной системы. Основные режимы открытия потоков ввода-вывода приведены в таблице 9.1.
Таблица 9.1. Основные режимы открытия потоков ввода-выводаОбозначениеРежим открытияПример использования<Чтение (существующего файла с начала)open($fh, '</temp/buffer.txt')>Перезапись (с начала файла)open($fh, '>/temp/buffer.txt')>>Дозапись (в конец файла)open($fh, '>>/temp/buffer.txt')+<Чтение и запись (файл должен существовать)open($fh, '+</temp/buffer.txt')+>Запись и чтение (файл усекается)open($fh, '+>/temp/buffer.txt')+>>Дозапись и чтениеopen($fh, '+>>/temp/buffer.txt')
Применяются две формы записи функции open(): старая с двумя аргументами, когда режим открытия указывается перед именем файла, и новая - с тремя аргументами, в которой режим открытия указывается отдельно вторым параметром. Сравните:
open $fh, '</temp/buffer.txt'; open $fh, '<', '/temp/buffer.txt';
Программисты, знающие язык C, могут воспользоваться для открытия потоков функцией sysopen(), которая аналогична функции открытия потоков в C, и к тому же позволяет более тонко настраивать режимы открытия файлов. А вообще в комплекте с Perl идет целый учебник по функции open(), который можно прочитать утилитой чтения документации:
perldoc perlopentut
Связь файлового манипулятора в программе с обрабатываемым файлом разрывается функцией закрытия потока close(), закрывающей поток ввода-вывода. Ей передается файловый манипулятор открытого файла:
close(FILE) or die("Ошибка при закрытии файла: $!n"); close $handle or die "Ошибка закрытия файла: $!n";
В новой многоуровневой подсистеме ввода-вывода Perl предусматривает при открытии потока вместе с режимом открытия указывать кодировку читаемых и записываемых данных, что позволяет автоматически преобразовывать информацию из одной кодировки в другую, например, так:
open my $in, "<:encoding(UTF-8)", 'utf8.txt' or die; open my $out, ">:encoding(cp1251)", 'cp1251 .txt' or die; while(<$in>){ print $out $_; } close $in or die; close $out or die;
В некоторых операционных системах (например, в MS-DOS), после открытия файла, но перед чтением двоичных данных требуется установить для файлового манипулятора режим работы с двоичными данными с помощью функции binmode($file_handle). При работе в операционных системах, не требующих установки этого режима, вызов функции binmode() не оказывает никакого действия.
В огромном числе случаев ввод данных в Perl-программу и вывод из нее результатов производится построчно, а для разделения строк файла используется разделитель входных записей, хранящийся в специальной переменной $/ ($INPUT_RECORD_SEPARATOR). Для чтения одной строки из входного потока используется операция "кристалл" (diamond), которой в качестве аргумента передается файловый манипулятор или переменная, содержащая манипулятор. Если аргумент не указан, данные читаются из стандартного входного потока.
$input = <>; # чтение строки в $input из STDIN $line = <FILE>; # чтение строки в $line из потока FILE $in = <$handle>; # чтение строки в $in из потока $handle
Операция чтения "кристалл" в скалярном контексте возвращает одну строку вместе с разделителем записей, а когда достигается конец файла, она возвращает неопределенное значение undef, которое воспринимается как ложное. Поэтому типичный цикл построчного чтения данных проверяет прочитанное значение и заканчивается, когда оно становится неопределенным:
open my $fh, "< $file" or die "Ошибка открытия: $!"; while (my $line = <$fh>) { # чтение строки в переменную $line chomp $line; # удаление разделителя строк print length $line, " $linen"; # обработка строки } close $fh or die "Ошибка закрытия: $!";
Операция чтения "кристалл" в списочном контексте возвращает список всех строк с разделителями записей. Так, например, можно считать файл в массив, попутно отсортировав его:
@lines= sort(<$fh>); # в @lines отсортированные строки из $fh
Построчный вывод данных выполняет функция print(), которая по умолчанию выводит список значений в текущий поток вывода, по умолчанию - в STDOUT. Если требуется направить информацию в другой поток, то перед списком выводимых данных указывается файловый дескриптор. Обратите внимание, что между файловым дескриптором и списком выводимых значений запятая не ставится. Вот примеры вывода данных:
print($list, $of, $output, $values); # вывод в STDOUT print STDOUT $list, $of, $output, $values; # вывод в STDOUT print(STDERR $list, $of, $output, $values); # вывод в STDERR print FILE $list, $of, $output, $values; # вывод в FILE print($file $list, $of, $output, $values); # вывод в $file