【问题标题】:Missing parentheses with Regex正则表达式缺少括号
【发布时间】:2012-12-29 08:59:42
【问题描述】:

我认为 Regex 不能用于检测丢失的括号是否正确(因为无法计算对)?使用 JavaScript,我已经截断了大约一千个字符串,需要手动编辑。我希望能够使用代码将这个列表缩小到需要注意的那些。字符串可以被认为是以下形式:

  • (这个很好,不需要注意)
  • 这也[好]
  • 这很糟糕(需要编辑
  • 这[(也)很糟糕
  • 这样}不好
  • 此字符串没有任何类型的括号,但也必须考虑

如果这是不可能的,那么我只需要编写一个函数来查找括号对。谢谢

【问题讨论】:

  • 就正则表达式而言,它们并不是检查编程语言语法的好工具。您可以在一个简单的 for 循环中以更清晰和错误安全的方式计算括号。或者使用类似 javacc 的工具来正确检查语法。
  • 对不起 - 我忘了提到字符串不是来自代码;它们来自文件名。

标签: javascript regex parentheses


【解决方案1】:

一些正则表达式风格能够匹配嵌套括号等递归结构,但语法非常复杂,通常只写一个函数就更容易了。 JavaScript 正则表达式根本不支持递归。

【讨论】:

    【解决方案2】:

    可以使用递归正则表达式来验证匹配的括号。例如,在 Perl 中,以下表达式匹配具有正确 () {} [] 嵌套的字符串:

    $r = qr/(?:(?>[^(){}\[\]]+)|\((??{$r})\)|\{(??{$r})\}|\[(??{$r})\])*/;
    

    为清楚起见,扩展了相同的表达式:

    $r = qr/
        (?:
            (?>
                [^(){}\[\]]+
            )
        |
            \(
                (??{$r})
            \)
        |
            \{
                (??{$r})
            \}
        |
            \[
                (??{$r})
            \]
        )*
    /x;
    

    外部组用* 量化而不是+ 以匹配空字符串,因此为了使$r 有用,实际匹配必须使用利用lookaheads/lookbehinds 或其他方式的表达式完成建立上下文,例如/^$r$/。例如,以下仅打印文件中没有正确嵌套的行:

    perl -ne '$r = qr/(?:(?>[^(){}\[\]]+)|\((??{$r})\)|\{(??{$r})\}|\[(??{$r})\])*/; print if !m/^$r$/' file
    

    为了澄清您的问题:如果这些是文件名而不是文件内容,您可以将 lsfind 或其他任何内容的输出传递到上述命令中,而不是 file

    ls | perl -ne '$r = qr/(?:(?>[^(){}\[\]]+)|\((??{$r})\)|\{(??{$r})\}|\[(??{$r})\])*/; print if !m/^$r$/'
    

    但是,正如其他人所说,一般来说,非正则表达式解决方案可能更好。

    注意来自Perl doc:“警告:这个扩展的正则表达式功能被认为是实验性的,可能会在没有通知的情况下进行更改。由于正则表达式引擎中未来优化的影响,执行的具有副作用的代码在不同版本之间的执行可能不同。”

    【讨论】:

      【解决方案3】:
      function isFine(str) {  
        return /[(){}\[\]]/.test( str ) && 
          ( str.match( /\(/g ) || '' ).length == ( str.match( /\)/g ) || '' ).length &&
          ( str.match( /\[/g ) || '' ).length == ( str.match( /]/g ) || '' ).length &&
          ( str.match( /{/g ) || '' ).length == ( str.match( /}/g ) || '' ).length;
      }
      

      测试

      isFine('(this is fine and does not need attention)');                 // true
      isFine('This is also [fine]');                                        // true
      isFine('This is bad( and needs to be edited');                        // false
      isFine('This [is (also) bad');                                        // false
      isFine('as is this} bad');                                            // false
      isFine('this string has no brackets but must also be considered');    // false
      

      但请注意,这不会检查括号顺序,即a)b(c 将被视为没问题。

      为了记录,这里有一个函数检查丢失的括号并检查每种类型是否正确平衡。它不允许a)b(c,但它确实允许(a[bc)d],因为每种类型都是单独检查的。

      function checkBrackets( str ) {
          var lb, rb, li, ri,
              i = 0,
              brkts = [ '(', ')', '{', '}', '[', ']' ];   
          while ( lb = brkts[ i++ ], rb = brkts[ i++ ] ) { 
              li = ri = 0;
              while ( li = str.indexOf( lb, li ) + 1 ) {
                  if ( ( ri = str.indexOf( rb, ri ) + 1 ) < li ) {
                      return false;
                  }
              }
              if ( str.indexOf( rb, ri ) + 1 ) {
                  return false;
              } 
          }
          return true;
      }
      

      最后,关于 Christophe 的帖子,这里似乎是检查缺少括号并检查所有括号是否正确平衡和嵌套的最佳解决方案:

      function checkBrackets( str ) {
          var s;
          str = str.replace( /[^{}[\]()]/g, '' );
          while ( s != str ) { 
              s = str;
              str = str.replace( /{}|\[]|\(\)/g, '' )
          }
          return !str;
      };
      
      checkBrackets( 'ab)cd(efg' );        // false   
      checkBrackets( '((a)[{{b}}]c)' );    // true   
      checkBrackets( 'ab[cd]efg' );        // true   
      checkBrackets( 'a(b[c)d]e' );        // false   
      

      【讨论】:

      • 后一个函数,因为它分别检查各种类型,所以会为'a(b[c)d]e' 之类的输入生成true
      • @Nikita Kouevda。好点子。谢谢你。我应该说“检查每种类型是否正确平衡”。我已经用一个新版本替换了这个函数,它还检查所有类型是否正确嵌套。
      • @NikitaKouevda 这里的目的是检测截断的字符串,而不是输入错误,所以这种技术看起来还不错。
      • @Christophe 抱歉,我并不是要过分批评;由于as is this} bad 的情况,我认为该问题需要更彻底的解决方案,这不能仅是截断的结果。
      • @NikitaKouevda 我明白你的意思,我已经发布了更通用案例的替代方案。
      【解决方案4】:

      您不能在正则表达式本身中进行递归,但您始终可以在 JavaScript 中进行。

      这是一个例子:

      // First remove non-brackets:
      string=string.replace(/[^{}[\]()]/g,"");
      // Then remove bracket pairs recursively
      while (string!==oldstring) {
        oldstring=string;
        string=string.replace(/({}|\[\]|\(\))/g,"");
      }
      

      其余的是不匹配的括号。

      现场演示:http://jsfiddle.net/3Njzv/

      如果您需要计算对数,您可以一次更换一个并添加一个计数器:

      // First remove non-brackets:
      string=string.replace(/[^{}[\]()]/g,"");
      
      // Then remove bracket pairs recursively
      var counter=-1;
      while (string!==oldstring) {
        counter ++;
        oldstring=string;
        string=string.replace(/({}|\[\]|\(\))/,"");
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-07
        • 2016-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多