使用Ruby 1.9.3上的大括号进行全局处理

前端之家收集整理的这篇文章主要介绍了使用Ruby 1.9.3上的大括号进行全局处理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
如果您使用File :: FNM_EXTGLOB选项,则最新版本的 Ruby支持在globbing中使用大括号

2.2.0 documentation

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

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

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

(另外,尝试使用File :: FNM_EXTGLOB给出了一个名称错误)

有没有办法在Ruby 1.9.3中使用大括号,比如第三方gem?

我想要匹配的字符串来自S3,而不是本地文件系统,所以我不能只是要求操作系统根据我的知识进行通配.

解决方法

我正在打包 Ruby Backport用于支撑圆柱形支撑.以下是该解决方案的基本部分:
  1. module File::Constants
  2. FNM_EXTGLOB = 0x10
  3. end
  4.  
  5. class << File
  6. def fnmatch_with_braces_glob(pattern,path,flags =0)
  7. regex = glob_convert(pattern,flags)
  8.  
  9. return regex && path.match(regex).to_s == path
  10. end
  11.  
  12. def fnmatch_with_braces_glob?(pattern,flags =0)
  13. return fnmatch_with_braces_glob(pattern,flags)
  14. end
  15.  
  16. private
  17. def glob_convert(pattern,flags)
  18. brace_exp = (flags & File::FNM_EXTGLOB) != 0
  19. pathnames = (flags & File::FNM_PATHNAME) != 0
  20. dot_match = (flags & File::FNM_DOTMATCH) != 0
  21. no_escape = (flags & File::FNM_NOESCAPE) != 0
  22. casefold = (flags & File::FNM_CASEFOLD) != 0
  23. syscase = (flags & File::FNM_SYSCASE) != 0
  24. special_chars = ".*?\\[\\]{},.+()|$^\\\\" + (pathnames ? "/" : "")
  25. special_chars_regex = Regexp.new("[#{special_chars}]")
  26.  
  27. if pattern.length == 0 || !pattern.index(special_chars_regex)
  28. return Regexp.new(pattern,casefold || syscase ? Regexp::IGNORECASE : 0)
  29. end
  30.  
  31. # Convert glob to regexp and escape regexp characters
  32. length = pattern.length
  33. start = 0
  34. brace_depth = 0
  35. new_pattern = ""
  36. char = "/"
  37.  
  38. loop do
  39. path_start = !dot_match && char[-1] == "/"
  40.  
  41. index = pattern.index(special_chars_regex,start)
  42.  
  43. if index
  44. new_pattern += pattern[start...index] if index > start
  45. char = pattern[index]
  46.  
  47. snippet = case char
  48. when "?" then path_start ? (pathnames ? "[^./]" : "[^.]") : ( pathnames ? "[^/]" : ".")
  49. when "." then "\\."
  50. when "{" then (brace_exp && (brace_depth += 1) >= 1) ? "(?:" : "{"
  51. when "}" then (brace_exp && (brace_depth -= 1) >= 0) ? ")" : "}"
  52. when "," then (brace_exp && brace_depth >= 0) ? "|" : ","
  53. when "/" then "/"
  54. when "\\"
  55. if !no_escape && index < length
  56. next_char = pattern[index += 1]
  57. special_chars.include?(next_char) ? "\\#{next_char}" : next_char
  58. else
  59. "\\\\"
  60. end
  61. when "*"
  62. if index+1 < length && pattern[index+1] == "*"
  63. char += "*"
  64. if pathnames && index+2 < length && pattern[index+2] == "/"
  65. char += "/"
  66. index += 2
  67. "(?:(?:#{path_start ? '[^.]' : ''}[^\/]*?\\#{File::SEPARATOR})(?:#{!dot_match ? '[^.]' : ''}[^\/]*?\\#{File::SEPARATOR})*?)?"
  68. else
  69. index += 1
  70. "(?:#{path_start ? '[^.]' : ''}(?:[^\\#{File::SEPARATOR}]*?\\#{File::SEPARATOR}?)*?)?"
  71. end
  72. else
  73. path_start ? (pathnames ? "(?:[^./][^/]*?)?" : "(?:[^.].*?)?") : (pathnames ? "[^/]*?" : ".*?")
  74. end
  75. when "["
  76. # Handle character set inclusion / exclusion
  77. start_index = index
  78. end_index = pattern.index(']',start_index+1)
  79. while end_index && pattern[end_index-1] == "\\"
  80. end_index = pattern.index(']',end_index+1)
  81. end
  82. if end_index
  83. index = end_index
  84. char_set = pattern[start_index..end_index]
  85. char_set.delete!('/') if pathnames
  86. char_set[1] = '^' if char_set[1] == '!'
  87. (char_set == "[]" || char_set == "[^]") ? "" : char_set
  88. else
  89. "\\["
  90. end
  91. else
  92. "\\#{char}"
  93. end
  94.  
  95. new_pattern += snippet
  96. else
  97. if start < length
  98. snippet = pattern[start..-1]
  99. new_pattern += snippet
  100. end
  101. end
  102.  
  103. break if !index
  104. start = index + 1
  105. end
  106.  
  107. begin
  108. return Regexp.new("\\A#{new_pattern}\\z",casefold || syscase ? Regexp::IGNORECASE : 0)
  109. rescue
  110. return nil
  111. end
  112. end
  113. end

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

  1. File.fnmatch('c{at,File::FNM_EXTGLOB)
  2. #=> true
  3. File.fnmatch('file{*.doc,*.pdf}','filename.doc')
  4. #=> false
  5. File.fnmatch('file{*.doc,'filename.doc',File::FNM_EXTGLOB)
  6. #=> true
  7. File.fnmatch('f*l?{[a-z].doc,[0-9].pdf}','filex.doc',File::FNM_EXTGLOB)
  8. #=> true
  9. File.fnmatch('**/.{pro,}f?l*','home/.profile',File::FNM_EXTGLOB | File::FNM_DOTMATCH)
  10. #=> true

fnmatch_with_braces_glob(和?variant)将被修补以代替fnmatch,因此符合Ruby 2.0.0的代码也适用于早期的Ruby版本.为清楚起见,上面显示代码包括一些性能改进,参数检查或Backports功能检测和补丁代码;这些显然将包含在项目的实际提交中.

我还在测试一些边缘情况并大大优化性能;它应该准备很快提交.一旦它在官方Backports版本中可用,我将在此处更新状态.

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

猜你在找的Ruby相关文章