我有一个简单的数据文件。文件中的每一行都有四个元素。有些行没有空白项。其他行有第一个条目,其余三个为空白,或者更确切地说用空格"填充"。它是一个以制表符分隔的文件。
输入文件示例:
.
.
.
30 13387412 34.80391242 sSN_FIRST
30 13387412 34.80391242 sSN5_40
30.1
30.2
30.3
30.4
31 14740248 65.60590089 s32138223_44
31 14740248 65.60590089 s321382_LAST
.
.
.
重申一下,我的文件中的"blanks"实际上包含一个空格,如果这很重要的话。
我的总体目标是在整个文件中"填充"第二列和第三列(第四列被忽略)。为了做到这一点,我需要我的脚本来识别一组连续的空白行,加上紧接在这组连续空白行的前一行和后一行。在上面的例子中,这将是第2 - 7行。一旦我能做到这一点,我就可以使用侧边行中的信息来帮助"填充"中间行中缺失的条目。
我一直在试验until
函数,但我没有成功地将它与一行一行地读取数据的循环耦合起来。例如,我可以读取行并找到空白行:
open( my $FILE, "<$mapfile" );
my @file = <$FILE>;
close $FILE;
for ( my $i = 1 ; $i < scalar @file ; $i++ )
{
my @entries = split( 't', $file[ $i ] );
if ( $entries[ 1 ] =~ m/ / )
{
print $file[ $i ]."n";
}
}
但是我正在尝试使用until
函数,以便读取行并搜索我正在寻找的连续行集("空白"行加上两个侧面的"完整"行)。例如:
until ( $file[ a line ] =~ m/ / && $file[ another line ] =~ m/ / )
{
my linear interpolation here;
}
谁能给我一个提示,如何耦合一种方式来读取数组和比较行,以找到我需要跨文件的集合?
你想要实现的是一个缓存算法:记住(缓存)以前的值,并在没有新值出现时使用它们。你甚至不需要正则表达式。:)
除了缓存旧值之外,还需要缓存中间的行。因为你只需要标签,你只需要抓住它们。然后,当您到达下一行时,您可以进行插值并发出结果。
我是这样做的。它比我最初的示例稍微复杂一些,但适用相同的原则:只存储中间行,然后在到达终端时发出结果。
use strict;
use warnings;
use feature 'say';
# Get start conditions, and cache those numbers.
sub read_block
{
my $line = <DATA>;
return 1 unless defined $line; # we're done if nothing more to read
# Process and store data from the first line in the block.
chomp $line;
my ($last_label, $last_num1, $last_num2, $last_label2) = split /t/, $line;
# Keep reading lines until we find the end of the block.
my @label_cache;
my $found_last = 0;
my ($label1, $num1, $num2, $label2);
while (!$found_last)
{
$line = <DATA>;
chomp $line;
($label1, $num1, $num2, $label2) = split /t/, $line;
if (defined $num1 && defined $num2)
{
$found_last = 1; # We have final numbers! We can interpolate now.
}
else
{
push @label_cache, $label1;
}
}
# Begin display. Show the first line of the block.
say "$last_labelt$last_num1t$last_num2t$last_label2";
# Calculate the slope for interpolation: (last - first) / difference
my $slope1 = ($num1 - $last_num1) / (@label_cache + 1);
my $slope2 = ($num2 - $last_num2) / (@label_cache + 1);
my $distance = 0;
# Display each label and the lines inside.
foreach my $label (@label_cache)
{
++$distance;
say $label, "t",
$slope1 * $distance + $last_num1, "t",
$slope2 * $distance + $last_num2;
}
# Display the final line in the block.
say "$label1t$num1t$num2t$label2";
# Not done yet, so return a 'false' value.
return 0;
}
# Main part of the script
my $done = 0;
while (! $done)
{
$done = read_block();
}
__DATA__
a 3 4 end
e
f
g
h
i
k 15 26 start
k 15 26 end
o
p
q
r
s 3 5 start
s 3 5 end
v
w
x
y 14 16 start
发出:
a 3 4 end
e 5 7.66666666666667
f 7 11.3333333333333
g 9 15
h 11 18.6666666666667
i 13 22.3333333333333
k 15 26 start
k 15 26 end
o 12.6 21.8
p 10.2 17.6
q 7.8 13.4
r 5.4 9.2
s 3 5 start
s 3 5 end
v 5.75 7.75
w 8.5 10.5
x 11.25 13.25
y 14 16 start
当然,你可以做任何你需要的数字四舍五入或格式化。:)
也许以下内容会有所帮助:
use strict;
use warnings;
my ( $last, $oneColumn );
my @file = <DATA>;
for my $line (@file) {
my @entires = split ' ', $line;
if ( @entires == 4 ) {
if ($oneColumn) {
print $line; # Succeeding line
$oneColumn = 0;
}
$last = $line;
next;
}
print $last if $last; # Preceeding line
undef $last;
print $line; # One-column line
$oneColumn = 1;
}
__DATA__
30 13387412 34.80391242 sSN_FIRST
30 13387412 34.80391242 sSN5_40
30.1
30.2
30.3
30.4
31 14740248 65.60590089 s32138223_44
31 14740248 65.60590089 s321382_LAST
输出:30 13387412 34.80391242 sSN5_40
30.1
30.2
30.3
30.4
31 14740248 65.60590089 s32138223_44
A 'full', line应该在@entries
中有四个元素,这就是if ( @entires == 4 )
所寻找的。如果找到了,只有在打印了单列行后,它才会将其作为下一行打印。然后,它节省了这条线。只有当行没有三个制表符时,才会在if
之外打印。
以下更短的脚本产生相同的输出:
use strict;
use warnings;
my @file = <DATA>;
for ( my $i = 1 ; $i < $#file ; $i++ ) {
if ( $file[$i] =~ /(?:ts){3}/ ) {
print $file[ $i - 1 ]; # Preceeding line
while ( $file[$i] =~ /(?:ts){3}/ and $i < $#file ) {
print $file[ $i++ ] # One-column line
}
print $file[$i]; # Succeeding line
}
}
__DATA__
30 13387412 34.80391242 sSN_FIRST
30 13387412 34.80391242 sSN5_40
30.1
30.2
30.3
30.4
31 14740248 65.60590089 s32138223_44
31 14740248 65.60590089 s321382_LAST
/(?:ts){3}/
匹配三组连续的制表符和空格,这只能在只有一列的行中找到。当它找到该模式时,它打印前一行,然后进入一个while
循环,该循环打印单列行,直到找到整行或位于数组末尾。最后,打印下一行。