2017-09-28 1 views
0

Ich habe eine große Datei zu suchen, die Strings wie file_+0.txt, file_[]1.txt, file_~8.txt usw.Python - Wie eine Zeichenfolge in einer großen Datei

Ich will haben, die fehlenden files_*.txt, bis eine bestimmte Zahl zu finden.

Zum Beispiel, wenn ich die folgende Datei geben und eine Nummer 5, sollte es sagen, dass die Vermissten sind 1 and 4

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

ich einen Python-Skript geschrieben, auf die ich kann den Dateipfad und eine Reihe geben und es wird mir alle Dateinamen geben, die bis zu dieser Nummer fehlen.

Mein Programm funktioniert für kleine Dateien. Aber wenn ich eine große Datei (12MB) gebe, die Dateinummern bis 10000 haben kann, hängt es einfach.

Hier ist mein aktueller Python-Code

#! /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() 

Ausgang hängt, wenn ich eine 12MB-Datei geben, die Dateien von 0 zu 9999

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

Ausgang für eine kleine Datei (gleichen wie oben haben kann Beispiel)

$python findFileNumbers.py 
Enter file path: sample.log 
Enter end file number: 5 
[0, 2, 3] 
[1, 4] 
  1. Wie kann ich t machen seine Arbeit für große Akten?
  2. Gibt es eine bessere Möglichkeit, diese Ergebnisse anstelle eines Python-Skripts zu erhalten?

Vielen Dank im Voraus!

+0

Groß in was? Die Anzahl der zu durchsuchenden Dateien, die Größe der Daten in der Datei, die Länge des Namens? – Mark

+0

Ich gab eine 12MB-Datei als Eingabe und die Anzahl der Dateien, die es durchsuchen kann, ist 10.000 – SyncMaster

+0

Es ist nicht erforderlich, die Dateien im Speicher abzubilden, wenn Sie nur ihre Namen abrufen müssen. – Mark

Antwort

2

zuerst die vorhandenen in einem Satz sammeln und dann nach den fehlenden suchen.

Der Grund, warum Sie hängen, weil Sie die gesamte Datei für jede Nummer durchlaufen. d. h. 12MB * 10000 = 120GB Das Skript läuft über 120 GB und so hängt es auch, wenn Sie es in mmap haben.

+2

Sie müssen '. *?' In Ihrem Regex verwenden, wenn OPs Implikation, dass mehrere Zahlen in einer Zeile auftreten können, korrekt sind. –

1

Ich würde vorschlagen, dass Sie einfach die Eingabedatei Zeile für Zeile durchlesen und jede Zeile für ihre Dateinummer analysieren. Verwenden Sie dann diese Dateinummer als Index für ein boolesches Array, das anfänglich auf False gesetzt wird.

Sie führen keine Verarbeitung aus, bei der die Datei gespeichert werden muss. Dieser Ansatz funktioniert für sehr große Dateien.

#~ 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() 

Dies erzeugt das folgende Ergebnis, aus dem Ihre filesPresent und filesMissing leicht wiederhergestellt werden.

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

Werfen wir einen Blick darauf werfen, was Sie tatsächlich tun, hier sind:

  1. Speicher die Datei zuordnen.
  2. Für jede Nummer

    a. Kompilieren Sie einen regulären Ausdruck für diese Zahl.
    b.Suchen Sie nach dem regulären Ausdruck in der gesamten Datei.

Dies ist sehr ineffizient für große Zahlen. Während Memory Mapping Ihnen eine stringartige Schnittstelle zu der Datei gibt, ist es keine Magie. Sie müssen immer noch Teile der Datei laden, um sich darin zu bewegen. Zur gleichen Zeit machen Sie einen Durchlauf, möglicherweise über die gesamte Datei, für jeden Regex. Und das Regex-Matching ist ebenfalls teuer.

Die Lösung hier wäre ein einzelner Durchlauf durch die Datei, Zeile für Zeile. Sie sollten den regulären Ausdruck vorkompilieren, anstatt ihn einmal pro Nummer zu kompilieren, wenn Sie nach einer großen Zahl suchen müssen. Um alle Zahlen in einem einzigen Durchgang zu erhalten, könnten Sie eine von allen Zahlen bis zu der gewünschten, "fehlend", und eine leere set genannt "gefunden" machen. Immer wenn Sie auf eine Zeile mit einer Nummer stoßen, verschieben Sie die Nummer von "Missing" zu "Found".

Hier ist eine Beispielimplementierung:

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 

Beachten Sie, dass der reguläre Ausdruck die reluctant quantifier.*? nach file_ verwendet. Dies wird so wenige Zeichen wie möglich übereinstimmen, bevor Sie nach einer Ziffer suchen. Wenn Sie den standardmäßigen gierigen Quantifizierer .* haben, würden mehrere Zahlen in einer Zeile nur mit der letzten übereinstimmen.

Verwandte Themen