Как читать записи фиксированной длины в Perl?

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

ABCDE 302
DEFGC 876

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

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

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

Ответ 1

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

Я бы не использовал это только для двух полей (я бы использовал pack/unpack напрямую, но для 20 или 50 полей мне нравится использовать Parse:: FixedLength (но я предвзятый). Например. (для вашего примера) (Update: также вы можете использовать $/и < > как альтернативу чтению ($ 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 и Джонатан Леффлер node ниже):

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

Ответ 2

my($key, $value) = unpack "A5 A3";    # Original, but slightly dubious

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

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

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

Альтернативно (это Perl, поэтому TMTOWTDI):

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

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

Ответ 3

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

open(my $fh, "<", $filename) or die $!;
while(read($fh, $buf, 10)) {
  ($field1, $field2) = unpack("A5 A5", $buf);
  # ... do something with data ...
}

Ответ 4

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

while (<FILE>)
{
    chomp;
    if (/^([A-Z]{5}) ([0-9]{3})$/)
    {
        $key = $1;
        $value = $2;
    }
}

Ответ 5

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

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

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