извлекать содержимое каждого уровня круглых скобок

Я конвертирую грамматику SMAPI в JSGF. Это очень похожие грамматики, используемые в разных системах распознавания речи. SMAPI использует вопросительный знак, как и весь остальной мир, для обозначения 0 или 1 предыдущего значения. JSGF использует для этого квадратные скобки. Итак, мне нужно преобразовать строку типа stuff? в [stuff] и строки в скобках, такие как ((((stuff)? that)? I)? like)?, в [[[[stuff] that] I] like]. Я должен оставить в покое такие строки, как ((((stuff) that) I) hate). Как указал Qtax, более сложным примером будет замена (foo ((bar)? (baz))?) на (foo [[bar] (baz)]).

Из-за этого мне нужно извлечь каждый уровень выражения в скобках, посмотреть, оканчивается ли оно вопросительным знаком, и заменить скобки и вопросительный знак квадратными скобками, если это так. Я думаю, что Эрик Стром ответит на это Вопрос почти в том, что мне нужно. Проблема в том, что когда я его использую, он возвращает самую большую совпадающую группировку, тогда как мне нужно выполнять операции с каждой отдельной группировкой.

Вот что у меня есть: s/( \( (?: [^()?]* | (?0) )* \) ) \?/[$1]/xg. Однако при сопоставлении с ((((stuff)? that)? I)? like)? он дает только [((((stuff)? that)? I)? like)]. Есть идеи, как это сделать?

I


person Nate Glenn    schedule 25.06.2012    source источник
comment
Можете ли вы использовать замену строки вместо регулярного выражения, чтобы заменить ( на [ и )? на ]?   -  person David B    schedule 25.06.2012
comment
В вашем заголовке говорится, что вам нужно извлечь содержимое круглых скобок, но в вашем тексте говорится, что вам нужно преобразовать круглые скобки в квадратные. Что он?   -  person TLP    schedule 25.06.2012
comment
Я отредактировал вопрос, чтобы лучше отразить мои намерения.   -  person Nate Glenn    schedule 25.06.2012
comment
Думаю, лучший пример: от (foo ((bar)? (baz))?) до (foo [[bar] (baz)])   -  person Qtax    schedule 25.06.2012
comment
@Qtax: да! Думаю, мне следует указать это в своем вопросе ...   -  person Nate Glenn    schedule 25.06.2012


Ответы (3)


Вы можете решить это несколькими способами, самый простой из которых - просто выполнить свое выражение до тех пор, пока больше не будет произведено замен. Например:

1 while s/( \( (?: [^()?]* | (?0) )* \) ) \?/[$1]/xg;

Но это крайне неэффективно (для глубоко вложенных строк).

Вместо этого вы можете сделать это за один проход следующим образом:

s{
  (?(DEFINE)
    (?<r>   \( (?: [^()]++ | (?&r) )*+ \)   )
  )

  ( \( )
  (?=   (?: [^()]++ | (?&r) )*+ \) \?   )

  |

  \) \?
}{
  $2? '[': ']'
}gex;
person Qtax    schedule 25.06.2012
comment
1 штука пока работает отлично! Как получилось, что предоставление регулярному выражению g swith не сделало то же самое? Однако, учитывая <testMe> = ((((stuff)? that)? I)? like)?, ваше регулярное выражение по какой-то причине дает мне <testMe> = ]]]]stuff] that] I] like]. - person Nate Glenn; 25.06.2012
comment
@NateGlenn, сейчас работает, сменил $1 на $2. (Забыл посчитать рекурсивную группу, да.) - person Qtax; 25.06.2012
comment
Хотя некоторые комментарии по этому поводу были бы хороши ... Это далеко за пределами моих возможностей регулярного выражения. - person Nate Glenn; 25.06.2012
comment
@NateGlenn - это не сработает, если в тексте есть ?, например, с текстом ((((stu?ff)? that)? I)? like)? - person Ωmega; 25.06.2012
comment
@ user1215106: Если бы это было правдой, какова вероятность того, что у вас будет? в грамматике распознавания речи? В грамматиках распознавания обычно нет знаков препинания, кроме тире и апострофов, которые допустимы в пределах слова. - person Nate Glenn; 25.06.2012

Вы также захотите взглянуть на решение ysth для этого вопроса и использовать уже имеющийся инструмент для решения этой проблемы. :

use Text::Balanced qw(extract_bracketed);
$text = '((((stuff)? that)? I)? like)?';

for ($i=0; $i<length($text); $i++) {
    ($match,$remainder) = extract_bracketed( substr($text,$i), '()' );
    if ($match && $remainder =~ /^\?/) {
        substr($text,$i) =
            '[' . substr($match,1,-1) . ']' . substr($remainder,1);
        $i=-1; # fixed
    }
}
person mob    schedule 25.06.2012
comment
@ Ωmega, как хорошо? Эта версия вообще не работает даже с исходным примером OP. (Результат [[([stuff] that)? I] like].) В этом простом примере первая версия работала, но это бесполезно, так как она терпит неудачу с правильным примером, таким как (foo ((bar)? (baz))?), результат (foo[([bar] (baz]?). Не работает ни на что, -1 пока не исправлено. - person Qtax; 28.06.2012

В более старых версиях Perl (до 5.10) для этого можно было использовать утверждения кода и динамическое регулярное выражение:

 ...
 my $s = '((((stuff)? that)? I)? like)?';

 # recursive dynamic regex, we need
 # to pre-declare lexical variables
 my $rg;

 # use a dynamically generated regex (??{..})
 # and a code assertion (?{..})
 $rg = qr{
          (?:                       # start expression
           (?> [^)(]+)              # (a) we don't see any (..) => atomic!
            |                       # OR 
           (                        # (b) start capturing group for level
            \( (??{$rg}) \) \?      # oops, we found parentheses \(,\) w/sth 
           )                        # in between and the \? at the end
           (?{ print "[ $^N ]\n" }) # if we got here, print the captured text $^N
          )*                        # done, repeat expression if possible
         }xs;

 $s =~ /$rg/;
 ...

во время совпадения утверждение кода выводит все совпадения, а именно:

 [ (stuff)? ]
 [ ((stuff)? that)? ]
 [ (((stuff)? that)? I)? ]
 [ ((((stuff)? that)? I)? like)? ]

Чтобы использовать это в соответствии с вашими требованиями, вы можете немного изменить утверждение кода, поместить скобки захвата в нужное место и сохранить совпадения в массиве:

 ...
 my @result;
 my $rg;
 $rg = qr{
          (?:                      
           (?> [^)(]+)             
            |                      
            \( ( (??{$rg}) ) \) \?  (?{ push @result, $^N })
          )*                     
         }xs;

 $s =~ /$rg/ && print map "[$_]\n", @result;
 ...

который говорит:

 [stuff]
 [(stuff)? that]
 [((stuff)? that)? I]
 [(((stuff)? that)? I)? like]

С Уважением

rbo

person rubber boots    schedule 25.06.2012