如何在Perl中拆分固定宽度的列?

时间:2009-12-12 15:57:44

标签: perl

编程对我来说是如此新鲜,我为不知道如何表达问题而道歉。

我有一个Perl脚本,它从内部工具获取变量。这并不总是它看起来像,但它将始终遵循这种模式:

darren.local           1987    A      Sentence1
darren.local           1996    C      Sentence2
darren.local           1991    E      Sentence3
darren.local           1954    G      Sentence4
darren.local           1998    H      Sentence5

使用Perl,将每条线路变为变量的最简单方法是什么?根据内部工具吐出的内容,每条线总是不同的,并且可以有超过五条线。每行中的大写字母最终将被排序(所有As,所有C,所有Es等)。我应该看正则表达式吗?

5 个答案:

答案 0 :(得分:18)

我喜欢使用unpack来做这类事情。它快速,灵活,可逆。

您只需要知道每列的位置,unpack可以自动修剪每列的额外空格。

如果您更改其中一列中的某些内容,则可以通过使用相同格式重新打包来轻松打包到原始格式:

my $format = 'A23 A8 A7 A*';

while( <DATA> ) {
    chomp( my $line = $_ );

    my( $machine, $year, $letter, $sentence ) =
        unpack( $format, $_ );

    # save the original line too, which might be useful later
    push @grades, [ $machine, $year, $letter, $sentence, $_ ];
    }

my @sorted = sort { $a->[2] cmp $b->[2] } @grades;

foreach my $tuple ( @sorted ) {
    print $tuple->[-1];
    }

# go the other way, especially if you changed things
foreach my $tuple ( @sorted ) {
    print pack( $format, @$tuple[0..3] ), "\n";
    }

__END__
darren.local           1987    A      Sentence1
darren.local           1996    C      Sentence2
darren.local           1991    E      Sentence3
darren.local           1954    G      Sentence4
darren.local           1998    H      Sentence5

现在,还有一个额外的考虑因素。听起来你可能在一个变量中有这么大的多行文本。通过在标量引用上打开文件句柄来处理文件。文件句柄可以处理剩下的事情:

 my $lines = '...multiline string...';

 open my($fh), '<', \ $lines;

 while( <$fh> ) {
      ... same as before ...
      }

答案 1 :(得分:3)

use strict;
use warnings;

# this puts each line in the array @lines
my @lines = <DATA>; # <DATA> is a special filehandle that treats
                    # everything after __END__ as if it was a file
                    # It's handy for testing things

# Iterate over the array of lines and for each iteration
# put that line into the variable $line
foreach my $line (@lines) {
   # Use split to 'split' each $line with the regular expression /s+/
   # /s+/ means match one or more white spaces.
   # the 4 means that all whitespaces after the 4:th will be ignored
   # as a separator and be included in $col4
   my ($col1, $col2, $col3, $col4) = split(/\s+/, $line, 4);

   # here you can do whatever you need to with the data
   # in the columns. I just print them out
   print "$col1, $col2, $col3, $col4 \n";
}


__END__
darren.local           1987    A      Sentece1
darren.local           1996    C      Sentece2
darren.local           1991    E      Sentece3
darren.local           1954    G      Sentece4
darren.local           1998    H      Sentece5

答案 2 :(得分:2)

假设文本被放入单个变量$ info中,那么您可以使用内部perl split函数将其拆分为单独的行:

my @lines = split("\n", $info); 

其中@lines是你的行数组。 “\ n”是换行符的正则表达式。您可以按如下方式遍历每一行:

foreach (@lines) {
   $line = $_;
   # do something with $line....  
}

然后你可以在空格上分割每一行(正则表达式\ s +,其中\ s是一个空白字符,而+表示1次或更多次):

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

然后您可以通过其数组索引直接访问每个字段:$ field [0],$ field [1]等。

或者,您可以这样做:

($var1, $var2, $var3, $var4) = split("\s+", $line);

将把每行中的字段放入单独的命名变量中。

现在 - 如果你想按第三列中的字符排序你的行,你可以这样做:

my @lines = split("\n", $info); 
my @arr = ();    # declare new array

foreach (@lines) {
   my @fields = split("\s+", $_);
   push(@arr, \@fields)    # add @fields REFERENCE to @arr 
}

现在你有了一个“数组数组”。这可以很容易地按如下方式排序:

@sorted = sort { $a->[2] <=> $b->[2] } @arr;

将按@fields的第3个元素(索引2)对@arr进行排序。

编辑2 要将具有相同第三列的行放入自己的变量中,请执行以下操作:

my %hash = ();             # declare new hash

foreach $line (@arr) {     # loop through lines
  my @fields = @$line;     # deference the field array

  my $el = $fields[2];     # get our key - the character in the third column

  my $val = "";
  if (exists $hash { $el }) {         # check if key already in hash
     my $val = $hash{ $el };        # get the current value for key
     $val = $val . "\n" . $line;    # append new line to hash value         
  } else {
     $val = $line;
  }
  $hash{ $el } = $val;         # put the new value (back) into the hash
}

现在您有一个用第三列字符键入的哈希值,每个键的值是包含该键的行。然后,您可以遍历哈希并打印输出或以其他方式使用哈希值。

答案 3 :(得分:0)

对于每行文字都是这样的:

my ($domain, $year, $grade, @text) = split /\s+/, $line;

我使用数组作为句子,因为不清楚末尾的句子是否有空格。然后,您可以根据需要将@text数组加入到新字符串中。如果最后的句子没有空格,那么你可以将@text变成$ text。

答案 4 :(得分:-1)

使用CPAN和我的模块DataExtract::FixedWidth

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

my @rows = <DATA>;

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

use Data::Dumper;

print Dumper $defw->parse( $_ ) for @rows;

__DATA__
darren.local           1987    A      Sentence1
darren.local           1996    C      Sentence2
darren.local           1991    E      Sentence3
darren.local           1954    G      Sentence4
darren.local           1998    H      Sentence5

没有那么简单。

相关问题