1

给定一个包含大约 100 000 个小文件的目录(每个文件大约 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。

感谢您的任何提示。

4

2 回答 2

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"], ...}
于 2013-08-15T08:29:24.300 回答
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 运行也可以 示例输出

于 2013-08-16T15:30:49.073 回答