快速而肮脏的方法是定义一个主要匹配字段分配的正则表达式,然后在另一个正则表达式中使用它来匹配它们之间的内容。
my $field_assignment_re = qr{^\s* field \s* = \s* [^;]+ ;}msx;
$code =~ /$field_assignment_re (.*?) $field_assignment_re/msx;
print $1;
这种方法的缺点是它可能匹配带引号的字符串等。
您可以使用正则表达式对代码进行排序,但正确解析它超出了正常的正则表达式。这是因为大量的平衡分隔符(即括号和大括号)和转义符(即。"<foo \"bar\"">"
)。为了让它正确,你需要写一个语法。
Perl 5.10 添加了递归体面匹配,使编写语法成为可能。他们还添加了命名捕获组来跟踪所有这些规则。现在您可以使用 Perl 5.10 正则表达式编写递归语法。
它仍然有点笨拙,Regexp::Grammar添加了一些增强功能,使编写正则表达式语法更加容易。
编写语法是关于从某个点开始并填写规则。你的程序是一堆Statement
s。什么是声明?一个分配,或一个函数调用,后跟一个;
. 什么是作业? Variable = Expression
. 什么是Variable
和Expression
?等等...
use strict;
use warnings;
use v5.10;
use Regexp::Grammars;
my $parser = qr{
<[Statement]>*
<rule: Variable> \w+
<rule: FunctionName> \w+
<rule: Escape> \\ .
<rule: Unknown> .+?
<rule: String> \" (?: <Escape> | [^\"] )* \"
<rule: Ignore> \.\.\.?
<rule: Expression> <Variable> | <String> | <Ignore>
<rule: Assignment> <Variable> = <Expression>
<rule: Statement> (?: <Assignment> | <FunctionCall> | <Unknown> ); | <Ignore>
<rule: FunctionArguments> <[Expression]> (?: , <[Expression]> )*
<rule: FunctionCall> <FunctionName> \( <FunctionArguments>? \)
}x;
my $code = <<'END';
field = "test \" string";
alkjflkj;
type = INT;
funcCall(.., field, "escaped paren \)", ...);
...
text = "desc";
field = "test string 1";
type = FLOAT;
funcCall(.., field, ...);
...
text = "desc 2";
field = "test string 2";
type = FLOAT;
funcCall(.., field, ...);
...
text = "desc 3";
END
$code =~ $parser;
这比正则表达式要健壮得多。包括:
<rule: Escape> \\ .
<rule: String> \" (?: <Escape> | [^\"] )* \"
处理其他棘手的边缘情况,例如:
funcCall( "\"escaped paren \)\"" );
这一切都结束了%/
。这是第一部分。
$VAR1 = {
'Statement' => [
{
'Assignment' => {
'Variable' => 'field',
'Expression' => {
'String' => '"test string"',
'' => '"test string"'
},
'' => 'field = "test string"'
},
'' => 'field = "test string";'
},
...
然后你可以遍历Statement
数组寻找匹配的Assignment
s 。Variable
field
my $seen_field_assignment = 0;
for my $statement (@{$/{Statement}}) {
# Check if we saw 'field = ...'
my $variable = ($statement->{Assignment}{Variable} || '');
$seen_field_assignment++ if $variable eq 'field';
# Bail out if we saw the second field assignment
last if $seen_field_assignment > 1;
# Print if we saw a field assignment
print $statement->{''} if $seen_field_assignment;
}
这似乎需要做很多工作,但值得学习如何编写语法。有很多问题可以用正则表达式解决一半,但用简单的语法就可以完全解决。从长远来看,正则表达式会变得越来越复杂,并且永远不会完全覆盖所有边缘情况,而语法则更容易理解并且可以变得完美。
这种方法的缺点是您的语法可能不完整并且可能会出错,尽管Unknown
规则会处理大部分内容。