Как я могу прочитать, проанализировать, а затем «не читать» и перечитать начало потока ввода в Perl? - PullRequest
1 голос
/ 30 октября 2010

Я читаю и обрабатываю поток ввода из файлового дескриптора ARGV в Perl (то есть конструкции while(<>)) обычного файлового дескриптора, который может быть STDIN. Однако мне необходимо проанализировать значительную часть входных данных, чтобы определить, в каком из четырех разных, но чрезвычайно похожих форматов он кодируется (различные кодировки ASCII показателей качества FASTQ; см. здесь ). После того, как я решил, в каком формате находятся данные, мне нужно вернуться и проанализировать эти строки во второй раз, чтобы фактически прочитать данные.

Так что мне нужно прочитать первые 500 строк или около того потока дважды. Или, чтобы взглянуть на это иначе, мне нужно прочитать первые 500 строк, а затем «положить их обратно», чтобы я мог прочитать их снова. Поскольку я могу читать из STDIN, я не могу просто вернуться к началу. И файлы огромные, поэтому я не могу просто прочитать все в память (хотя чтение этих первых 500 строк в память нормально). Какой лучший способ сделать это?

В качестве альтернативы, можно ли как-то дублировать входной поток?

Редактировать: Подождите минуту. Я только что понял, что больше не могу обрабатывать ввод как один большой поток, потому что мне нужно определять формат каждого файла независимо. Так что я не могу использовать ARGV. Однако остальная часть вопроса остается в силе.

Ответы [ 2 ]

2 голосов
/ 30 октября 2010

Как вы сказали, если дескриптор файла может быть STDIN, вы не можете использовать seek для его перемотки. Но это все еще довольно просто. Я бы не стал возиться с модулем:

my @lines;

while (<$file>) {
  push @lines, $_;
  last if @lines == 500;
}

... # examine @lines to determine format

while (defined( $_ = @lines ? shift @lines : <$file> )) {
  ... # process line
}

Помните, что в этом случае вам нужен явный defined, потому что особый случай, который добавляет неявный defined к некоторым while циклам, не применяется к этому более сложному выражению.

1 голос
/ 30 октября 2010

Там - это a Модуль CPAN , который предоставляет метод unread для класса IO::Handle.Тем не менее, его предупреждения делают один несколько осторожным.Я бы тщательно оценил его пригодность.

Если вам действительно нужно всего лишь сэкономить 500 строк, каждая из которых достаточно короткая, этого модуля может быть достаточно;в его примере используется STDIN.

Однако я нервничаю из-за магии ARGV.Если ваш оператор <> вызывает открытие и чтение нескольких отдельных файлов, то я не знаю, что вы сможете создать резервную копию файла, отличного от того, который открыт в данный момент.

Вы могли бы в конечном итоге просто написать логику возврата.Либо это, либо наложение каких-то ограничений на обработку ARGV, связанных с несколькими входными файлами и / или характером STDIN.

Большинство моих программ с магической обработкой ARGV имеют в начале защиту, которая читает что-то вроде

if (@ARGV == 0 && -t STDIN) {
    # select one or the other of the next two lines:

    # opt 1: emit warning 
    warn "$0: reading stdin from /dev/tty\n";

    # opt 2: populate @ARGV
    @ARGV = grep { -f && -T } <*>;  # glob plain textfiles

 }

Во втором вышеприведенном случае, где по умолчанию используются все обычные текстовые файлы в текущем каталоге, следует также решить, что делать, если grep создает пустой список.

Для некоторых программ, которые ожидают или, по крайней мере, допускают аргументы каталога, вместо этого я иногда инициализирую пустой @ARGV - ".", чтобы программа по умолчанию использовала текущий рабочий каталог процесса.

...