Быстрый и грязный способ определить регулярное выражение, которое в основном соответствует заданию поля, а затем использовать его в другом регулярном выражении, чтобы соответствовать тому, что находится между ними.
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 добавлено recursive decent matching, чтобы написать грамматику. Они также добавили named capture groups, чтобы отслеживать все эти правила. Теперь вы можете написать рекурсивную грамматику с регулярными выражениями Perl 5.10.
Это до сих пор неловкое, Regexp::Grammar добавляет некоторые улучшения, чтобы значительно упростить грамматику регулярных выражений.
Написание грамматики начинается с некоторого момента и заполнения правил. Ваша программа - куча Statement
s. Что такое заявление?Назначение или 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