Согласование сбалансированной круглой скобки в регулярном выражении 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 и может ли кто-то решить эту проблему?

Ответ 1

Используйте модуль perl "Regexp:: Common". Он имеет хорошее сбалансированное регулярное выражение, которое хорошо работает.

# ASN.1
use Regexp::Common;
$bp = $RE{balanced}{-parens=>'{}'};
@genes = $l =~ /($bp)/g;

Ответ 2

Вот пример в perlre, используя рекурсивные функции регулярного выражения, представленные в версии 5.10. Хотя вы ограничены v5.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;

Ответ 3

Попробуйте что-то вроде этого:

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);

Ответ 4

Я согласен с Скоттом Риппи, более или менее, о написании собственного парсера. Здесь простой:

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 на что-то конкретное приложение. И вам, вероятно, придется подстроить выше, чтобы обрабатывать случаи, не включенные в ваш пример. (Например, могут ли строки с кавычками содержать \"? Может ли ' использоваться вместо "? Этот код не обрабатывает ни одну из этих возможностей.)

Ответ 5

Хотя Рекурсивные регулярные выражения обычно можно использовать для захвата "сбалансированных фигурных скобок" {}, они не будут работать для вас, потому что у вас также есть требование соответствовать "сбалансированным котировкам" ".
Это было бы очень сложной задачей для регулярного выражения Perl, и я уверен, что это невозможно. (Напротив, это можно было бы сделать с помощью функции балансировки групп Microsoft" Regex).

Я бы предложил создать собственный парсер. Когда вы обрабатываете каждый символ, вы считаете каждый " и {} и разделяете только на ,, если они "сбалансированы".

Ответ 6

Расколотое решение кажется простым. Разделите взгляд на свою основную переменную 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;