我对Perl脚本有以下输入,我希望在每个<table>...</table>
结构中首次出现NAME =“...”字符串。
整个文件被读入一个字符串,正则表达式作用于该输入。
但是,正则表达式始终返回最后一次出现的NAME="..."
个字符串。任何人都可以解释发生了什么以及如何解决这个问题?
Input file:
ADSDF
<TABLE>
NAME="ORDERSAA"
line1
line2
NAME="ORDERSA"
line3
NAME="ORDERSAB"
</TABLE>
<TABLE>
line1
line2
NAME="ORDERSB"
line3
</TABLE>
<TABLE>
line1
line2
NAME="ORDERSC"
line3
</TABLE>
<TABLE>
line1
line2
NAME="ORDERSD"
line3
line3
line3
</TABLE>
<TABLE>
line1
line2
NAME="QUOTES2"
line3
NAME="QUOTES3"
NAME="QUOTES4"
line3
NAME="QUOTES5"
line3
</TABLE>
<TABLE>
line1
line2
NAME="QUOTES6"
NAME="QUOTES7"
NAME="QUOTES8"
NAME="QUOTES9"
line3
line3
</TABLE>
<TABLE>
NAME="MyName IsKhan"
</TABLE>
Perl Code从这里开始:
use warnings;
use strict;
my $nameRegExp = '(<table>((NAME="(.+)")|(.*|\n))*</table>)';
sub extractNames($$){
my ($ifh, $ofh) = @_;
my $fullFile;
read ($ifh, $fullFile, 1024);#Hardcoded to read just 1024 bytes.
while( $fullFile =~ m#$nameRegExp#gi){
print "found: ".$4."\n";
}
}
sub main(){
if( ($#ARGV + 1 )!= 1){
die("Usage: extractNames infile\n");
}
my $infileName = $ARGV[0];
my $outfileName = $ARGV[1];
open my $inFile, "<$infileName" or die("Could not open log file $infileName");
my $outFile;
#open my $outFile, ">$outfileName" or die("Could not open log file $outfileName");
extractNames( $inFile, $outFile );
close( $inFile );
#close( $outFile );
}
#call
main();
答案 0 :(得分:4)
试试这个:
'(?><TABLE>\n+(?:(?!</TABLE>|NAME=).*\n+)*)NAME="([^"]+)"'
(?:.*\n+)*
消耗任何不需要的行,而嵌入式预测 - (?!</TABLE>|NAME=)
- 使其不会超出第一个NAME字段或TABLE记录的结尾。为了防止有一个没有NAME字段的记录,我将大部分表达式包装在一个原子组中 - (?>...)
- 以防止无意义的回溯。
请注意,现在只有一个捕获组。只有当你真的需要捕捉某些东西时才使用它们是一种好习惯;否则,使用非捕获变种:(?:...)
。
((NAME="(.+)")|(.*|\n))*
最外面的parens中的部分可以匹配任何内容:标记,NAME=
行,换行符 - 甚至是空行。将其包裹在由贪婪的*
控制的组中,现在它匹配所有内容。没有任何内容可以使它在第一个NAME字段停止匹配,甚至在记录结束时停止匹配。
所以它实际上“找到”每个出现的NAME="..."
个字符串,但它是在一次匹配尝试中一次性消耗整个输入。对于封闭*
的每次迭代,捕获组都会被覆盖;当它完成时,最终的NAME值 - MyName IsKhan
- 恰好留在第4组。
我使用负向前瞻来检查贪婪,但你也可以通过使用非贪婪的量词来更直接地做到这一点。这是我的正则表达式看起来如何用一个不情愿的*
取代负面的前瞻:
'<TABLE>\n+(?:.*\n+)*?NAME="([^"]+)"'
简单地切换到非贪婪的量词对你的正则表达式没有帮助;你也必须做出一些结构性的改变。
答案 1 :(得分:1)
尝试让你的正则表达式非贪婪:
my $nameRegExp = '(<table>((NAME="(.+?)")|(.*?|\n))*</table>)';
即使上述正则表达式 不 也会列出文件中的所有NAME行。它将列出每个<TABLE>...</TABLE>
块中的一个NAME行(最后一行)。
列出您可以执行的所有NAME行:
my $nameRegExp = 'NAME="(.+?)"';
和print $1
;
答案 2 :(得分:1)
首先,用正则表达式解析XML是个坏主意。 其次,您需要将正则表达式更改为以下内容:
my $nameRegExp = '(<table>((NAME="(.+)?")|(.*?|\n))*?</table>)';
这样正则表达式变得非贪婪,应该返回第一次出现。
答案 3 :(得分:1)
$/ = '</TABLE>';
while (<>) {
chomp;
@F = split "\n";
$g = 0;
for ($o = 0; $o <= $#F; $o++) {
if ($F[$o] =~ /^NAME=/) {
$F[$o] =~ s/^NAME=//g;
$v = $F[$o];
$g = 1;
last;
}
}
if ($g) { print $v."\n"; }
}
输出
$ perl myscript.pl file
"ORDERSAA"
"ORDERSB"
"ORDERSC"
"ORDERSD"
"QUOTES2"
"QUOTES6"
"MyName IsKhan"
它的全部要点:使用</TABLE>
作为记录分隔符,使用换行符作为字段分隔符。浏览每个字段并找到NAME=
。如果找到,请在=
符号后替换并获取字符串。