【问题标题】:Wildcard string matching in RubyRuby 中的通配符字符串匹配
【发布时间】:2011-06-23 04:33:53
【问题描述】:

我想编写一个实用函数/模块,它将提供简单的通配符/glob 匹配字符串。我不使用正则表达式的原因是用户最终将使用某种配置文件提供匹配的模式。我找不到任何稳定的宝石 - 尝试过小丑,但设置有问题。

我正在寻找的功能很简单。例如,给定以下模式,以下是匹配项:

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

我希望它尽可能高效。我考虑过从模式字符串创建正则表达式,但这在运行时似乎效率很低。对此实施有何建议?谢谢。

编辑:我使用的是 ruby​​ 1.8.7

【问题讨论】:

    标签: ruby pattern-matching glob string-matching


    【解决方案1】:

    我不明白你为什么认为它会低效。对这类事情的预测是出了名的不可靠,在你向后弯腰寻找更快的方法之前,你应该确定它太慢了。然后你应该分析它以确保这是问题所在(顺便说一句,从切换到 1.9 平均速度提升 3-4 倍)

    无论如何,这样做应该很容易,例如:

    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
    

    【讨论】:

    • 我认为以'*hn'为例,他需要'john is awesome'也返回true,并且与/.*hn$/不匹配
    • 这似乎不是 glob 在我的计算机 (Mac OSX Leopard) 上工作的方式gist.github.com/1041942
    • 对于我的目的,我想通配符比 glob 更准确 - 对于'*hn' 上的情况,我希望匹配模式之前和之前的所有内容,之后什么都没有;所以true 对应'john'false 对应'john is ..'。谢谢
    • 这与这个解决方案是一致的。
    【解决方案2】:
    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
    

    这个方法应该返回你的正则表达式

    PS:它没有经过测试:D

    【讨论】:

    • 做了一些编辑 - 感谢您指出语法错误 - perl/php 太多:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2015-07-29
    • 1970-01-01
    • 2018-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多