我需要在多行上解析一些数据。我希望在一行上有一些数据点来创建一个类似于结构的表。数据各不相同。有些是在多行上分解的,有些只是2行。
示例数据
情景1:
Start Anchor - First parse boundary
Name1 - Only one line of name
12345 - Number line needed
ending anchor - End parse boundary
data
data
data
情景2:
Start Anchor
name1 - Two lines of name.
name2
12345987
ending anchor
data
data
data
期望的结果
情景1:
Name1 12345
data
data
data
情景2:
name1 name2 12345987
data
data
data
解释
所以在这个数据中,我需要同一行上的名称和相应的数字。我想将\n
替换为解析边界之间的内容,同时仍保留其余数据。
我当前的结果
情景1:
name1 12345987 - easy because only 1 line of name
data
data
情景2:
name1 name2 - tough part
12345987
data
data
代码
my $text = read_file( 'list_2.txt' );
use File::Slurp;
$text =~ s/^Start Anchor\n(.*?)\n(.*?)ending anchor/$1 $2/gism;
print $text;
print;
在我看来,$1
变量应该捕获名称,因为使用了结束锚。 $2
变量应该捕获数字,因为它总是在结束锚点之上一行。
答案 0 :(得分:1)
正在打败你的是非贪婪的通配符。模式^Start Anchor\n(.*?)\n(.*?)ending anchor
将在Start Anchor\n
之后与少数字符匹配到下一个换行符,即第一个名称行。然后下一个捕获将匹配 - 尽可能少的字符,但在这里没有区别 - 到ending anchor
,whiuch是第二个名称行和数字,以及它们之间的换行符,因为你有有效的/s
修饰符。
解析内存中的整个文件很少是一个简单的工作,通常更好的选择是逐行读取并保持状态数据记住你在结构中的位置。
在这种情况下,一个简单的嵌套读循环就可以了。您没有说多个块是否可以出现在同一个文件中,但是此解决方案可以处理我认为您想要的情况。
use strict;
use warnings;
my @name;
while (<DATA>) {
if (/^Start Anchor/) {
while (<DATA>) {
last if /^ending anchor/;
chomp;
push @name, $_;
}
print "@name\n";
@name = ()
}
else {
print;
}
}
__DATA__
Start Anchor
Name1
12345
ending anchor
data
data
data
Start Anchor
name1
name2
12345987
ending anchor
data
data
data
<强>输出强>
Name1 12345
data
data
data
name1 name2 12345987
data
data
data
答案 1 :(得分:0)
您仍然可以逐行处理文件,只需记住您当前处于的状态或阶段:
#!/usr/bin/perl
use warnings;
use strict;
my $state = 'search anchor';
my @names;
while (<DATA>) {
if ('search anchor' eq $state and /Start Anchor/) {
$state = 'collect names';
} elsif ('collect names' eq $state) {
chomp;
push @names, $_;
$state = 'expect ending anchor' if /^[0-9]+$/;
} elsif ('expect ending anchor' eq $state) {
die 'Ending anchor not found' unless /ending anchor/;
$state = 'data';
print "@names\n"
} elsif ('data' eq $state) {
if (/Start Anchor/) {
$state = 'collect names';
@names = ();
} else {
print;
}
}
}
__DATA__
Start Anchor
Name1
12345
ending anchor
data
data
data
Start Anchor
name1
name2
12345987
ending anchor
data
data
data
答案 2 :(得分:0)
第二个名称是可选的。
正则表达式使用eval修饰符来进行替换格式化
它不是必需的,如果需要,只需替换$1 $2 $3\n
就可以完成。
# /(?xm)^Start\ Anchor\n\s*^(\w.*)\n(?:^(\w.*)\n)?\s*^(\d+).*\n\s*^ending\ anchor(?:\n|$)/
(?xm-)
^ Start\ Anchor \n
\s*
^
( \w .* ) # (1), Name1 required
\n
(?:
^
( \w .* ) # (2), Name2 optional
\n
)?
\s*
^
( \d+ ) # (3), Numbers
.* \n
\s*
^ ending\ anchor
(?: \n | $ )
Perl代码:
use strict;
use warnings;
$/ = undef;
my $data = <DATA>;
$data =~
s/
^Start\ Anchor\n\s*^(\w.*)\n(?:^(\w.*)\n)?\s*^(\d+).*\n\s*^ending\ anchor(?:\n|$)
/
"$1 ".(defined $2 ? "$2 " : "") . "$3\n"
/exmg;
print $data, "\n";
__DATA__
Start Anchor
name1
12345
ending anchor
data
data
data
Start Anchor
name1
name2
12345987
ending anchor
data
data
data
输出:
name1 12345
data
data
data
name1 name2 12345987
data
data
data
答案 3 :(得分:0)
两个提示
逐行处理文件而不是诽谤
将Range Operator ..
用于跨越多行的逻辑
以下是根据需要清理您的脚本:
use strict;
use warnings;
use autodie;
my $file = 'list_2.txt';
#open my $fh, '<', $file;
my $fh = \*DATA;
while (<$fh>) {
if ( my $range = /^Start Anchor/ .. /^ending anchor/ ) {
if ( $range =~ /E/ ) {
print "\n";
} elsif ( $range > 1 ) {
chomp;
print ' ' if $range > 2;
print;
}
} else {
print;
}
}
__DATA__
Start Anchor
Name1
12345
ending anchor
data
data
data
Start Anchor
name1
name2
12345987
ending anchor
data
data
data
输出:
Name1 12345
data
data
data
name1 name2 12345987
data
data
data