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

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

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

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

    ABCDE 302
    DEFGC 876
    

    我可以

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

    但是,有没有一种方法可以通过read/uckap来做到这一点呢?

    5 回复  |  直到 17 年前
        1
  •  12
  •   community wiki 9 revs runrig    17 年前
    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 " " .

        2
  •  18
  •   Jonathan Leffler    17 年前

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

    我不会只在两个字段中使用它(我会使用 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的“另见”部分)。

    更新:哇,这是一个替代答案,而不是官方答案。..好吧,既然它就是这样,我应该包括一些Jonathan Leffler更直接的代码,这可能是你通常应该做的(见 包装 / 打开 docs和下面的Jonathan Leffler节点):

    $_ = "ABCDE 302";
    my($key, $blank, $value) = unpack "A5A1A3";
    
        3
  •  6
  •   Michael Cramer    17 年前

    假设每条记录包含两个五字符字段的10个字符记录:

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

    以下是另一种方法:

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

    无论您的记录和字段是否为固定长度,如果字段由统一的分隔符(如空格或逗号)分隔,则使用split函数比解包更容易。

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

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