代码之家  ›  专栏  ›  技术社区  ›  David Nehme

如何在Perl中读取固定长度的记录?

  •  12
  • David Nehme  · 技术社区  · 16 年前

    用Perl读取固定长度记录的最佳方法是什么?我知道要读一个文件,比如:

    ABCDE 302
    DEFGC 876
    

    我能做到

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

    但是,是否有一种方法可以通过读/解包来实现这一点?

    5 回复  |  直到 12 年前
        1
  •  12
  •   community wiki 9 revs runrig    15 年前

    更新:有关最终答案,请参阅下面的乔纳森·莱弗勒的答案。

    我不会只在两个字段中使用这个(我会使用 pack / unpack 直接),但对于20或50个左右的字段,我喜欢使用 Parse::FixedLength (但我有偏见)。例如(例如)(更新:您也可以使用$/和<>作为读取($fh、$buf、$buf\u-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";
      }
    }
    

    有一些类似的模块使pack/unpack更加“友好”(请参阅parse::fixedlength的“see also”部分)。

    更新:哇,这是一个备选答案,而不是正式答案……好吧,既然是这样,我应该包括乔纳森·莱弗勒的一些更直接的代码,这很可能是你通常应该怎么做(见 包装 / 打开 下面是Docs和Jonathan Leffler的节点):

    $_ = "ABCDE 302";
    my($key, $blank, $value) = unpack "A5A1A3";
    
        2
  •  18
  •   Jonathan Leffler    16 年前
    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
  •  6
  •   Michael Cramer    16 年前

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

    open(my $fh, "<", $filename) or die $!;
    while(read($fh, $buf, 10)) {
      ($field1, $field2) = unpack("A5 A5", $buf);
      # ... do something with data ...
    }
    
        4
  •  -1
  •   ruben2020    12 年前

    还有另一种方法:

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

    无论记录和字段的长度是否固定,如果字段由统一的分隔符(如空格或逗号)分隔,您都可以比解包更轻松地使用拆分函数。

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

    查找要拆分的文档。参数列表和分隔符模式的格式上存在有用的变体。