12

在 Perl 中读取固定长度记录的最佳方法是什么。我知道要读取如下文件:

ABCDE 302
DEFGC 876

我可以

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

但是没有办法通过读取/解包来做到这一点吗?

4

5 回答 5

18
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 " ".

于 2009-01-02T17:06:20.433 回答
12

更新:有关明确的答案,请参阅下面 Jonathan Leffler 的答案。

我不会只将它用于两个字段(我会直接使用pack / unpack),但对于 20 或 50 个左右的字段,我喜欢使用Parse::FixedLength(但我有偏见)。例如(对于您的示例)(更新:另外,您可以使用 $/ 和 <> 作为替代 read($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 的“另见”部分)。

更新:哇,这是一个替代答案,而不是官方答案......好吧,既然它就是这样,我应该包括一些乔纳森莱弗勒更直接的代码,这可能是你通常应该做的(请参阅下面的打包/解包文档和 Jonathan Leffler 的节点):

$_ = "ABCDE 302";
my($key, $blank, $value) = unpack "A5A1A3";
于 2009-01-02T20:53:44.717 回答
6

假设每条记录有 10 条字符记录,每条记录有两个 5 字符字段:

open(my $fh, "<", $filename) or die $!;
while(read($fh, $buf, 10)) {
  ($field1, $field2) = unpack("A5 A5", $buf);
  # ... do something with data ...
}
于 2009-01-02T18:28:37.803 回答
-1

这是另一种方法:

while (<FILE>)
{
    chomp;
    if (/^([A-Z]{5}) ([0-9]{3})$/)
    {
        $key = $1;
        $value = $2;
    }
}
于 2013-02-19T19:20:47.840 回答
-2

不管你的记录和字段是否是定长的,如果字段用统一的分隔符(如空格或逗号)分隔,你可以使用 split 功能比 unpack 更容易。

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

查找拆分的文档。参数列表和分隔符模式的格式有一些有用的变化。

于 2009-01-02T17:31:46.607 回答