【问题标题】:How do I obtain the (possibly nested) capture groups in a regular expression?如何在正则表达式中获取(可能是嵌套的)捕获组?
【发布时间】:2012-03-15 22:26:32
【问题描述】:

给定一个正则表达式:

/say (hullo|goodbye) to my lovely (.*)/

还有一个字符串:

"my $2 is happy that you said $1"

从包含正则表达式中的捕获组的字符串中获取正则表达式的最佳方法是什么?那就是:

/my (.*) is happy that you said (hullo|goodbye)/

显然我可以在原始正则表达式的字符串表示上使用正则表达式,但这可能会给嵌套捕获组带来困难。

我正在使用 Ruby。到目前为止,我的简单实现大致如下:

class Regexp
  def capture_groups
    self.to_s[1..-2].scan(/\(.*?\)/)
  end
end

regexp.capture_groups.each_with_index do |capture, idx|
  string.gsub!("$#{idx+1}", capture)
end
/^#{string}$/

【问题讨论】:

  • 感谢您的关注 - 我已经把问题说得更清楚了(我希望如此)。
  • 我不明白为什么这被否决或被投票关闭。对于 OP,您可能只想调查现有的正则表达式引擎,或者看看是否有办法查看现有引擎(IIRC 主要是 C)。
  • 谢谢戴夫。我在 C 中不是特别舒服!而且我不确定为什么这会被否决。也许人们没有意识到这个问题不仅仅是简单的字符串处理。

标签: ruby regex


【解决方案1】:

我想您需要创建自己的函数来执行此操作:

  • 创建空字典groupsactive_groups并初始化counter = 1
  • 遍历字符串表示中的字符:
    • 如果当前字符 = '(' 和前一个字符 != \
      • counter 键添加到active_groups 并增加counter
    • 将当前字符添加到所有active_groups
    • 如果当前字符 = ')' 和前一个字符 != \:
      • active_groups 中删除最后一项(key, value) 并将其添加到groups
  • 如果需要,将groups 转换为数组

您可能还想实现:

  • ignore = True 在未转义的 '['']' 之间
  • 如果当前字符 = '|'active_groups 为空,则重置 counter(如果 active_group 不为空,则减少 counter

    更新来自 cmets:

  • '(?:' 开头的非捕获组

【讨论】:

  • 是的,需要构建足够的解析器来识别捕获组。注意(?:.*) 不是捕获。
【解决方案2】:

所以一旦我意识到我真正需要的是一个正则表达式解析器,事情就开始落实到位。我发现了这个项目:

它可以生成匹配正则表达式的字符串。它使用http://treetop.rubyforge.org/ 定义了一个正则表达式语法。不幸的是,它定义的语法是不完整的,虽然在很多情况下很有用。

我还偶然发现了https://github.com/mjijackson/citrus,它的作用与 Treetop 类似。

然后我发现了这个令人兴奋的宝石:

它定义了一个完整的正则表达式语法并将正则表达式解析成一个可行走的树。然后,我能够在树上行走并挑选出我想要的树的部分(捕获组)。

不幸的是,在我的 fork 中修复了一个小错误:https://github.com/LaunchThing/regexp_parser

这是我的 Regexp 补丁,它使用了固定的 gem:

class Regexp
  def parse
    Regexp::Parser.parse(self.to_s, 'ruby/1.9')
  end

  def walk(e = self.parse, depth = 0, &block)
    block.call(e, depth)
    unless e.expressions.empty?
      e.each do |s| 
        walk(s, depth+1, &block) 
      end
    end
  end

  def capture_groups
    capture_groups = []
    walk do |e, depth|
      capture_groups << e.to_s if Regexp::Expression::Group::Capture === e
    end
    capture_groups
  end
end

然后我可以在我的应用程序中使用它来在我的字符串中进行替换 - 最终目标 - 按照这些思路:

from = /^\/search\/(.*)$/
to = '/buy/$1'

to_as_regexp = to.dup

# I should probably make this gsub tighter
from.capture_groups.each_with_index do |capture, idx|
  to_as_regexp.gsub!("$#{idx+1}", capture)
end
to_as_regexp = /^#{to_as_regexp}$/

# to_as_regexp = /^\/buy\/(.*)$/

我希望这对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    • 2020-10-03
    • 1970-01-01
    相关资源
    最近更新 更多