Как читать записи фиксированной длины в Perl? - PullRequest
12 голосов
/ 02 января 2009

Как лучше всего читать записи фиксированной длины в Perl. Я знаю, чтобы прочитать файл, как:

ABCDE 302
DEFGC 876

Я могу сделать

while (<FILE>) {
   $key = substr($_, 0, 5);
   $value = substr($_, 7, 3);
}

но разве нет способа сделать это с помощью чтения / распаковки?

Ответы [ 5 ]

18 голосов
/ 02 января 2009
my($key, $value) = unpack "A5 A3";    # Original, but slightly dubious

Нам обоим нужно проверить параметры на странице справки unpack (и, в частности, на странице справки pack ).

Поскольку оператор пакета A удаляет конечные пробелы, ваш пример может быть закодирован как:

my($key, $value) = unpack "A6A3";

В качестве альтернативы (это Perl, поэтому TMTOWTDI):

my($key, $blank, $value) = unpack "A5A1A3";

1 является необязательным, но систематическим и симметричным. Одним из преимуществ этого является то, что вы можете проверить, что $blank eq " ".

12 голосов
/ 02 января 2009

Обновление: для окончательного ответа см. Ответ Джонатана Леффлера ниже.

Я бы не использовал это только для двух полей (я бы использовал pack / unpack напрямую), но для 20 или 50 или около того полей мне нравится использовать Parse :: FixedLength (но я пристрастен). Например. (для вашего примера) (Обновление: также вы можете использовать $ / и <> в качестве альтернативы чтению ($ fh, $ buf, $ buf_length) ... см. ниже):

use Parse::FixedLength;

my $pfl = Parse::FixedLength->new([qw(
  key:5
  blank:1
  value:3
)]);
# Assuming trailing newline
# (or add newline to format above and remove "+ 1" below)
my $data_length = $pfl->length() + 1;

{
  local $/ = \$data_length;
  while(<FILE>) {
    my $data = $pfl->parse($_);
    print "$data->{key}:$data->{value}\n";
    # or
    print $data->key(), ":", $data->value(), "\n";
  }
}

Существуют некоторые похожие модули, которые делают упаковку / распаковку более «дружественной» (см. Раздел «Смотрите также» в Parse :: FixedLength).

Обновление: вау, это должен был быть альтернативный ответ, а не официальный ответ ... ну, так как это то, чем он является, я должен включить некоторые из более прямого кода Джонатана Леффлера, который, вероятно, вам следует обычно делают это (см. pack / unpack docs и узел Джонатана Леффлера ниже):

$_ = "ABCDE 302";
my($key, $blank, $value) = unpack "A5A1A3";
6 голосов
/ 02 января 2009

Предположим, 10 записей символов из двух полей по пять символов на запись:

open(my $fh, "<", $filename) or die $!;
while(read($fh, $buf, 10)) {
  ($field1, $field2) = unpack("A5 A5", $buf);
  # ... do something with data ...
}
0 голосов
/ 19 февраля 2013

Вот еще один способ сделать это:

while (<FILE>)
{
    chomp;
    if (/^([A-Z]{5}) ([0-9]{3})$/)
    {
        $key = $1;
        $value = $2;
    }
}
0 голосов
/ 02 января 2009

Независимо от того, имеют ли ваши записи и поля фиксированную длину, если поля разделены одинаковыми разделителями (такими как пробел или запятая), вы можете использовать функцию split проще, чем распаковывать.

my ($field1, $field2) = split / /;

Посмотрите документацию по разделению. В списке аргументов и в формате шаблона разделителя есть полезные варианты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...