在Ruby 1.9.3上使用花括号进行通配 [英] Globbing using braces on 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屋!