如何在Perl中从固定宽度格式中提取列?

时间:2009-09-29 20:02:30

标签: perl fixed-width

我正在编写一个Perl脚本来运行并获取各种数据元素,例如:

1253592000
1253678400                 86400                 6183.000000
1253764800                 86400                 4486.000000 
1253851200  36.000000      86400                10669.000000
1253937600  0.000000       86400                 9126.000000
1254024000  0.000000       86400                 2930.000000
1254110400  0.000000       86400                 2895.000000
1254196800  0.000000                             8828.000000

我可以抓住这个文本文件的每一行没问题。

我正在使用正则表达式来抓取每个字段。一旦我在一个变量中有一行,即$ line - 我怎样才能抓住每个字段并将它们放入自己的变量中,即使它们有不同的分隔符?

6 个答案:

答案 0 :(得分:13)

此示例说明如何使用空格作为分隔符(split)或使用固定列布局(unpack)来分析行。使用unpack如果您使用大写字母(A10等),则会删除空格。 注意:正如brian d foy指出的那样,split方法对于缺少字段的情况(例如,第二行数据)不起作用,因为字段位置信息将迷路;除非我们误解您的数据,否则unpack是前往此处的方式。

use strict;
use warnings;

while (my $line = <DATA>){
    chomp $line;
    my @fields_whitespace = split m'\s+', $line;
    my @fields_fixed = unpack('a10 a10 a12 a28', $line);
}

__DATA__
1253592000                                                  
1253678400                 86400                 6183.000000
1253764800                 86400                 4486.000000
1253851200 36.000000       86400                10669.000000
1253937600  0.000000       86400                 9126.000000
1254024000  0.000000       86400                 2930.000000
1254110400  0.000000       86400                 2895.000000
1254196800  0.000000                             8828.000000

答案 1 :(得分:3)

使用my module DataExtract::FixedWidth。对于在perl中使用Fixed Width列,它是功能最全,经过充分测试的。如果速度不够快,您可以传入unpack_string,无需启发式检测边界。

#!/usr/bin/env perl
use strict;
use warnings;
use DataExtract::FixedWidth;
use feature ':5.10';

my @rows = <DATA>;
my $de = DataExtract::FixedWidth->new({
  heuristic => \@rows
  , header_row => undef
});

say join ('|',  @{$de->parse($_)}) for @rows;

    --alternatively if you want header info--

my @rows = <DATA>;
my $de = DataExtract::FixedWidth->new({
  heuristic => \@rows
  , header_row => undef
  , cols => [qw/timestamp field2 period field4/]
});

use Data::Dumper;
warn Dumper $de->parse_hash($_) for @rows;

__DATA__
1253592000
1253678400                 86400                 6183.000000
1253764800                 86400                 4486.000000
1253851200  36.000000      86400                10669.000000
1253937600  0.000000       86400                 9126.000000
1254024000  0.000000       86400                 2930.000000
1254110400  0.000000       86400                 2895.000000
1254196800  0.000000                             8828.000000

答案 2 :(得分:0)

我不确定列名称和格式,但您应该可以使用Text::FixedWidth

根据自己的喜好调整此配方
use strict;
use warnings;
use Text::FixedWidth;

my $fw = Text::FixedWidth->new;
$fw->set_attributes(
    qw(
        timestamp undef  %10s
        field2    undef  %10s
        period    undef  %12s
        field4    undef  %28s
        )
);

while (<DATA>) {
    $fw->parse( string => $_ );
    print $fw->get_timestamp . "\n";
}

__DATA__
1253592000
1253678400                 86400                 6183.000000
1253764800                 86400                 4486.000000
1253851200 36.000000       86400                10669.000000
1253937600  0.000000       86400                 9126.000000
1254024000  0.000000       86400                 2930.000000
1254110400  0.000000       86400                 2895.000000
1254196800  0.000000                             8828.000000

答案 3 :(得分:-1)

您可以分割线条。看来您的分隔符只是空格?您可以按以下顺序执行操作:

@line = split(" ", $line);

这将匹配所有空格。然后,您可以通过$ line [0],$ line [1]等进行边界检查和访问每个字段。

Split也可以采用正则表达式而不是字符串作为分隔符。

@line = split(/\s+/, $line);

这可能会做同样的事情。

答案 4 :(得分:-1)

如果所有字段都具有相同固定宽度并使用空格格式化,则可以使用以下split

@array = split / {1,N}/, $line;

其中N是该字段的含义。这将为每个空场产生一个空间。

答案 5 :(得分:-2)

固定宽度分隔可以这样完成:

my @cols;
my %header;
$header{field1} = 0; // char position of first char in field
$header{field2} = 12;
$header{field3} = 15;

while(<IN>) {

   print chomp(substr $_, $header{field2}, $header{field3}); // value of field2 


}

我的Perl非常生疏,所以我确信那里存在语法错误。但这就是它的要点。