2011-06-23 4 views
9

Je voudrais écrire une fonction/un module utilitaire qui fournira une simple correspondance générique/glob à des chaînes. La raison pour laquelle je n'utilise pas d'expressions régulières est que l'utilisateur sera celui qui finira par fournir les modèles à faire correspondre en utilisant un certain type de fichier de configuration. Je ne pouvais pas trouver un tel gem qui est stable - joker essayé, mais il a eu des problèmes de configuration.Chaîne de caractère générique correspondant dans Ruby

La fonctionnalité que je recherche est simple. Par exemple, étant donné les modèles suivants, voici les correspondances:

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... 

Je voudrais que ce soit aussi efficace que possible. J'ai pensé à créer des regex à partir des chaînes de pattern, mais cela semblait plutôt inefficace à faire à l'exécution. Des suggestions sur cette implémentation? Merci.

EDIT: J'utilise Ruby 1.8.7

Répondre

13

Je ne vois pas pourquoi pensez-vous que ce serait inefficace. Les prédictions sur ce genre de choses sont notoirement peu fiables, vous devriez décider que c'est trop lent avant de vous pencher en arrière pour trouver un moyen plus rapide. Et puis vous devez le profiler pour vous assurer que c'est là que se situe le problème (par exemple, il y a une augmentation moyenne de 3-4x de la commutation à 1.9)

Quoi qu'il en soit, cela devrait être assez facile, quelque chose comme :

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

Je pense que dans le cas de ' « * hn'' par exemple, il a besoin ' » john est awesome'' pour retourner vrai aussi, et avec '/.* hn $ /' ne correspondra pas –

+0

Ne semble pas être la façon dont les globs fonctionnent sur mon ordinateur (Mac OSX Leopard) https://gist.github.com/1041942 –

+0

I supposons que le joker est plus précis que glob pour mon but - pour le cas sur ''* hn' e tout avant et jusqu'à-le modèle pour correspondre, et rien après; donc 'true' pour' 'john'',' false' pour '' john is .. ''. merci – 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 

Ce methoid devrait retourner votre regexp

PS: il n'a pas été testé: D

+2

fait des modifications - merci pour signaler les erreurs de syntaxe - trop perl/php: D –

Questions connexes