Глава 7 Доступ к файлам

7.7. Создание фильтра

Проблема

Вы хотите написать программу, которая получает из командной строки список файлов. Если файлы не заданы, входные данные читаются из STDIN. При этом пользователь должен иметь возможность передать программе "-" для обозначения STDIN или "someprog ram |" для

Решение

Читайте строки оператором, оператор о:
while (<>) {
# Сделать что-то со строкой
}

Комментарий

Встречая конструкцию:
while (<>) {
# . . .
}
Perl преобразует ее к следующему виду':
unshift(@ARGV, o-o) unless @ARGV;
while($ARGV = shift @ARGV) {
unless (open(ARGV, $ARGV)) {
\\ программе показанный фрагмент не будет работать из-за внутренней специфики ARGV.
warn "Can't open $ARGV: $!\n";
next;
} while (defined($_ = )) {
#.. .
}
}
Внутри цикла с помощью ARGV и $ARGV можно получить дополнительные данные или узнать имя текущего обрабатываемого файла. Давайте посмотрим, как это делается. Общие принципы Если пользователь не передает аргументы, Perl заносит в @ARGV единственную строку, "-". Это сокращенное обозначение соответствует STDIN при открытии для чтения и STDOUT - для записи. Кроме того, пользователь может передать "-" в командной строке вместо им Далее в цикле из @ARGV последовательно извлекаются аргументы, а имена файлов копируются в глобальную переменную $ARGV. Если файл не удается открыть, Perl переходит к следующему файлу. В противном случае начинается циклическая обработка строк открытого фай При вызове open не используется форма open (ARGV, "> $ARGV"). Это позволяет добиться интересных эффектов - например, передать в качестве аргумента строку "gzip -de file.gz |", чтобы программа получила в качестве входных данных результаты команды "gzip -de Массив @ARGV может изменяться перед циклом или внутри него. Предположим, вы хотите, чтобы при отсутствии аргументов входные данные читались не из STDIN, а из всех программных и заголовочных файлов С и C++. Вставьте следующую строку перед началом обработки
@ARGV = glob("*.[Cch]") unless @ARGV;

Перед началом цикла следует обработать аргументы командной строки - либо с помощью модулей Getopt (см. главу 15 "Пользовательские интерфейсы"), либо вручную: # Аргументы 1: Обработка необязательного флага -с
if (@ARGV && $ARGV[0] eq o-c') {
$chop_first++;
shift;
}
# Аргументы 2: Обработка необязательного флага -NUMBER
if (OARGV && $ARGV[0] =~ /"-(\d+)$/) {
$columns = $1;
shift;
}
# Аргументы 3: Обработка сгруппированных флагов -a, -i -n, и -u
while (OARGV && $ARGV[0] ="" /"-(.+)/ & (shift, ($_ = $1), 1)) {
next if /"$/;
s/a// && (++$append, redo);
s/i// && (++$ignore_ints, redo);
s/n// && (++$nostdout, redo);
s/u// && (++$unbuffer, redo);
die "usage: $0 [-ainu] [filenames] ...\n";
ЕСЛИ не считать неявного перебора аргументов командной строки, о не выделяется ничем особенным. Продолжают действовать все специальные переменные, управляющие процессом ввода/вывода (см. главу 8). Переменная $/ определяет разделитель записей, а $. сод
undef $/;
while (<>) {
# Теперь в $_ находится полное содержимое файла, " ,
# имя которого хранится в $ARGV
}
}

Если значение $/ локализовано, старое значение автоматически восстанавливается при выходе из блока:
{ # Блок для local
local $/; # Разделитель записей становится неопределенным
while (<>) {
# Сделать что-то; в вызываемых функциях и значение $/ остается неопределенным
}
} # Восстановить $/
Поскольку при обработке файловые манипуляторы никогда не закрываются явно, номер записи $. не сбрасывается. Если вас это не устраивает, самостоятельно организуйте явное закрытие файлов для сброса $.:
while (<>) {
print "$ARGV:$.:$_";
close ARGV if eof;
}

Функция eof проверяет достижение конца файла при последней операции чтения. Поскольку последнее чтение выполнялось через манипулятор ARGV, eof сообщает, что мы находимся в конце текущего файла. В этом случае файл закрывается, а переменная $, сбрасывае Параметры командной строки В Perl предусмотрены специальные параметры командной строки - -n, -р и -i, упрощающие написание фильтров и однострочных программ. Параметр -п помещает исходный текст программы внутрь цикла while (<>). Обычно он используется в фильтрах типа дгер или программах, которые накапливают статистику по прочитанным данным. Пример 7.1. ffndlogini
#!/usr/bin/perl
# findlogini - вывести все строки, содержащие подстроку "login"
while (<>) { # Перебор файлов в командной строке print if /login/;
}
Программу из примера 7.1 можно записать так, как показано в примере 7.2.
Пример 7.2. rindlogin2
#!/usr/bin/perl -n
# findlogin2 - вывести все строки, содержащие подстроку "login"
print if /login/;

Параметр -n может объединяться с -е для выполнения кода Perl из командной строки:
% perl -ne 'print if /login/'
Параметр -р аналогичен -n, однако он добавляет print в конец цикла. Обычно он используется в программах для преобразования входных данных.
Пример 7.3. lowercasel
#!/usr/bin/perl
# lowercase - преобразование всех строк в нижний регистр
use locale;
while (<>) { # Перебор в командной строке
s/(["\WO-9_])/\l$1/g; # Перевод всех букв в нижний регистр
print;
}
Программу из примера 7.3 можно записать так, как показано в примере 7.4.
Пример 7.4. lowercase2
#!/usr/bin/perl -р
# lowercase - преобразование всех строк в нижний регистр
use locale;
s/(["\WO-9_])/\l$1/g; # Перевод всех букв в нижний регистр
Или непосредственно в командной строке следующего вида:
% perl -Miocale -pe 's/(["\WO-9_])/\1$1/g'
При использовании -п или -р для неявного перебора входных данных для всего цикла негласно создается специальная метка LINE:. Это означает, что из внутреннего цикла можно перейти к следующей входной записи командой next LINE (аналог next в awk). При закрыт Пример 7.5. countchunks
#!/usr/bin/perl -n
# countchunks - подсчет использованных слов
# с пропуском комментариев. При обнаружении __END__ или __DATA__
# происходит переход к следующему файлу.
for (split /\W+/) {
next LINE if /"#/;
close ARGV if /__(DATA|END)__/;
$chunks++:
} ED { print "Found $chunks chunks\n" }
В файле .history, создаваемым командным интерпретатором tcsh, перед каждой строкой указывается время, измеряемое в секундах с начала эпохи:
#+0894382237 less /etc/motd "+0894382239 vi '/.exrc
#+0894382242 date
#+0894382239 who
#+0894382288 telnet home

Простейшая однострочная программа приводит его к удобному формату:
%perl -pe 's/"#\+(\d+)\n/localtime($1) . " "/е'
Tue May 5 09:30:37 1998 less /etc/motd
Tue May 5 09:30:39 1998 vi "/.exi-c
Tue May 5 09:30:42 1998 date
Tue May 5 09:30:42 1998 who
Tue May 5 09:30:28 1998 telnet home

Параметр -i изменяет каждый файл в командной строке. Он описан в рецепте 7.9 и обычно применяется в сочетании с -р. Для работы с национальными наборами символов используется директива use locale.

> Смотри также --------------------------------
perlmn(1); рецепты 7.9; 16.6.

7.8. Непосредственная модификация файла с применением временной копии

Проблема

Требуется обновить содержимое файла на месте. При этом допускается применение временного файла.

Решение

Прочитайте данные из исходного файла, запишите изменения во временный файл и затем переименуйте временный файл в исходный:
open(OLD, "< $old") or die "can't open $old: $!";
open(NEW, "< $new") or die "can't open $new: $!";
select(NEW); N Новый файловый манипулятор,
# используемый print по умолчанию
while () {
# Изменить $_, затем...
print NEW $_ or die "can't write $new: $!";
}
close(OLD) or die "can't close $old: $!";
close(NEW) or die "can't close $new: $!";
rename($old, "$old,orig") or die "can't rename $old to $old.orig: $!";
rename($new, $old) or die "can't rename $new to Sold: $!";


Такой способ лучше всего приходит для обновления файлов "на месте". Комментарий Этот метод требует меньше памяти, чем другие подходы, не использующие временных файлов. Есть и другие преимущества - наличие резервной копии файла, надежность и простота программирования. Показанная методика позволяет внести в файл те же изменения, что и другие версии, не использующие временных файлов. Например, можно вставить новые строки перед 20-й строкой файла:
while () {
if ($. == 20) {
print NEW "Extra line 1\n";
print NEW "Extra line 2\n":
} print NEW $_;
}
Или удалить строки с 20 по 30:
while () {
next if 20 .. 30;
print NEW $_;
}


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

> Смотри также --------------------------------
Рецепты 7.1; 7.9-7.10

7.9. Непосредственная модификация файла с помощью параметра -i

Проблема

Требуется обновить файл на месте из командной строки, но вам лень' возиться с файловыми операциями из рецепта 7.8.

Решение

Воспользуйтесь параметрами -i и -р командной строки Perl. Запишите свою программу в виде строки: % perl -i.orig -p 'ФИЛЬТР' файл"! файл2 файлЗ ... Или воспользуйтесь параметрами в самой программе:
#!/usr/bin/perl -i.orig -p
# Фильтры

Комментарий

Параметр командной строки -i осуществляет непосредственную модификацию файлов. Он создает временный файл, как и в предыдущем рецепте, однако Perl берет на себя все утомительные хлопоты с слайдами. Используйте -i в сочетании с -р (см. рецепт 7.7), чтобы пр
% perl -pi.orig -e 's/DATE/localtime/e' в следующий фрагмент:
while (<>) {
if ($ARGV ne $oldargv) { # Мы перешли к следующему файлу?
rename($ARGV, $ARGV . '.orig');
open(ARGVOUT, ">$ARGV"); # Плюс проверка ошибок
select(ARGVOUT);
$oldargv = $ARGV;
} s/DATE/localtime/e;
} continue{


Конечно, имеется в виду лень творческая, а не греховная.
print;
} select (STDOUT); # Восстановить стандартный вывод
Параметр -i заботится о создании резервных копий (если вы не желаете сохранять исходное содержимое файлов, используйте -i вместо -i.orig), а -р заставляет Perl перебирать содержимое файлов, указанных в командной строке (или STDIN при их отсутствии). Приведенная выше однострочная программа приводит данные:

Dear Sir/Madam/Ravenous Beast,
As of DATE, our records show your account is overdue. Please settle by the end of the month. Yours in cheerful usury, --A. Moneylender
к следующему виду:

Dear Sir/Madam/Ravenous Beast,
As of Sat Apr 25 12:28:33 1998, our records show your account is overdue. Please settle by the end of the month. Yours in cheerful usury, --A. Moneylender
Этот параметр заметно упрощает разработку и чтение программ-трансляторов. Например, следующий фрагмент заменяет все изолированные экземпляры "hisvar" на "hervar" во всех файлах С, C++ и у асе:
%perl -i.old -pe 's{\bhisvar\b}{hervar}g' *.[Cchy]
%рег1 -i.old -ne 'print unless /"STARTS/ .. /"END$/' bigfile.text

Действие -i может включаться и выключаться с помощью специальной переменной $"1. Инициализируйте @ARGV и затем примените о так, как применили бы -i для командной строки:
# Организовать перебор файлов *.с в текущем каталоге,
# редактирование на месте и сохранение старого файла с расширением .orig
local $"I = '.orig'; # Эмулировать -i.orig
local @>ARGV = glob("*.c"); # Инициализировать список файлов
while (<>) {
if ($. - 1) {
print "This line should appear at the top of each file\n";
}
s/\b(p)earl\b/{1}erl/ig; # Исправить опечатки с сохранением регистра
print;
} continue {close ARGV if eof}

Учтите, что при создании резервной копии предыдущая резервная копня унпч тожается.

> Смотри также -------------------------------
Описание переменных $"1 и $. в perlvar(1) описание оператора . . в разделе "Range Operator" perlop(1); perlmn(1).

7.10. Непосредственная модификация файла без применения временного файла

Проблема

Требуется вставить, удалить или изменить одну или несколько строк файла. При этом вы не хотите (или не можете) создавать временный файл.

Решение

Откройте файл в режиме обновления ("+<"), прочитайте все его содержимое в массив строк, внесите необходимые изменения в массиве, после чего перезапишите файл и выполните усечение до текущей позиции.
open(FH, "+< file" or die "opening: $!";
@ARRAY = ;
# Модификация массива ARRAY
seek(FH,0,0) or die "Seeking: $!";
print FH OARRAY or die "Printing: $!";
truncate(FH,tell(FH)) or die "Truncating: $!";
close(FH) or die "Closing; $!";

Комментарий

Как сказано во введении, операционная система интерпретирует файлы как неструктурированные потоки байтов. Из-за этого вставка, непосредственная модификация или изменение отдельных битов невозможны (кроме особого случая, рассматриваемого в рецепте 8.13 - ф Чтение в память всего содержимого подходит для небольших файлов, но с большими возникают сложности. Попытка применить его для 800-мегабайтных файлов журналов на Web-сервере приведет либо к переполнению виртуальной памяти, либо общему сбою системы виртуаль
open(F, "+< $infile") or die "can't read $infile: $!";
$out = '';
while () {
s/DATE/localtime/eg;
$out .= $_, } seek(F, 0, 0) or die "Seeking: $!";
print F $out or die "Printing: $!";
truncate(F, tell(F)) or die "Truncating: $!";
close(F) or die "Closing: $!";
Другие примеры операций, которые могут выполняться на месте, приведены и рецептах главы 8. Этот вариант подходит лишь для самых решительных. Он сложен в написании, расходует больше памяти (теоретически - намного больше), не сохраняет резервной копии и может озадачить других программистов, которые попытаются читать данные из обновляемого файла. Если вы особо мнительны, не забудьте заблокировать файл.

> Смотри также --------------------------------
Описание функций seek, truncate, open и sysopen в perlfunc(1); рецепты 7.8-7.9.

7.11. Блокировка файла

Проблема

Несколько процессов одновременно пытаются обновить один и тот же файл.

Решение

Организуйте условную блокировку с помощью функции flock:

open(FH, "+< $path") or die "can't open $path: $!";
flock(FH,2) or die "can't flock $path: $!";
# Обновить файл, затем... close(FH)
or die "can't close $path: $!";

Комментарий

Операционные системы сильно отличаются по типу и степени надежности используемых механизмов блокировки. Perl старается предоставить программисту рабочее решение даже в том случае, если операционная система использует другой базовый механизм. Функция flock Символические константы LOCK_SH, LOCK_EX, LOCK_UN и LOCK_NB появились в модуле Fcnti лишь начиная с версии 5.004, но даже теперь они доступны лишь по специальному запросу с тегом : flock. Они равны соответственно 1, 2, 4 и 8, и эти значения можно использо

sub LOCK_SH() { 1 } # Совместная блокировка (для чтения)
sub LOCK_EX() { 2 } # Монопольная блокировка (для записи)
sub LOCK_NB() { 4 } # Асинхронный запрос блокировки
sub LOCK_UN() { 8 } # Снятие блокировки (осторожно!)


Блокировки делятся на две категории: совместные (shared) и монопольные (exclusive). Термин "монопольный" может ввести вас в заблуждение, поскольку процессы не обязаны соблюдать блокировку файлов. Иногда говорят, что flock реализует условную блокир вить все операции записи в файл до того момента, когда с ним закончит работу последний процесс чтения. Условная блокировка напоминает светофор на перекрестке. Светофор работает лишь в том случае, если люди обращают внимание на цвет сигнала: красный или зеленый - или желтый для условной блокировки. Красный цвет не останавливает движение; он всего лишь сообщ Добропорядочный процесс сообщает о своем намерении прочитать данные из файла, запрашивая блокировку LOCK_SH. Совместная блокировка файла может быть установлена сразу несколькими процессами, поскольку они (предположительно) не будут изменять данные. Если п Функция flock по умолчанию приостанавливает процесс. Указывая флаг LOCK_NB, при запросе можно получить блокировку без приостановки. Благодаря этому можно предупредить пользователя об ожидании снятия блокировок другими процессами:
unless (flock(FH, LOCK_EX|LOCK_NB)) {
warn "can't immediately write-lock the file ($!), blocking ...";
unless (flock(FH, LOCK_EX)) {
die "can't get write-lock on numfile: $!"; }
}

Если при использовании LOCK_NB вам было отказано в совместной блокировке, следовательно, кто-то другой получил LOCK_EX и обновляет файл. Отказ в монопольной блокировке означает, что другой процесс установил совместную или монопольную блокировку, поэто Блокировки исчезают с закрытием файла, что может произойти лишь после завершения процесса. Ручное снятие блокировки без закрытия файла - дело рискованное. Это связано с буферизацией. Если между снятием блокировки и очисткой буфера проходит некоторое время
if ($] < 5.004) { # Проверить версию perl
my $old_fh = select(FH);
local $|=1; # Разрешить буферизацию команд
local $\ = ''; # Очистить разделитель выходных записей
print ""; # Вызвать очистку буфера
select($old_fh); # Восстановить предыдущий манипулятор
}
flock(FH, LOCK_UN);
До появления Perl версии 5.004 очистку буфера приходилось выполнять принудительно. Программисты часто забывали об этом, поэтому в 5.004 снятие блокировки изменилось так, чтобы несохраненные буферы очищались непосредственно перед снятием блокировки. А вот как увеличить число в файле с применением flock:
use Fcnti qw(:DEFAULT :flock);
sysopen(FH, "numfile", 0_RDWR|0_CREAT)
or die "can't open numfile: $!";
flock(FH, LOCK_EX) or die "can't write-lock numfile: $!";

# Блокировка получена, можно выполнять ввод/вывод

$num = || 0; # HE ИСПОЛЬЗУЙТЕ "or" ! !
seek(FH, 0, 0) or die "can't rewind numfile : $!";
truncate(FH, 0) or die "can't truncate numfile: $!";
print FH $num+1, "\n" or die "can't write numfile: $!";
close(FH) or die "can't close numfile: $!":


Закрытие файлового манипулятора приводит к очистке буферов и снятию блокировки с файла. Функция truncate описана в главе 8. С блокировкой файлов дело обстоит сложнее, чем можно подумать - и чем нам хотелось бы. Блокировка имеет условный характер, поэтому если один процесс использует ее, а другой - нет, все идет прахом. Никогда не используйте факт существования файла в качестве В блокировках NFS участвует как сервер, так и клиент. Соответственно, нам не известен общий механизм, гарантирующий надежную блокировку в NFS. Это возможно в том случае, если некоторые операции заведомо имеют атомарный характер в реализации сервера или кл Не путайте функцию Perl flock с функцией SysV lockf. В отличие от lockf flock блокирует сразу весь файл. Perl не обладает непосредственной поддержкой lockf. Чтобы заблокировать часть файла, необходимо использовать функцию fcnti (см. программу lockarea в к

> Смотри также --------------------------------
писание функций flock и fcnti в perlfunc(1) документация по стандартным модулям Fcnti и DB_File; рецепт 7.21-7.22.

7.12. Очистка буфера

Проблема

Операция вывода через файловый манипулятор выполняется не сразу. Из-за этого могут возникнуть проблемы в сценариях CGI на некоторых Web-серверах, враждебных по отношению к программисту. Если Web-сервер получит предупреждение от Perl до того, как увидит (б

Решение

Запретите бусреризацию, присвоив истинное значение (обычно 1) переменной $ | на уровне файлового манипулятора:

$old_fh = select(OUTPUT_HANDLE);
$1 = 1;
select($old_fh);
Или, если вас не пугают последствия, вообще запретите буферизацию вызовом метода autoflush из модулей 10:
use 10::Handle;
OUTPUT_HANDLE->autoflush( 1);

Комментарий

В большинстве реализации stdio буферизация определяется типом выходного устройства. Для дисковых файлов применяется блочная буферизация с размером буфера, превышающим 2 Кб. Для каналов (pipes) и сокетов часто при меняется буфер размера от 0,5 до 2 Кб. Пос Функция Perl print не поддерживает по-настоящему небуферизованного вывода - физической записи каждого отдельного символа. Вместо этого поддерживается командная буферизация, при которой физическая запись выполняется после каждой отдельной команды вывода. П Для управления буферизацией вывода используется специальная переменная $|. Присваивая ей true, вы тем самым разрешаете командную буферизацию. На ввод она не влияет (небуферизованный ввод рассматривается в рецептах 15.6 и 15.8). Если $| присваивается false, будет использоваться стандартная буферизация stdio. Отличия продемонстрированы в примере 7.6. Пример 7.6. seeme

#!/usr/bin/perl -w
# seeme - буферизация вывода в stdio
$| = (@ARGV > 0); # Командная буферизация при наличии аргументов
print "Now you don't see it...";
sleep 2;
print "now you do\n";


Если программа запускается без аргументов, STDOUT не использует командную буферизацию. Терминал (консоль, окно, сеанс telnet и т. д.) получит вывод лишь после завершения всей строки, поэтому вы ничего не увидите в течение 2 секунд, после чего буде В сомнительном стремлении к компактности кода программисты включают возвращаемое значение select (файловый манипулятор, который был выбран в настоящий Момент) в другой вызов select:

select((select(OUTPuT_HANDLE), $| = 1)[0]);
Существует и другой выход. Модули FileHandle и 10 содержат метод autoflush. Его вызов с аргументом true или false (по умолчанию используется true) управляет автоматической очисткой буфера для конкретного выходного манипулятора:
use FileHandle;
STDERR->autoflush; # Уже небуферизован в stdio
$filehandle->autoflush(0);


Если вас не пугают странности косвенной записи (см. главу 13 "Классы, объекты и связи"), можно написать нечто похожее на обычный английский текст:
use 10::Handle;
# REMOTE_CONN - манипулятор интерактивного сокета,
# a DISK_FILE - манипулятор обычного файла.
autoflush REMOTE_CONN 1; # Отказаться от буферизации для ясности
autoflush DISK_FILE 0; # Буферизовать для повышения быстродействия
Мы избегаем жутких конструкций select, и программа становится более понятной. К сожалению, при этом увеличивается время компиляции, поскольку включение модуля IO::Handle требует чтения и компиляции тысяч строк кода. Научитесь напрямую работать с $ |, этог Чтобы выходные данные оказались в нужном месте в нужное время, необходимо позаботиться о своевременной очистке буфера. Это особенно важно для соке-тов, каналов и устройств, поскольку они нередко участвуют в интерактивном вводе/выводе, а также из-за того, Пример 7.7. getcomidx

#!/usr/bin/perl
# getpcomidx - получить документ index.html с www.perl.com
use 10::Socket;
$sock = new 10::Socket::INET (PeerAddr => 'www.perl.com', PeerPort => 'http(80)');
die "Couldn't create socket: $@>" unless $sock;
# Библиотека не поддерживает $!; в ней используется $@
$sock->autoflush(1);
# На Mac \n\n "обязательно* заменяется последовательностью \015\012\015\012.
# Спецификация рекомендует это и для других систем,
# однако в реализациях рекомендуется поддерживать и "\cJ\cJ".
# Наш опыт показывает, что именно так и получается.
$sock->print("GET /index.html http/1.1\n\n");
$document = join('', $sock->getlines());
print "DOC IS: $document\n";


Ни один из рассмотренных нами типов буферизации не позволяет управлять буферизацией ввода. Для этого обращайтесь к рецептам 15.6 и 15.8.

> Смотри также -------------------------------
Описание переменной $ | в perlvar(1); описание функции select в perlfunc(1) документация по стандартным модулям FileHandle и IO::Handle.

7.13. Асинхронное чтение из нескольких манипуляторов

Проблема

Вы хотите узнавать о наличии данных для чтения, вместо того чтобы приостанавливать процесс в ожидании ввода, как это делает о. Такая возможность пригодится при получении данных от каналов, сокетов, устройств и других программ. Решение Если вас не смущают операции с битовыми векторами, представляющими наборы файловых дескрипторов, воспользуйтесь функцией select с нулевым тайм аутом:
$rin = ' o;

# Следующая строка повторяется для всех опрашиваемых манипуляторов
vec($rin, fileno(FH-l), 1) = 1:
vec($rin, fileno(FH2), 1) = 1;
vec($rin, fileno(FH3), 1) = 1;
$nfound = select($rout=$rin, undef, undef, 0);
if ($nfound) { # На одном или нескольких манипуляторах имеются входные данные
if (vec($r,fileno(FH1),1)) {
# Сделать что-то с FH1 } if (vec($r,fileno(FH2),1)) {
it Сделать что-то с FH2 } if (vec($r,fileno(FH3),1)) {
# Сделать что-то с FH3
}
}

Модуль IO::Select позволяет абстрагироваться от операций с битовыми векторами:
use 10::Select;
$select = 10::Select->new();
# Следующая строка повторяется для всех опрашиваемых манипуляторов
$select->add(*FILEHANDLE):
if (@>ready = $select->can_read(0)) { # Имеются данные на манипуляторах из массива
@ready }

Комментарий

Функция select в действительности объединяет сразу две функции. Вызванная с одним аргументом, она изменяет текущий манипулятор вывода по умолчанию (см. рецепт 7.12). При вызове с четырьмя аргументами она сообщает, какие файловые манипуляторы имеют входные Первые три аргумента select представляют собой строки, содержащие битовые векторы. Они определяют состояние файловых дескрипторов, ожидающих ввода, вывода или сообщений об ошибках (например, сведений о выходе данных за пределы диапазона для срочной переда

$rin = o o;
vec($rin, fileno(FILEHANDLE), 1) = 1;
$nfound = select($rin, undef, undef, 0); # Обычная проверка
if ($nfound) {
$line = ;
print "I read $line";
}

Однако такое решение не идеально. Если среди передаваемых символов не встретится символ перевода строки, программа переходит в ожидание в . Чтобы справиться с этой проблемой, мы последовательно читаем по одному символу и обрабатываем готовую строку при получении "\п". При этом отпадает необходимость в синхронном вызове . Другое решение (без проверки файлов) описано в рецепте 7.15. Модуль IO::Select скрывает от вас операции с битовыми векторами. Метод 10: : Select ->new() возвращает новый объект, для которого можно вызвать метод add, чтобы дополнить набор новыми файловыми манипуляторами. После включениях всех интересующих вас манипу Вызовы 4-аргументной версии select не должны чередоваться с вызовами каких-либо функций буферизованного вывода, перечисленных во введении (read, о, seek, tell и т. д.). Вместо этого следует использовать sys read - вместе с sysseek, если вы хотите изменить Чтение данных из сокета или канала с немедленным продолжением работы описано в рецепте 17.13. Асинхронному чтению с терминала посвящены рецепты 15.6 и 15.8.

[> Смотри также --------------------------------
Описание функции select в perlfunc(1); документация по стандартному модулю IO::Select; рецепт 7.14.


© copyright 2000 Soft group
Используются технологии uCoz