我有一个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等)。我应该看正则表达式吗?
答案 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
没有那么简单。