从字符串中删除不匹配的括号

Posted

技术标签:

【中文标题】从字符串中删除不匹配的括号【英文标题】:Remove unmatched parentheses from a string 【发布时间】:2011-07-22 10:48:19 【问题描述】:

我想从字符串中删除“非合作”括号。

即,所有( 都应该被删除,除非它们后面跟着一个) 在字符串中的某处。同样,所有) 前面没有( 在字符串中的某处都应该被删除。

理想情况下,算法也会考虑嵌套。

例如:

"(a)".remove_unmatched_parents # => "(a)"
"a(".remove_unmatched_parents # => "a"
")a(".remove_unmatched_parents # => "a"

【问题讨论】:

【参考方案1】:

也许可以考虑下推自动机,而不是正则表达式。 (我不确定 Ruby 正则表达式是否可以处理这个问题,我相信 Perl 可以)。

一个(非常琐碎的)过程可能是:

对于输入字符串中的每个字符:

    如果不是 '(' 或 ')' 则只需将其附加到输出中 如果是 '(' 则增加 seen_parens 计数器并添加它 如果它是')' 并且 seen_parens > 0,添加它并减少seen_parens。否则跳过它。

在过程结束时,如果 seen_parens > 0,则从末尾开始删除那么多括号。 (此步骤可以通过堆栈或递归的方式合并到上述过程中。)

整个过程是O(n),即使开销比较高

编码愉快。

【讨论】:

确实如此。这是字面上在世界范围内用于教学的示例,该示例无法使用正则表达式进行解析。现在,Ruby 的Regexp 比正则表达式强大得多,它们实际上可以解析这种语言,但它并不完全可维护。您可以在更短的时间内编写一个简单的递归下降解析器或下推自动机,甚至比您读取别人交给您的Regexp 所花费的时间还短,更不用说编写您自己的了。如果您将Regexp 拆分为多行以放入 cmets,那么自动机甚至会更短。 感谢算法。我的回答 (***.com/questions/5424959/…) 对你来说合适吗?【参考方案2】:

以下使用oniguruma。如果您使用的是 ruby​​1.9,Oniguruma 是内置的正则表达式引擎。如果您使用的是 ruby​​1.8,请参阅:oniguruma。

更新

我一直懒得只是复制和粘贴别人的正则表达式。好像有问题。

所以现在,我自己写了。我相信它现在应该可以工作了。

class String
    NonParenChar = /[^\(\)]/
    def remove_unmatched_parens
        self[/
            (?:
                (?<balanced>
                    \(
                        (?:\g<balanced>|#NonParenChar)*
                    \)
                )
                |#NonParenChar
            )+
        /x]
    end
end
(?&lt;name&gt;regex1) 将(子)正则表达式 regex1 命名为 name,并使其可以被调用。 ?g&lt;name&gt; 将是一个代表regex1 的子正则表达式。注意这里?g&lt;name&gt; 不代表匹配regex1 的特定字符串,而是代表regex1 本身。实际上,可以将?g&lt;name&gt; 嵌入到(?&lt;name&gt;...) 中。

更新 2

这更简单。

class String
    def remove_unmatched_parens
        self[/
            (?<valid>
                \(\g<valid>*\)
                |[^()]
            )+
        /x]
    end
end

【讨论】:

@pst 上一个居然有问题。所以现在,我自己写了一个不同的正则表达式。这一次,应该没问题。希望不要爆炸。【参考方案3】:

这是我的解决方案,基于@pst 的算法:

class String
  def remove_unmatched_parens
    scanner = StringScanner.new(dup)
    output = ''
    paren_depth = 0

    while char = scanner.get_byte
      if char == "("
        paren_depth += 1
        output << char
      elsif char == ")"
        output << char and paren_depth -= 1 if paren_depth > 0
      else
        output << char
      end
    end

    paren_depth.times output.reverse!.sub!('(', '').reverse! 
    output
  end
end

【讨论】:

那么,remove_unmatched_parents 是错字吗?我是这么想的。【参考方案4】:

构建一个简单的 LR 解析器:

tokenize, token, stack = false, "", []

")(a))(()(asdf)(".each_char do |c|
  case c
  when '('
    tokenize = true
    token = c
  when ')'
    if tokenize
      token << c 
      stack << token
    end
    tokenize = false
  when /\w/
    token << c if tokenize
  end
end

result = stack.join

puts result

运行产量:

wesbailey@feynman:~/code_katas> ruby test.rb
(a)()(asdf)

我不同意那些修改 String 类的人,因为你永远不应该打开一个标准类。正则表达式对于解析器来说非常脆弱并且难以支持。我无法想象 6 个月后回到以前的解决方案并试图记住他们在做什么!

【讨论】:

【参考方案5】:

算法:

    遍历给定的字符串。 在执行此操作时,跟踪堆栈中的“(”位置。 如果找到任何“)”,则从堆栈中删除顶部元素。 如果堆栈为空,则从字符串中删除“)”。 最后,我们可以有不匹配的大括号的位置,如果有的话。

Java 代码: 出席@http://a2ajp.blogspot.in/2014/10/remove-unmatched-parenthesis-from-given.html

【讨论】:

以上是关于从字符串中删除不匹配的括号的主要内容,如果未能解决你的问题,请参考以下文章

如何使用正则表达式删除字符串中的括号符号

正则表达式从具有多个方括号的字符串中查找匹配项

如何用正则表达式匹配括号中的内容,不包含括号

C# Regex - 如何从字符串中删除多个成对的括号

LeetCode 301 删除无效的括号[dfs 字符串] HERODING的LeetCode之路

用栈实现括号匹配的检验