C'est un début très solide. J'ai quelques suggestions, cependant.
Premièrement, votre utilisation de readlines
est un peu inefficace. readlines
crée une nouvelle liste de lignes à partir du fichier -- elle stocke le fichier entier en mémoire. Mais vous n'êtes pas obligé de faire cela ; si tout ce que vous voulez faire est d'itérer sur les lignes d'un fichier, vous pouvez simplement dire for line in file
ou dans votre cas :
for i, line in enumerate(book):
Sinon, si vous souhaitez vraiment conserver le fichier en mémoire, peut-être pour des recherches répétées, enregistrez le résultat de la commande readlines
à une variable :
booklines = book.readlines()
for i, line in enumerate(booklines):
Vous pouvez également stocker le texte comme une chaîne unique avec read
mais cela n'est pas très utile dans ce cas, car il faudrait encore le diviser :
booktxt = book.read()
booklines = book.splitlines() #
for i, line in enumerate(booklines)
Deuxièmement, je dirais que plutôt que d'utiliser i
comme variable d'index, puis l'enregistrer séparément dans le fichier ln
Si vous avez besoin d'un nom de variable, utilisez simplement un nom de variable significatif dès le départ. ln
est bien, line_number
est plus clair mais verbeux, lineno
est un bon compromis. Tenons-nous en à ln
ici puisque nous savons tous ce que cela signifie.
for ln, line in enumerate(book):
Troisièmement, comme utdemir l'a souligné dans les commentaires, vous n'avez pas vraiment besoin de regex pour cela. Il est possible que cela ait un sens si vous voulez que votre utilisateur puisse entrer des recherches plus sophistiquées, mais les RE sont suffisamment compliqués pour faire une ui par défaut discutable. J'utiliserais simplement in
pour une simple correspondance de sous-chaîne, comme dans :
if word_search in line:
Les autres instructions if sont correctes, et dans certains cas, c'est la meilleure chose à faire. Cependant, souvent, dans des situations qui nécessiteraient (disons) case
les déclarations, il est en fait préférable d'utiliser un dictionnaire. Bien sûr, ici vous avez des plages, donc nous devons être un peu plus intelligents.
Commençons par un dictionnaire des pages de démarrage. Comme cela est probablement évident, il doit précéder la boucle afin que nous ne redéfinissions pas le dictionnaire à chaque fois.
first_lines = {36: 'Genesis', 4812: 'Exodus', 8867: 'Leviticus', 11749: 'Numbers'}
Maintenant, nous devons établir une carte ln
à l'une de ces valeurs de dictionnaire. Mais il y a de fortes chances que ln
n'est pas égal à l'un des nombres ci-dessus, et nous ne pouvons donc pas l'introduire directement dans le dictionnaire. Nous avons pourrait utiliser un for
boucle pour itérer sur les clés du dictionnaire ( for key in first_lines
), stocker la clé précédente dans prev_key
tester si ln > key
et, si c'est le cas, renvoyer prev_key
. Mais il existe en fait une façon beaucoup plus agréable de le faire en Python. Au lieu d'écrire une boucle normale, nous filtre la liste, en utilisant soit la fonction intégrée filter
ou une compréhension de liste pour supprimer de la liste les valeurs qui sont plus grandes que ln
. Ensuite, nous trouvons le max
.
first_line = max(filter(lambda l: l < ln, first_lines))
Ici first_lines
agit comme une liste non ordonnée de ses clés ; en général, vous pouvez itérer sur les clés d'un dictionnaire comme vous le feriez pour une liste, avec la réserve que les clés peuvent être dans n'importe quel ordre. lambda
est un moyen de définir une fonction courte : cette fonction prend x
comme argument et renvoie le résultat de x < ln
. Nous devons le faire de cette façon parce que filter
veut une fonction comme premier argument. Elle retourne une liste contenant toutes les valeurs de first_lines
qui donnent un True
résultat.
Comme cela peut être un peu difficile à lire, surtout lorsque lambda
est impliquée, il est probablement préférable d'utiliser une liste de compréhension ici. Les compréhensions de listes sont très lisibles et intuitives pour la plupart des gens.
first_line = max([l for l in first_lines if l < ln])
Nous pouvons même laisser de côté les parenthèses dans ce cas, puisque nous le passons directement à une fonction. Python interprète cela comme une "expression génératrice", qui ressemble à une compréhension de liste mais qui calcule les valeurs à la volée, au lieu de les stocker dans une liste au préalable.
first_line = max(l for l in first_lines if l < ln)
Maintenant, pour obtenir le nom du livre, tout ce que vous avez à faire est d'utiliser first_line
comme une clé :
bibook = first_lines[first_line]
Le résultat final :
import os
import sys
import re
word_search = raw_input(r'Enter a word to search: ')
book = open("KJV.txt", "r")
first_lines = {36: 'Genesis', 4812: 'Exodus', 8867: 'Leviticus', 11749: 'Numbers'}
for ln, line in enumerate(book):
if word_search in line:
first_line = max(l for l in first_lines if l < ln)
bibook = first_lines[first_line]
template = "\nLine: {0}\nString: {1}\nBook: {2}\n"
output = template.format(ln, word_search, bibook)
print output