Как получить (возможно, вложенные) группы захвата в регулярном выражении? - PullRequest
0 голосов
/ 16 марта 2012

Дано регулярное выражение:

/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 ]

2 голосов
/ 16 марта 2012

Полагаю, вам нужно создать собственную функцию, которая бы делала это:

  • создать пустые словари groups и active_groups и инициализировать counter = 1
  • перебрать символы в строковом представлении:
    • если текущий символ = '(' и предыдущий символ! = \:
      • добавить counter ключ к active_groups и увеличить counter
    • добавить текущий символ ко всем active_groups
    • если текущий символ = ')' и предыдущий символ! = \:
      • удалить последний элемент (ключ, значение) из active_groups и добавить его к groups
  • преобразовать groups в массив, если необходимо

Вы также можете реализовать:

  • ignore = True между неоткрытыми '[' и ']'
  • сброс counter, если текущий символ = '|' и active_groups пусто (или уменьшить counter, если active_group не пусто)

    ОБНОВЛЕНИЯ из комментариев:

  • входящие в группу группы без захвата, начиная с '(?:'
1 голос
/ 17 марта 2012

Итак, как только я понял, что мне действительно нужен парсер регулярных выражений, все стало на свои места. Я обнаружил этот проект:

, который может генерировать строки, которые соответствуют регулярному выражению. Он определяет грамматику регулярного выражения, используя 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\/(.*)$/

Надеюсь, это поможет кому-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...