2017-05-03 3 views
2

J'ai un mot dans un fichier texte appelé words.txt, et j'ai besoin de vérifier si certains de ces mots sont dans mon dossier Source, qui contient également des sous-dossiers et des fichiers.Comment vérifier plusieurs mots dans un dossier

j'ai pu obtenir tous les mots dans un tableau en utilisant ce code:

array_of_words = [] 

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

Et j'ai aussi (un peu) compris comment effectuer une recherche dans l'ensemble dossier source, y compris les sous-dossiers et sous-dossiers pour un mot spécifique en utilisant:

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

au lieu de chercher un mot comme api, je veux rechercher le dossier source pour tout le tableau de mots (si cela est possible).

+1

Est-ce que tu dois le faire dans ruby? L'outil en ligne de commande 'egrep' pourrait le faire beaucoup plus facilement via quelque chose comme' egrep -r '(api | function | method) "*' ... – Brian

+0

Hey @Brian, oui, malheureusement, il doit être en ruby. –

Répondre

0

recherche récursivement répertoire pour l'un des mots contenus dans 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 
+0

J'ai mis à jour la réponse pour échapper le contenu de words.txt –

+0

Hey donc j'ai eu la même erreur exacte. '=== ': séquence d'octets invalide dans UTF-8 (ArgumentError) –

+0

' Regexp.quote (word.strip)} .join (' | ') 'n'est pas une bonne idée car elle peut générer des fausses coups positifs de sous-chaîne. –

2

Considérez ceci:

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

va lire le fichier en mémoire, puis le convertir en éléments individuels dans un tableau. Vous pouvez accomplir la même chose en utilisant:

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

Un problème potentiel est son non scalable. Si "words.txt" est plus grand que la mémoire disponible, votre code aura des problèmes alors soyez prudent.

La recherche d'un tableau de mots dans un fichier peut se faire de plusieurs façons, mais j'ai toujours trouvé qu'il était plus facile d'utiliser une expression régulière. Perl a un excellent module appelé Regexp :: Assemble qui facilite la conversion d'une liste de mots en un modèle très efficace, mais Ruby manque ce genre de fonctionnalité. Voir "Is there an efficient way to perform hundreds of text substitutions in Ruby?" pour une solution que j'ai rassemblée dans le passé pour vous aider.

Ruby a Regexp.union mais ce n'est qu'une aide partielle.

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

Le modèle généré a des drapeaux pour l'expression, donc vous devez être prudent avec interpoler dans un autre motif:

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

(?-mix: vous causer des problèmes afin de ne pas le faire. Au lieu de cela, utilisez:

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

qui générera le motif et se comportera comme prévu.

Malheureusement, ce n'est pas une solution complète soit, parce que les mots peuvent être trouvés comme sous-chaînes en d'autres termes:

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

La façon de travailler autour de c'est de mettre en mot-frontières autour du modèle:

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

qui ont l'air puis des mots entiers pour:

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

Plus d'informations sont disponibles dans la documentation Regexp de Ruby.

Une fois que vous avez un motif que vous souhaitez utiliser, la recherche devient plus simple. Ruby a la classe Find, ce qui facilite la recherche récursive de répertoires dans les répertoires. La documentation couvre comment l'utiliser.

Alternativement, vous pouvez bricoler votre propre méthode en utilisant la classe Dir. Encore une fois, il a des exemples dans la documentation pour l'utiliser, mais je vais généralement avec Find.

Lors de la lecture des fichiers que vous numérisez, je recommande d'utiliser foreach pour lire les fichiers ligne par ligne. File.read et File.readlines sont pas évolutive et peut rendre votre programme se comporter de façon erratique comme Ruby essaie de lire un gros fichier en mémoire. Au lieu de cela, foreach se traduira par un code très évolutif qui s'exécute plus rapidement. Voir "Why is "slurping" a file not a good practice?" pour plus d'informations. En utilisant les liens ci-dessus, vous devriez être capable de mettre rapidement quelque chose ensemble qui fonctionnera efficacement et sera flexible.


Ce code non testé devrait vous aider à démarrer:

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 

Il affichera le fichier, il est la lecture, et « vrai » ou « faux » s'il y a un coup de trouver un des mots dans la liste.

Ce n'est pas évolutif en raison de readlines et read et pourrait souffrir d'un grave ralentissement si l'un des fichiers est énorme. Encore une fois, voir les mises en garde dans le lien "slurp" ci-dessus.

+0

Bonjour, merci pour cette information étonnante/utile je viendrai avec une meilleure solution, mais ceci def. aide! –