2017-09-28 4 views
0

J'ai un gros fichier qui peut avoir des chaînes comme file_+0.txt, file_[]1.txt, file_~8.txt etc.Python - Comment rechercher une chaîne dans un grand fichier

Je veux trouver le files_*.txt manquant jusqu'à un certain nombre.

Par exemple, si je donne le fichier ci-dessous et un numéro 5, il faut dire que les disparus sont 1 and 4

asdffile_[0.txtsadfe 
asqwffile_~2.txtsafwe 
awedffile_[]2.txtsdfwe 
qwefile_*0.txtsade 
zsffile_+3.txtsadwe 

J'ai écrit un script Python auquel je peux donner le chemin du fichier et un numéro et il me donnera tous les noms de fichiers qui manquent jusqu'à ce nombre.

Mon programme fonctionne pour les petits fichiers. Mais quand je donne un gros fichier (12MB) qui peut avoir des numéros de fichiers jusqu'à 10000, il se bloque.

Voici mon code actuel Python

#! /usr/bin/env/python 
import mmap 
import re 

def main(): 
    filePath = input("Enter file path: ") 
    endFileNum = input("Enter end file number: ") 
    print(filePath) 
    print(endFileNum) 
    filesMissing = [] 
    filesPresent = [] 
    f = open(filePath, 'rb', 0) 
    s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 
    for x in range(int(endFileNum)): 
     myRegex = r'(.*)file(.*)' + re.escape(str(x)) + r'\.txt' 
     myRegex = bytes(myRegex, 'utf-8') 
     if re.search(myRegex, s): 
      filesPresent.append(x) 
     else: 
      filesMissing.append(x) 
    #print(filesPresent) 
    print(filesMissing) 

if __name__ == "__main__": 
    main() 

sortie se bloque lorsque je donne un fichier de 12 Mo qui peut avoir des fichiers de 0 à 9999

$python findFileNumbers.py 
Enter file path: abc.log 
Enter end file number: 10000 

sortie pour un petit fichier (comme ci-dessus exemple)

$python findFileNumbers.py 
Enter file path: sample.log 
Enter end file number: 5 
[0, 2, 3] 
[1, 4] 
  1. Comment puis-je faire t son travail pour les gros fichiers?
  2. Y a-t-il une meilleure façon d'obtenir ces résultats au lieu d'un script Python?

Merci d'avance!

+0

Grand en termes de quoi? Le nombre de fichiers à rechercher, la taille des données dans le fichier, la longueur de son nom? – Mark

+0

J'ai donné un fichier de 12 Mo en entrée et le nombre de fichiers qu'il peut rechercher est de 10 000 – SyncMaster

+0

Il n'est pas nécessaire de mapper les fichiers en mémoire si vous avez juste besoin de leur nom. – Mark

Répondre

2

d'abord recueillir les existants dans un ensemble, puis chercher les manquants. La raison pour laquelle vous vous bloque parce que vous parcourez le fichier entier pour chaque numéro. i.e 12 Mo * 10000 = 120 Go Le script passe à 120 Go et il se bloque même si vous l'avez en mmap.

+2

Vous devez utiliser '. *?' Dans votre regex si l'implication de l'OP que plusieurs nombres peuvent se produire sur une ligne est correcte. –

1

Je vous suggère de simplement lire le fichier d'entrée ligne par ligne et d'analyser chacune des lignes pour son numéro de fichier. Ensuite, utilisez ce numéro de fichier comme index dans un ensemble de tableaux booléens False initialement.

Vous ne faites aucun traitement nécessitant que le fichier soit en mémoire. Cette approche fonctionnera pour les fichiers très volumineux.

#~ import mmap 
import re 
import numpy as np 

def main(): 
    #~ filePath = input("Enter file path: ") 
    filePath = 'filenames.txt' 
    #~ endFileNum = input("Enter end file number: ") 
    endFileNum = 5 
    print(filePath) 
    print(endFileNum) 
    found = np.zeros(1+endFileNum, dtype=bool) 
    patt = re.compile(r'[^\d]+(\d+)') 
    with open(filePath) as f: 
     for line in f.readlines(): 
      r = patt.search(line).groups(0)[0] 
      if r: 
       found[int(r)]=True 
    print (found) 

    #~ filesMissing = [] 
    #~ filesPresent = [] 
    #~ files = np.zeros[endFileNum, dtype=bool] 
    #~ f = open(filePath, 'rb', 0) 
    #~ s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 
    #~ for x in range(int(endFileNum)): 
     #~ myRegex = r'(.*)file(.*)' + re.escape(str(x)) + r'\.txt' 
     #~ myRegex = bytes(myRegex, 'utf-8') 
     #~ if re.search(myRegex, s): 
      #~ filesPresent.append(x) 
     #~ else: 
      #~ filesMissing.append(x) 
    #print(filesPresent) 
    #~ print(filesMissing) 

if __name__ == "__main__": 
    main() 

Cela produit le résultat suivant à partir de laquelle votre filesPresent et filesMissing sont facilement récupérés.

filenames.txt 
5 
[ True False True True False False] 
1

Jetons un coup d'oeil à ce que vous faites réellement ici:

  1. carte mémoire le fichier.
  2. Pour chaque numéro

    a. Compilez une expression régulière pour ce nombre.
    b.Recherchez l'expression régulière dans le fichier entier.

Ceci est très inefficace pour les grands nombres. Alors que le mappage de la mémoire vous donne une interface semblable à une chaîne dans le fichier, ce n'est pas magique. Vous avez encore des morceaux de chargement du fichier à déplacer dedans. En même temps, vous effectuez une passe, potentiellement sur le fichier entier, pour chaque regex. Et la correspondance regex est chère aussi.

La solution ici serait de faire un seul passage dans le fichier, ligne par ligne. Vous devriez pré-compiler l'expression régulière au lieu de la compiler une fois par nombre si vous avez un grand nombre à rechercher. Pour obtenir tous les numéros en un seul passage, vous pouvez faire un de tous les numéros jusqu'à celui que vous voulez, appelé "manquant", et un set vide appelé "trouvé". Chaque fois que vous rencontrez une ligne avec un nombre, vous déplacer le nombre de «manquant» à «trouvé».

Voici une exemple d'implémentation:

filePath = input("Enter file path: ") 
endFileNum = int(input("Enter end file number: ")) 
missing = set(range(endFileNum)) 
found = set() 
regex = re.compile(r'file_.*?(\d+)\.txt') 
with open(filePath) as file: 
    for line in file: 
     for match in regex.finditer(line) 
      num = int(match.groups(1)) 
      if num < endFileNum: 
       found.add(num) 
missing -= found 

Notez que l'expression régulière utilise le reluctant quantifier.*? après file_. Cela correspondra le moins de caractères possible avant de chercher un chiffre. Si vous avez le quantificateur gourmand par défaut de .*, plusieurs nombres sur une ligne correspondent uniquement au dernier.