如何在正则表达式中获取(可能嵌套的)捕获组?

时间:2012-03-15 22:26:33

标签: ruby regex

给出正则表达式:

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

2 个答案:

答案 0 :(得分:2)

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

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

您可能还想实施:

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

    来自评论的

    更新

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

答案 1 :(得分:1)

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

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

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

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

定义完整的正则表达式语法并将正则表达式解析为可步行树。然后,我能够走树,挑出我想要的树的部分(捕获组)。

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

这是我的Regexp补丁,它使用固定的宝石:

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\/(.*)$/

我希望这可以帮助其他人。