2011-06-23 14 views
9

Chciałbym napisać funkcję/moduł narzędzia, który zapewni proste dopasowanie symboli wieloznacznych do ciągów. Powodem, dla którego nie używam wyrażeń regularnych, jest to, że użytkownik będzie tym, który w końcu dostarczy wzorce do dopasowania za pomocą jakiegoś pliku konfiguracyjnego. Nie mogłem znaleźć takiego klejnotu, który byłby stabilny - próbował jokera, ale miał problemy z konfiguracją.Dopasowywanie ciągów znaków wieloznacznych w Ruby

Funkcja, której szukam, jest prosta. Na przykład, biorąc pod uwagę następujące wzorce, tutaj są dopasowania:

pattern | test-string   | match 
========|=====================|==================== 
*hn  | john, johnny, hanna | true , false, false  # wildcard , similar to /hn$/i 
*hn* | john, johnny, hanna | true , true , false  # like /hn/i 
hn  | john, johnny, hanna | false, false, false  # /^hn$/i 
*h*n* | john, johnny, hanna | true , true , true 
etc... 

Chciałbym, aby było tak wydajnie, jak to możliwe. Myślałem o tworzeniu wyrażeń regularnych z ciągów znaków, ale w środowisku wykonawczym było to raczej nieefektywne. Wszelkie sugestie dotyczące tej implementacji? dzięki.

EDIT: Używam Ruby 1.8.7

Odpowiedz

13

nie widzę dlaczego uważasz, że byłoby to nieefektywne. Prognozy na temat tego typu rzeczy są notorycznie niewiarygodne, powinieneś zdecydować, że jest zbyt wolno, zanim pójdziesz do tyłu, aby znaleźć szybszy sposób. A następnie powinieneś go profilować, aby upewnić się, że jest to problem (przy 3-4 krotnym przyspieszeniu z przejścia do 1,9).

W każdym razie powinno być całkiem łatwo to zrobić, coś w stylu :

class Globber 
    def self.parse_to_regex(str) 
    escaped = Regexp.escape(str).gsub('\*','.*?') 
    Regexp.new "^#{escaped}$", Regexp::IGNORECASE 
    end 

    def initialize(str) 
    @regex = self.class.parse_to_regex str 
    end 

    def =~(str) 
    !!(str =~ @regex) 
    end 
end 


glob_strs = { 
    '*hn' => [['john', true, ], ['johnny', false,], ['hanna', false]], 
    '*hn*' => [['john', true, ], ['johnny', true, ], ['hanna', false]], 
    'hn'  => [['john', false,], ['johnny', false,], ['hanna', false]], 
    '*h*n*' => [['john', true, ], ['johnny', true, ], ['hanna', true ]], 
} 

puts glob_strs.all? { |to_glob, examples| 
    examples.all? do |to_match, expectation| 
    result = Globber.new(to_glob) =~ to_match 
    result == expectation 
    end 
} 
# >> true 
+0

myślę, że w przypadku '„* hn'' na przykład, że potrzebuje '” John awesome'' zwrócić także true, a przy '/.* hn $ /' nie będzie pasować –

+0

Nie wydaje się być tak jak globs działa na moim komputerze (Mac OSX Leopard) https://gist.github.com/1041942 –

+0

I załóżmy, że symbol wieloznaczny jest dokładniejszy niż glob dla mojego celu - dla przypadku na '' * hn'' chciałbym e wszystko przed i do wzorca do dopasowania, i nic po; więc 'true' dla' 'john'',' false' for ''john is ..''. dzięki – sa125

1
def create_regex(pattern) 
if pattern[0,1] != '*' 
    pattern = '[^\w\^]' + pattern 
end 
if pattern[-1,1] != '*' 
    pattern = pattern + '[^\w$]' 
end 
return Regexp.new(pattern.gsub(/\*/, '.*?')) 
end 

Ten methoid należy zwrócić regexp

PS: to nie jest badana: D

+2

zrobić kilka edycji - dzięki za wskazanie błędów składni - zbyt dużo perl/php: D –

Powiązane problemy