如果您使用File :: FNM_EXTGLOB选项,则最新版本的
Ruby支持在globbing中使用大括号
File.fnmatch('c{at,ub}s','cats',File::FNM_EXTGLOB) #=> true # { } is supported on FNM_EXTGLOB
但是,1.9.3文档说它在1.9.3中不受支持:
File.fnmatch('c{at,'cats') #=> false # { } isn't supported
(另外,尝试使用File :: FNM_EXTGLOB给出了一个名称错误)
有没有办法在Ruby 1.9.3中使用大括号,比如第三方gem?
解决方法
我正在打包
Ruby Backport用于支撑圆柱形支撑.以下是该解决方案的基本部分:
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,flags =0) return fnmatch_with_braces_glob(pattern,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函数可用的各种标志,并使用glob模式构建合适的Regexp以匹配这些功能.使用此解决方案,可以成功运行这些测试:
File.fnmatch('c{at,File::FNM_EXTGLOB) #=> true File.fnmatch('file{*.doc,*.pdf}','filename.doc') #=> false File.fnmatch('file{*.doc,'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(和?variant)将被修补以代替fnmatch,因此符合Ruby 2.0.0的代码也适用于早期的Ruby版本.为清楚起见,上面显示的代码不包括一些性能改进,参数检查或Backports功能检测和补丁代码;这些显然将包含在项目的实际提交中.
我还在测试一些边缘情况并大大优化性能;它应该准备很快提交.一旦它在官方Backports版本中可用,我将在此处更新状态.