提取每个括号级别的内容

时间:2012-06-25 16:28:58

标签: regex perl grammar speech parentheses

我正在将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)])取代。

因此,我必须提取带括号的表达式的每个级别,看它是否以问号结尾,并用方括号替换parens和问号(如果有)。 我认为Eric Strom对this问题的回答几乎就是我所需要的。问题是,当我使用它时,它返回最大的匹配分组,而我需要对每个单独的分组进行操作。

这是我到目前为止所做的:s/( \( (?: [^()?]* | (?0) )* \) ) \?/[$1]/xg。但是,与((((stuff)? that)? I)? like)?匹配时,它只会生成[((((stuff)? that)? I)? like)]。关于如何做到这一点的任何想法?

3 个答案:

答案 0 :(得分:4)

您还需要查看ysth's solution to that question,并使用已有的工具来解决此问题:

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
    }
}

答案 1 :(得分:2)

在较旧的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

答案 2 :(得分:1)

您可以通过几种方式解决它,最简单的方法就是执行您的表达式,直到不再进行替换。 E.g:

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

但这非常低效(对于深度嵌套的字符串)。

你可以在这样的一次传球中代替:

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

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

  |

  \) \?
}{
  $2? '[': ']'
}gex;