提高Ruby中文件搜索的速度

时间:2013-08-15 08:15:52

标签: ruby performance

给定一个包含大约10万个小文件的目录(每个文件大约1kB)。 我需要获取这些文件的列表并对其进行迭代,以便查找具有相同名称但不同大小写的文件(这些文件位于Linux ext4 FS上)。 目前,我使用了这样的代码:

   def similar_files_in_folder(file_path, folder, exclude_folders = false)
     files = Dir.glob(file_path, File::FNM_CASEFOLD)
     files_set = files.select{|f| f.start_with?(folder)}
     return files_set unless exclude_folders
     files_set.reject{|entry| File.directory? entry}
   end

   dir_entries = Dir.entries(@directory) - ['.', '..']
   dir_entries.map do |file_name|
     similar_files_in_folder(file_name, @directory)
   end

这种方法的问题是该片段需要很多!!!时间到了。 我的系统大约需要几个小时。

还有另一种方法可以实现相同的目标,但在Ruby中要快得多吗?

限制:我无法在内存中加载文件列表,然后只是在小写的情况下比较名称,因为在@directory中会出现新文件。 所以,我需要在每次迭代时扫描@directory。

感谢任何提示。

2 个答案:

答案 0 :(得分:2)

如果我正确理解你的代码,这已经返回了所有这些100k文件名的数组:

dir_entries = Dir.entries(@directory) - ['.', '..']
#=> ["foo.txt", "bar.txt", "BAR.txt", ...]

我会用小写文件名对这个数组进行分组:

dir_entries.group_by(&:downcase)
#=> {"foo.txt"=>["foo.txt"], "bar.txt"=>["bar.txt", "BAR.txt"], ... }

选择出现次数超过1次的那些:

dir_entries.group_by(&:downcase).select { |k, v| v.size > 1 }
#=> {"bar.txt"=>["bar.txt", "BAR.txt"], ...}

答案 1 :(得分:1)

我的评论意味着您可以在遍历文件系统时搜索字符串,而不是首先构建大量所有可能的文件,然后再搜索。我写了一些类似于linux find <path> | grep --color -i <pattern>的东西,除了仅在basename中突出显示模式:

require 'find'

#find files whose basename matches a pattern (and output results to console)
def find_similar(s, opts={})
  #by default, path is '.', case insensitive, no bash terminal coloring
  opts[:verbose] ||= false
  opts[:path] ||= '.'
  opts[:insensitive]=true if opts[:insensitive].nil?
  opts[:color]||=false
  boldred = "\e[1m\e[31m\\1\e[0m"    #contains an escaped \1 for regex

  puts "searching for \"#{s}\" in \"#{opts[:path]}\", insensitive=#{opts[:insensitive]}..." if opts[:verbose]
  reg = opts[:insensitive] ? /(#{s})/i : /(#{s})/
  dir,base = '',''
  Find.find(opts[:path]) {|path|
    dir,base = File.dirname(path), File.basename(path)
    if base =~ reg
      if opts[:color]
        puts "#{dir}/#{base.gsub(reg, boldred)}"
      else
        puts path
      end
    end
  }
end

time = Time.now
#find_similar('LOg', :color=>true)    #similar to   find . | grep --color -i LOg
find_similar('pYt', :path=>'c:/bin/sublime3/', :color=>true, :verbose=>true)
puts "search took #{Time.now-time}sec"

示例输出(cygwin),但如果从cmd.exe运行也可以 example output