2013-05-04 16 views
5

Wystąpił problem z regex ruby. Muszę znaleźć wszystkie (potencjalnie nakładające się) mecze. Jest to uproszczenie problemu:Ruby Regex, uzyskać wszystkie możliwe dopasowania (bez przycinania ciąg)

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

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

regex Próbuję wykonać i uzyskać wszystkie wyniki dla wygląda następująco:

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

Czy istnieje biblioteka lub sposób zrobić regex w ruby uzyskać wyniki, których szukam?

Znalazłem kilka wskazówek, że było to możliwe w Perlu, ale po wielu godzinach badań nie znalazłem nic o sposobie na to Ruby.

Jednak udało mi się znaleźć "Javascript Regex - Find all possible matches, even in already captured matches", ale nie mogę znaleźć niczego podobnego dla Rubiego, ani znaleźć coś podobnego do ostatniej właściwości indeksu w wersji Ruby. Szczerze mówiąc nie sądzę, że i tak by zadziałało, ponieważ wyrażeń regularnych, które zamierzam użyć, jest rekursywne i opiera się na całym łańcuchu, podczas gdy ta metoda odciąga łańcuch.

+0

Więc właściwie chcesz permutacji ciągu? – HamZa

+0

Nie. To tylko przykład. Rzeczywiste wyrażenie regularne jest nieco trudniejsze, ale jest to problem, który pojawia się. Metoda skanowania usuwa fragmenty znalezionego ciągu i kontynuuje. Potrzebuję go, aby zachować ciąg, aby można było go obejrzeć w następnym możliwym meczu. – Automatico

+0

Nie jestem pewien, ale pierwszy i drugi przykład są nieco sprzeczne. Podążając za pierwszym przykładem, myślę, że drugi powinien zwrócić: [aa, aa, aa, aa, aa ..., aaa, aaa, aaa ..., aaaa, aaaa, aaaa ..., aaaa , aaaaa, aaaaa] ' – HamZa

Odpowiedz

5

trochę stary temat ... nie wiem, czy rozumiem, ale najlepsze, co mogę znaleźć to:

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

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

skanowania idzie thru każdy bajt i „pozytywnej antycypowana” (?=) testuje regexp (..+)\1 w każdy s tep. Z wyprzedzeniem nie zużywają one bajtów, ale grupa przechwytująca w środku zwraca dopasowanie, jeśli istnieje.

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

Po prostu brakuje Ci drugiej grupy przechwytującej?

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

Wygląda na to, że coś może być nie tak z oczekiwaniami.

+1

** Jak szef + 1 ** – HamZa

+0

Wiesz co. Właśnie zdałem sobie sprawę, że popełniłem mały błąd. – Automatico

+0

Ale sugerowane rozwiązania nadal nie działały z pierwotnym problemem. To wyrażenie regularne powinno również uruchamiać na 'aaa'. – Automatico

3

Problem z dowolnym rozwiązaniem opartym na scan polega na tym, że nie zawsze będzie się on nakładał na mecze, ponieważ scan zawsze robi postępy. Może być możliwe przekształcenie wyrażeń regularnych tak, aby był całkowicie osadzony w dodatkowym przedziale ujemnym o szerokości równej zeru, a następnie użycie scan, ale w IIRC istnieją inne poprawne wzorce regexp, które nie działają z wyprzedzeniem lub z lookbehind.

Występuje pewna niejasność w zadawanym pytaniu. To interpretuje pytanie jako naprawdę wymagające znalezienia wszystkich unikatowych pasujących fragmentów ciągu docelowego, dla którego dopasuje wyrażenie regularne. Choć nie jest to bezwzględnie konieczne, wykorzystuje leniwą ocenę ruby ​​2.0, aby uniknąć nadmiernych alokacji tablicy pośredniej.

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

EDYCJA: sprawił, że zwrócił grupy przechwytywania, gdy są obecne. Pożądane rozwiązanie OP dla nieagresywnej postaci /^(..+?)\1+$/ jest nieprawidłowe, ponieważ ? oznacza, że ​​będzie zadowolony z wzoru z najmniejszą liczbą znaków.

+0

Otrzymuję 'nieokreśloną metodę 'leniwą' dla # Automatico

+0

@ Cort3z Jak stwierdziłem w odpowiedzi, ['lazy'] (http://ruby-doc.org/core-2.0/Enumerable.html#method-i-lazy) jest cechą ruby 2.0. W wersji 1.9 można go po prostu pominąć i powinno działać dobrze, wystarczy uzyskać bardziej pośrednie wyniki. – dbenhur

1

Nie rozumiem, dlaczego oczekiwane wyniki powinny być takie, ale w przypadku zastosowania wyrażenia regularnego z różnych punktów początkowych, zrobi to.

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

Jak napisano powyżej, to nie pasuje do oczekiwanego rezultatu, a ja nie rozumiem, dlaczego można się spodziewać, co robisz:

"aaaaaa".awesome_regex_scan(/^(..+?)\1+$/) # => [["aaaaaa", "aa"]] 
"aaaaaa".awesome_regex_scan(/^(..+)\1+$/) # => [["aaaaaa", "aaa"]] 
Powiązane problemy