Как читать записи фиксированной длины в 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. Существуют полезные варианты в списке аргументов и в формате шаблона разделителя.