2016-01-05 8 views
8

रूबी के हाल के संस्करणों, ग्लोबिंग में ब्रेसिज़ के उपयोग का समर्थन करता है, तो आप 2.2.0 documentationरूबी पर ब्रेसिज़ का उपयोग कर ग्लोबिंग 1.9.3

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

हालांकि से फ़ाइल :: FNM_EXTGLOB विकल्प

उपयोग करते हैं, 1.9 .3 प्रलेखन कहते हैं कि यह 1.9.3 में समर्थित नहीं है:

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

(भी File::FNM_EXTGLOB उपयोग करने के लिए कोशिश कर रहा एक नाम त्रुटि दिया)

रूबी 1.9.3 में ब्रेसिज़ का उपयोग करके ग्लोब करने का कोई तरीका है, जैसे कि थर्ड-पार्टी मणि?

जिन तारों के साथ मैं मिलान करना चाहता हूं वे एस 3 से हैं, स्थानीय फाइल सिस्टम नहीं, इसलिए मैं ऑपरेटिंग सिस्टम को ग्लोबिंग करने के लिए कह सकता हूं, जहां तक ​​मुझे पता है।

+1

चूंकि 'फाइल' ग्लोबिंग करने में सक्षम है, इसलिए ओएस निश्चित रूप से सक्षम है। मैं शर्त लगाता हूं कि एस 3 घुड़सवार या पसंद है, इसलिए कृपया '% x | ls c {at, ub} s | 'पर प्रयास करें, इसे काम करना चाहिए। – mudasobwa

उत्तर

0

यह एक मजेदार रूबी व्यायाम था! कोई विचार करता है, तो इस समाधान आप के लिए पर्याप्त मजबूत है, लेकिन यहाँ जाता है:

class File 
    class << self 
    def fnmatch_extglob(pattern, path, flags=0) 
     explode_extglob(pattern).any?{|exploded_pattern| 
     fnmatch(exploded_pattern,path,flags) 
     } 
    end 

    def explode_extglob(pattern) 
     if match=pattern.match(/\{([^{}]+)}/) then 
     subpatterns = match[1].split(',',-1) 
     subpatterns.map{|subpattern| explode_extglob(match.pre_match+subpattern+match.post_match)}.flatten 
     else 
     [pattern] 
     end 
    end 
    end 
end 

बेहतर परीक्षण की जरूरत है, लेकिन यह साधारण मामलों के लिए ठीक काम करने के लिए लगता है:

[2] pry(main)> File.explode_extglob('c{at,ub}s') 
=> ["cats", "cubs"] 
[3] pry(main)> File.explode_extglob('c{at,ub}{s,}') 
=> ["cats", "cat", "cubs", "cub"] 
[4] pry(main)> File.explode_extglob('{a,b,c}{d,e,f}{g,h,i}') 
=> ["adg", "adh", "adi", "aeg", "aeh", "aei", "afg", "afh", "afi", "bdg", "bdh", "bdi", "beg", "beh", "bei", "bfg", "bfh", "bfi", "cdg", "cdh", "cdi", "ceg", "ceh", "cei", "cfg", "cfh", "cfi"] 
[5] pry(main)> File.explode_extglob('{a,b}c*') 
=> ["ac*", "bc*"] 
[6] pry(main)> File.fnmatch('c{at,ub}s', 'cats') 
=> false 
[7] pry(main)> File.fnmatch_extglob('c{at,ub}s', 'cats') 
=> true 
[8] pry(main)> File.fnmatch_extglob('c{at,ub}s*', 'catsssss') 
=> true 

रूबी 1.9 के साथ परीक्षण किया गया। 3 और रूबी 2.1.5 और 2.2.1।

+1

यह छोटी परियोजनाओं के लिए वास्तव में एक अच्छा समाधान है। मैंने आपके समाधान के समान ही उपयोग किया था (लेकिन उतना ही अच्छा नहीं), लेकिन बड़े/गहरे पदानुक्रमों पर प्रदर्शन कारणों के लिए पूरी तरह से विश्लेषण किए गए समाधान के साथ जाने का विकल्प चुना; यह एक * विशाल * अंतर (कई मामलों में परिमाण के आदेश से अधिक) बनाया। ध्यान दें कि यदि आप अपने 'fnmatch_extglob' पर तीसरा तर्क ('flags = 0') जोड़ते हैं और' fln' 'को' fnmatch' के तीसरे पैरामीटर में पास करते हैं, तो आपको मुफ्त में झंडे के व्यवहार का पूरा सेट मिल जाएगा, और आप 'आप किसी भी कार्यात्मक परीक्षण को पारित कर सकते हैं जिसे आप फेंक सकते हैं। –

+0

टिप्पणी के लिए धन्यवाद। मैंने वैकल्पिक झंडे जोड़े। –

1

मैं ब्रेसिज़ ग्लोबिंग समर्थन के लिए 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, 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 निर्माण करने के लिए ग्लोब पैटर्न का उपयोग करता। इस समाधान के साथ, इन परीक्षण सफलतापूर्वक चलाया जा सकता है:

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 के स्थान पर समझौता किया जाएगा, ताकि रूबी 2.0.0 अनुरूप कोड पहले रूबी संस्करणों के साथ काम करेंगे कुंआ। स्पष्टता कारणों के लिए, ऊपर दिखाए गए कोड में कुछ प्रदर्शन सुधार, तर्क जांच, या बैकपोर्ट सुविधा का पता लगाने और पैच-इन कोड शामिल नहीं है; इन्हें परियोजना में वास्तविक सबमिशन में स्पष्ट रूप से शामिल किया जाएगा।

मैं अभी भी कुछ किनारे के मामलों का परीक्षण कर रहा हूं और प्रदर्शन को अनुकूलित कर रहा हूं; यह बहुत जल्द जमा करने के लिए तैयार होना चाहिए। एक बार यह आधिकारिक बैकपोर्ट्स रिलीज में उपलब्ध हो जाने पर, मैं यहां स्थिति अपडेट कर दूंगा।

ध्यान दें कि Dir::glob समर्थन भी एक ही समय में आ जाएगा।

संबंधित मुद्दे