Соответствующие сбалансированные скобки в регулярном выражении Perl
У меня есть выражение, которое мне нужно разделить и сохранить в массиве:
aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }, aaa="bbb{}" { aa="b}b" }, aaa="bbb,ccc"
Он должен выглядеть следующим образом после разделения и сохранения в массиве:
aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }
aaa="bbb{}" { aa="b}b" }
aaa="bbb,ccc"
Я использую Perl версии 5.8 и кто-то может решить эту проблему?
7 ответов
Используйте модуль perl "Regexp::Common". У этого есть хорошая сбалансированная скобка Regex, которая работает хорошо.
# ASN.1
use Regexp::Common;
$bp = $RE{balanced}{-parens=>'{}'};
@genes = $l =~ /($bp)/g;
В Perlre есть пример использования рекурсивных функций регулярных выражений, представленных в v5.10. Хотя вы ограничены версией v8.8, другие люди, приходящие на этот вопрос, должны найти правильное решение:)
$re = qr{
( # paren group 1 (full function)
foo
( # paren group 2 (parens)
\(
( # paren group 3 (contents of parens)
(?:
(?> [^()]+ ) # Non-parens without backtracking
|
(?2) # Recurse to start of paren group 2
)*
)
\)
)
)
}x;
Чтобы соответствовать сбалансированным круглым скобкам или фигурным скобкам, и если вы хотите учесть обратные косые (экранированные) скобки, предлагаемые решения не будут работать. Вместо этого вы должны написать что-то вроде этого (основываясь на предложенном решении на perlre ):
$re = qr/
( # paren group 1 (full function)
foo
(?<paren_group> # paren group 2 (parens)
\(
( # paren group 3 (contents of parens)
(?:
(?> (?:\\[()]|(?![()]).)+ ) # escaped parens or no parens
|
(?&paren_group) # Recurse to named capture group
)*
)
\)
)
)
/x;
Попробуйте что-то вроде этого:
use strict;
use warnings;
use Data::Dumper;
my $exp=<<END;
aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } } , aaa="bbb{}" { aa="b}b" }, aaa="bbb,ccc"
END
chomp $exp;
my @arr = map { $_ =~ s/^\s*//; $_ =~ s/\s* $//; "$_}"} split('}\s*,',$exp);
print Dumper(\@arr);
Я согласен со Скоттом Риппи, более или менее, написать собственный парсер. Вот простой:
my $in = 'aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }, ' .
'aaa="bbb{}" { aa="b}b" }, ' .
'aaa="bbb,ccc"'
;
my @out = ('');
my $nesting = 0;
while($in !~ m/\G$/cg)
{
if($nesting == 0 && $in =~ m/\G,\s*/cg)
{
push @out, '';
next;
}
if($in =~ m/\G(\{+)/cg)
{ $nesting += length $1; }
elsif($in =~ m/\G(\}+)/cg)
{
$nesting -= length $1;
die if $nesting < 0;
}
elsif($in =~ m/\G((?:[^{}"]|"[^"]*")+)/cg)
{ }
else
{ die; }
$out[-1] .= $1;
}
(Протестировано в Perl 5.10; извините, у меня нет Perl 5.8 под рукой, но, насколько я знаю, нет никаких существенных различий.) Излишне говорить, что вы захотите заменить die
с чем-то конкретным приложением. И вам, вероятно, придется настроить вышеупомянутое для обработки случаев, не включенных в ваш пример. (Например, могут ли строки в кавычках содержать \"
? Можно '
использоваться вместо "
? Этот код не обрабатывает ни одну из этих возможностей.)
Расколотое решение кажется самым простым. Разделить взгляд на вашу основную переменную aaa
, с границей слова вокруг. Удалите конечные пробелы и запятую с необязательной группой символов.
$string = 'aaa="bbb{ccc}ddd" { aa="bb,cc" { a="b", c="d" } }, aaa="bbb{}" { aa="b}b" }, aaa="bbb,ccc"';
my @array = split /[,\s]*(?=\baaa\b)/, $string;
Хотя рекурсивные регулярные выражения обычно можно использовать для захвата "сбалансированных скобок" {}
, они не будут работать на вас, потому что вы также должны соответствовать "сбалансированным кавычкам" "
,
Это было бы очень сложной задачей для регулярного выражения Perl, и я вполне уверен, что это невозможно. (В отличие от этого, вероятно, это можно сделать с помощью функции регулярных выражений Microsoft "балансировки групп").
Я бы предложил создать свой собственный парсер. Когда вы обрабатываете каждого персонажа, вы считаете каждого "
а также {}
и только разделить на ,
если они "сбалансированы".