如何检查文件夹中的多个单词

时间:2017-05-03 21:15:15

标签: ruby search directory subdirectory

我在一个名为words.txt的文本文件中有一个单词,我需要检查这些单词是否在我的Source文件夹中,其中还包含子文件夹和文件。

我能够使用以下代码将所有单词放入数组中:

array_of_words = [] 

File.readlines('words.txt').map do |word|
  array_of_words << word
end

我还有(有点)想出如何使用以下方法搜索整个Source文件夹,包括特定单词的子文件夹和子文件:

Dir['Source/**/*'].select{|f| File.file?(f) }.each do |filepath|
  puts filepath
  puts File.readlines(filepath).any?{ |l| l['api'] } 
end

我没有搜索像api这样的单词,而是想在Source文件夹中搜索整个单词数组(如果可能的话)。

2 个答案:

答案 0 :(得分:2)

考虑一下:

File.readlines('words.txt').map do |word|
  array_of_words << word
end

将整个文件读入内存,然后将其转换为数组中的单个元素。你可以使用以下方法完成同样的事情:

array_of_words = File.readlines('words.txt')

潜在的问题是它不可扩展。如果“words.txt”大于可用内存,则代码会出现问题,所以要小心。

在文件中搜索单词数组可以通过多种方式完成,但我总是发现使用正则表达式最容易。 Perl有一个名为Regexp :: Assemble的强大模块,可以很容易地将单词列表转换为非常有效的模式,但Ruby缺少这种功能。有关我过去整理过的一个解决方案,请参阅“Is there an efficient way to perform hundreds of text substitutions in Ruby?”。

Ruby确实有Regexp.union但是它只是部分帮助。

words = %w(foo bar)
re = Regexp.union(words) # => /foo|bar/

生成的模式具有表达式的标志,因此您必须小心将其插入到另一种模式中:

/#{re}/ # => /(?-mix:foo|bar)/

(?-mix:会给你带来麻烦,所以不要这样做。而是使用:

/#{re.source}/ # => /foo|bar/

将生成模式并且表现得像我们期望的那样。

不幸的是,这也不是一个完整的解决方案,因为换句话说,这些单词可以作为子字符串找到:

'foolish'[/#{re.source}/] # => "foo"

解决这个问题的方法是在模式周围设置字边界:

/\b(?:#{re.source})\b/ # => /\b(?:foo|bar)\b/

然后查找整个单词:

'foolish'[/\b(?:#{re.source})\b/] # => nil

Ruby的Regexp文档中提供了更多信息。

一旦你想要使用一个模式,那么搜索变得更简单。 Ruby有Find类,这使得递归搜索文件的目录变得容易。该文档介绍了如何使用它。

或者,您可以使用Dir类来拼凑自己的方法。同样,它在文档中有使用它的示例,但我通常使用Find。

在阅读您正在扫描的文件时,我建议您使用foreach逐行阅读文件。 File.readFile.readlines 可扩展,并且当Ruby尝试将大文件读入内存时,可能会使您的程序行为异常。相反,foreach将导致运行速度更快的可伸缩代码。有关详细信息,请参阅“Why is "slurping" a file not a good practice?”。

使用上面的链接,您应该能够快速地将某些内容放在一起,以便有效地运行并保持灵活性。

这个未经测试的代码可以帮助您入门:

WORD_ARRAY = File.readlines('words.txt').map(&:chomp)
WORD_RE = /\b(?:#{Regexp.union(WORD_ARRAY).source}\b)/

Dir['Source/**/*'].select{|f| File.file?(f) }.each do |filepath|
  puts "#{filepath}: #{!!File.read(filepath)[WORD_RE]}"
end

它将输出正在读取的文件,并且“true”或“false”是否有命中找到列表中的一个单词。

由于readlinesread,它无法扩展,如果任何文件很大,可能会严重减速。再次,请参阅上面“slurp”链接中的警告。

答案 1 :(得分:0)

递归搜索目录,查找words.txt

中包含的任何字词
re = /#{File.readlines('words.txt').map { |word| Regexp.quote(word.strip) }.join('|')}/

Dir['Source/**/*.{cpp,txt,html}'].select{|f| File.file?(f) }.each do |filepath|
  puts filepath
  puts File.readlines(filepath, "r:ascii").grep(re).any?
end