快速和骯髒的方法是定義一個大多匹配字段分配的正則表達式,然後在另一個正則表達式中使用它來匹配它們之間的內容。
my $field_assignment_re = qr{^\s* field \s* = \s* [^;]+ ;}msx;
$code =~ /$field_assignment_re (.*?) $field_assignment_re/msx;
print $1;
這種方法的不足之處是它可能會匹配引用的字符串等。
您可以排序的解析與正則表達式的代碼,但它的解析正確是超出正常的正則表達式。這是因爲大量的平衡分隔符(即,parens和大括號)和逃逸(即"<foo \"bar\"">"
)。要做到這一點,你需要編寫一個語法。
Perl 5.10增加了recursive decent matching使寫文法成爲可能。他們還添加了named capture groups以跟蹤所有這些規則。現在,您可以使用Perl 5.10正則表達式編寫遞歸語法。
它仍然有點笨重,Regexp::Grammar增加了一些增強功能,使寫正則表達式語法更容易。
寫一個語法是關於從某個點開始並填入規則。你的程序是一堆Statement
s。什麼是聲明?一個Assignment或FunctionCall後跟一個;
。什麼是作業? 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
爲什麼會產生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
規則將處理大部分。
這類問題很難用正則表達式。我會建議使用[Parse :: RecDescent](https://metacpan.org/pod/Parse::RecDescent)或[Regexp :: Grammars](https://metacpan.org/pod/Regexp: :文法)。 – Schwern
你只能用[脾氣暴躁的貪婪牌]做這種事情(https://regex101.com/r/fC8bS2/1)。不允許跳過,除非您想使用兩個預讀並捕獲一些不連續的文本塊。 –
這很容易用正則表達式來完成。問題是你的例子展示了一種複雜的語言。沒有細節,對於那種特定語言的解析器來說似乎更合適。 – sln