在Ruby 1.9.3上使用花括号进行通配 [英] Globbing using braces on Ruby 1.9.3

查看:88
本文介绍了在Ruby 1.9.3上使用花括号进行通配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果使用File :: FNM_EXTGLOB选项,则最新版本的Ruby支持在括号中使用花括号

Recent versions of Ruby support the use of braces in globbing, if you use the File::FNM_EXTGLOB option

来自 2.2.0文档

File.fnmatch('c{at,ub}s', 'cats', File::FNM_EXTGLOB) #=> true  # { } is supported on FNM_EXTGLOB

但是,1.9.3文档说它在1.9.3中不受支持:

However, the 1.9.3 documentation says it isn't supported in 1.9.3:

File.fnmatch('c{at,ub}s', 'cats')       #=> false # { } isn't supported

(同样,尝试使用File::FNM_EXTGLOB给出了名称错误)

(also, trying to use File::FNM_EXTGLOB gave a name error)

在Ruby 1.9.3中,是否有任何方法可以使用花括号(例如第三方gem)?

Is there any way to glob using braces in Ruby 1.9.3, such as a third-party gem?

我要匹配的字符串来自S3,而不是本地文件系统,所以我不能只问操作系统就我所知进行遍历.

The strings I want to match against are from S3, not a local file system, so I can't just ask the operating system to do the globbing as far as I know.

推荐答案

我正在打包 Ruby Backport 可支持大括号.这是该解决方案的重要组成部分:

I'm in the process of packaging up a Ruby Backport for braces globbing support. Here are the essential parts of that solution:

module File::Constants
  FNM_EXTGLOB = 0x10
end

class << File
  def fnmatch_with_braces_glob(pattern, path, flags =0)
    regex = glob_convert(pattern, flags)

    return regex && path.match(regex).to_s == path
  end

  def fnmatch_with_braces_glob?(pattern, path, flags =0)
    return fnmatch_with_braces_glob(pattern, path, flags)
  end

private
  def glob_convert(pattern, flags)
    brace_exp = (flags & File::FNM_EXTGLOB) != 0
    pathnames = (flags & File::FNM_PATHNAME) != 0
    dot_match = (flags & File::FNM_DOTMATCH) != 0
    no_escape = (flags & File::FNM_NOESCAPE) != 0
    casefold = (flags & File::FNM_CASEFOLD) != 0
    syscase = (flags & File::FNM_SYSCASE) != 0
    special_chars = ".*?\\[\\]{},.+()|$^\\\\" + (pathnames ? "/" : "")
    special_chars_regex = Regexp.new("[#{special_chars}]")

    if pattern.length == 0 || !pattern.index(special_chars_regex)
      return Regexp.new(pattern, casefold || syscase ? Regexp::IGNORECASE : 0)
    end

    # Convert glob to regexp and escape regexp characters
    length = pattern.length
    start = 0
    brace_depth = 0
    new_pattern = ""
    char = "/"

    loop do
      path_start = !dot_match && char[-1] == "/"

      index = pattern.index(special_chars_regex, start)

      if index
        new_pattern += pattern[start...index] if index > start
        char = pattern[index]

        snippet = case char
        when "?"  then path_start ? (pathnames ? "[^./]" : "[^.]") : ( pathnames ? "[^/]" : ".")
        when "."  then "\\."
        when "{"  then (brace_exp && (brace_depth += 1) >= 1) ? "(?:" : "{"
        when "}"  then (brace_exp && (brace_depth -= 1) >= 0) ? ")" : "}"
        when ","  then (brace_exp && brace_depth >= 0) ? "|" : ","
        when "/"  then "/"
        when "\\"
          if !no_escape && index < length
            next_char = pattern[index += 1]
            special_chars.include?(next_char) ? "\\#{next_char}" : next_char
          else
            "\\\\"
          end
        when "*"
          if index+1 < length && pattern[index+1] == "*"
            char += "*"
            if pathnames && index+2 < length && pattern[index+2] == "/"
              char += "/"
              index += 2
              "(?:(?:#{path_start ? '[^.]' : ''}[^\/]*?\\#{File::SEPARATOR})(?:#{!dot_match ? '[^.]' : ''}[^\/]*?\\#{File::SEPARATOR})*?)?"
            else
              index += 1
              "(?:#{path_start ? '[^.]' : ''}(?:[^\\#{File::SEPARATOR}]*?\\#{File::SEPARATOR}?)*?)?"
            end
          else
            path_start ? (pathnames ? "(?:[^./][^/]*?)?" : "(?:[^.].*?)?") : (pathnames ? "[^/]*?" : ".*?")
          end
        when "["
          # Handle character set inclusion / exclusion
          start_index = index
          end_index = pattern.index(']', start_index+1)
          while end_index && pattern[end_index-1] == "\\"
            end_index = pattern.index(']', end_index+1)
          end
          if end_index
            index = end_index
            char_set = pattern[start_index..end_index]
            char_set.delete!('/') if pathnames
            char_set[1] = '^' if char_set[1] == '!'
            (char_set == "[]" || char_set == "[^]") ? "" : char_set
          else
            "\\["
          end
        else
          "\\#{char}"
        end

        new_pattern += snippet
      else
        if start < length
          snippet = pattern[start..-1]
          new_pattern += snippet
        end
      end

      break if !index
      start = index + 1
    end

    begin
      return Regexp.new("\\A#{new_pattern}\\z", casefold || syscase ? Regexp::IGNORECASE : 0)
    rescue
      return nil
    end
  end
end

此解决方案考虑了 File :: fnmatch 函数,并使用全局模式构建合适的 Regexp 以匹配功能.使用此解决方案,这些测试可以成功运行:

This solution takes into account the various flags available for the File::fnmatch function, and uses the glob pattern to build a suitable Regexp to match the features. With this solution, these tests can be run successfully:

File.fnmatch('c{at,ub}s', 'cats', File::FNM_EXTGLOB)
#=> true
File.fnmatch('file{*.doc,*.pdf}', 'filename.doc')
#=> false
File.fnmatch('file{*.doc,*.pdf}', 'filename.doc', File::FNM_EXTGLOB)
#=> true
File.fnmatch('f*l?{[a-z].doc,[0-9].pdf}', 'filex.doc', File::FNM_EXTGLOB)
#=> true
File.fnmatch('**/.{pro,}f?l*', 'home/.profile', File::FNM_EXTGLOB | File::FNM_DOTMATCH)
#=> true

将修补fnmatch_with_braces_glob(和?变体)以代替fnmatch,以便兼容Ruby 2.0.0的代码也可与早期Ruby版本一起使用.为了清楚起见,上面显示的代码不包括某些性能改进,参数检查或Backports功能检测和修补代码.这些显然将包括在实际提交给项目中.

The fnmatch_with_braces_glob (and ? variant) will be patched in place of fnmatch, so that Ruby 2.0.0-compliant code will work with earlier Ruby versions, as well. For clarity reasons, the code shown above does not include some performance improvements, argument checking, or the Backports feature detection and patch-in code; these will obviously be included in the actual submission to the project.

我仍在测试一些极端情况,并在很大程度上优化性能.它应该准备好很快提交.在正式的Backports版本中可用后,我将在此处更新状态.

I'm still testing some edge cases and heavily optimizing performance; it should be ready to submit very soon. Once it's available in an official Backports release, I'll update the status here.

请注意, Dir :: glob 支持也会同时出现.

Note that Dir::glob support will be coming at the same time, as well.

这篇关于在Ruby 1.9.3上使用花括号进行通配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆