2013-05-04 3 views
5

У меня возникла проблема с регулярным выражением ruby. Мне нужно найти все (потенциально совпадающие) совпадения. Это упрощение задачи:Ruby Regex, получить все возможные совпадения (без обрезки строки)

#Simple example 
"Hey".scan(/../) 
=> ["He"] 
#Actual results 

#With overlapping matches the result should be 
=> ["He"], ["ey"] 

Регулярное выражение Я пытаюсь выполнить и получить все результаты для выглядит следующим образом:

"aaaaaa".scan(/^(..+)\1+$/) #This looks for multiples of (here) "a" bigger than one that "fills" the entire string. "aa"*3 => true, "aaa"*2 => true. "aaaa"*1,5 => false. 
=> [["aaa"]] 

#With overlapping results this should be 
=> [["aa"],["aaa"]] 

Есть библиотека или способ сделать регулярное выражение в рубине для получения результатов, которые я получаю?

Я нашел некоторые подсказки, что это было возможно в Perl, но после нескольких часов исследований я ничего не нашел о способе Ruby сделать это.

Однако я смог найти это «Javascript Regex - Find all possible matches, even in already captured matches», но я не смог найти ничего похожего на Ruby и не нашел что-то похожее на последнее свойство индекса в версии Ruby. Честно говоря, я не думаю, что это сработало бы, так как регулярное выражение, которое я намереваюсь использовать, является рекурсивным и полагается на всю строку, в то время как этот метод отбрасывает строку.

+0

Итак, в основном вы хотите перестановку строки? – HamZa

+0

Нет. Это просто пример. Реальное регулярное выражение немного сложнее, но это проблема, которая возникает. Метод сканирования отрывает части найденной строки и продолжается. Мне нужно, чтобы он сохранил строку, чтобы ее можно было просмотреть с помощью следующего возможного совпадения. – Automatico

+0

Я не уверен, но первый и второй примеры немного противоречат друг другу. Следуя первому примеру, я бы подумал, что второй должен вернуть '[aa, aa, aa, aa, aa ...., aaa, aaa, aaa ..., aaaa, aaaa, aaaa ...., aaaaa , aaaaa, aaaaaa] ' – HamZa

ответ

5

Вид старой теме ... Не уверен, если Я понимаю, но лучше я могу найти это:

"Hey".scan(/(?=(..))/) 
=> [["He"], ["ey"]] 

"aaaaaa".scan(/(?=(..+)\1)/) 
=> [["aaa"], ["aa"], ["aa"]] 

сканирование проходит через каждый байт и «позитивный прогностический» (?=) проверяет регулярное выражение (..+)\1 в каждом S теп. Упреждающие ожидания не потребляют байты, но группа захвата внутри нее возвращает совпадение, если оно существует.

0
class String 
    def awesome_regex_scan(pattern) 
    result = [] 
    source = self 
    while (match = source.match(pattern)) 
     result << match.to_s 
     source = source.slice(match.begin(0)+1..-1) 
    end 
    result 
    end 
end 

p "Hey".awesome_regex_scan(/../) 
3

Вам не хватает второй группы захвата?

"aaaaaa".scan(/(..+?)(\1+)/) 
#=> [["aa", "aaaa"]] 

Возможно, что-то не так с вашим ожиданием.

+1

** Как босс + 1 ** – HamZa

+0

Знаете что. Я просто понял, что у меня была небольшая ошибка. – Automatico

+0

Но предлагаемые решения все еще не работали с исходной проблемой. Это регулярное выражение должно запускаться и на 'aaa'. – Automatico

3

Проблема с любым решением, основанным на scan, не находит совпадающих совпадений, так как scan всегда делает продвижение вперед. Возможно, можно переписать регулярное выражение так, чтобы оно было полностью внедрено в позиционный look-head с нулевой шириной, а затем использовало scan, но в противном случае существуют другие шаблоны регулярных выражений, которые не работают в lookahead или lookbehind.

В заданном вопросе есть какая-то двусмысленность. Это интерпретирует вопрос как действительно просящий найти все уникальные подстроки соответствия целевой строки, для которой будет соответствовать регулярное выражение. Хотя он не является строго необходимым, он использует рубиновую 2.0 ленивую оценку, чтобы избежать избыточных промежуточных распределений массивов.

class String 
    def each_substring 
    Enumerator.new do |y| 
     (0...length).each do |b| 
     (b...length).each do |e| 
      y << self[b..e] 
     end 
     end 
     y << '' 
    end 
    end 
end 

class Regexp 
    def all_possible_matches(str) 
    str.each_substring.lazy. 
    map { |s| match(s) }. 
    reject(&:nil?). 
    map { |m| m.size > 1 ? m[1..-1] : m[0] }. 
    to_a.uniq 
    end 
end 

/.{2,4}/.all_possible_matches('abcde') 
=> ["ab", "abc", "abcd", "bc", "bcd", "bcde", "cd", "cde", "de"] 

/^(..+?)\1+$/.all_possible_matches('aaaaaa') 
=> [["aa"]] 
/^(..+)\1+$/.all_possible_matches('aaaaaa') 
=> [["aa"], ["aaa"]] 
/^(..+?)\1+$/.all_possible_matches('aaaaaaaaa') 
=> [["aa"], ["aaa"]] 
/^(..+)\1+$/.all_possible_matches('aaaaaaaaa') 
=> [["aa"], ["aaa"], ["aaaa"]] 

РЕДАКТИРОВАТЬ: они были возвращены группами захвата в случае присутствия. Желаемое решение OP для неживой формы /^(..+?)\1+$/ ошибочно, так как ? означает, что он будет удовлетворен шаблоном с наименьшим количеством символов.

+0

Я получаю 'undefined method 'lazy' для # Automatico

+0

@ Cort3z Как я уже сказал в ответе, ['lazy'] (http://ruby-doc.org/core-2.0/Enumerable.html#method-i-lazy) является особенностью рубина 2,0. В 1.9 вы можете просто опустить его, и он должен работать нормально, просто произведите более промежуточные результаты. – dbenhur

1

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

class String 
    def awesome_regex_scan r 
    (0...length).map{|i| match(r, i)}.map(&:to_a).reject(&:empty?).uniq 
    end 
end 

"Hey".awesome_regex_scan(/../) # => [["He"], ["ey"]] 

Как написано выше, это не соответствует вашему ожидаемого результата, и я не понимаю, почему вы ожидаете, что вы делаете:

"aaaaaa".awesome_regex_scan(/^(..+?)\1+$/) # => [["aaaaaa", "aa"]] 
"aaaaaa".awesome_regex_scan(/^(..+)\1+$/) # => [["aaaaaa", "aaa"]] 
Смежные вопросы