Ich habe ~ 200 kurze Textdateien (50 KB), die alle ein ähnliches Format haben. Ich möchte in jeder dieser Dateien eine Zeile finden, die eine bestimmte Zeichenfolge enthält, und dann diese Zeile plus die nächsten drei Zeilen (aber nicht die restlichen Zeilen in der Datei) in eine andere Textdatei schreiben. Ich versuche mir Python beizubringen, um dies zu tun, und habe ein sehr einfaches und grobes kleines Skript geschrieben, um dies auszuprobieren. Ich verwende Version 2.6.5 und führe das Skript vom Mac-Terminal aus:

#!/usr/bin/env python

f = open('Test.txt')

Lines=f.readlines()
searchquery = 'am\n'
i=0

while i < 500:
    if Lines[i] == searchquery:
        print Lines[i:i+3]
        i = i+1
    else:
        i = i+1
f.close()

Dies funktioniert mehr oder weniger und druckt die Ausgabe auf den Bildschirm. Aber ich möchte stattdessen die Zeilen in eine neue Datei drucken, also habe ich so etwas versucht:

f1 = open('Test.txt')
f2 = open('Output.txt', 'a')

Lines=f1.readlines()
searchquery = 'am\n'
i=0

while i < 500:
if Lines[i] == searchquery:
    f2.write(Lines[i])
    f2.write(Lines[i+1])
    f2.write(Lines[i+2])
    i = i+1
else:
    i = i+1
f1.close()
f2.close()

Es wird jedoch nichts in die Datei geschrieben. Ich habe es auch versucht

from __future__ import print_function
print(Lines[i], file='Output.txt')

Und kann das auch nicht zum Laufen bringen. Wenn jemand erklären kann, was ich falsch mache, oder Vorschläge machen kann, was ich stattdessen versuchen sollte, wäre ich wirklich dankbar. Wenn Sie Vorschläge zur Verbesserung der Suche haben, würde ich mich auch über diese freuen. Ich habe eine Testdatei verwendet, in der die Zeichenfolge, die ich finden möchte, der einzige Text in der Zeile ist, aber in meinen realen Dateien befindet sich die Zeichenfolge, die ich benötige, immer noch am Anfang der Zeile, gefolgt von einer Reihe anderer Texte Ich denke, die Art und Weise, wie ich die Dinge jetzt eingerichtet habe, wird auch nicht wirklich funktionieren.

Danke und sorry, wenn dies eine super grundlegende Frage ist!

13
Andreanna 6 Okt. 2012 im 04:18

5 Antworten

Beste Antwort

Wie von @ajon hervorgehoben, glaube ich nicht, dass irgendetwas grundlegend falsch mit Ihrem Code ist, außer der Einrückung. Mit der Einrückung funktioniert es für mich. Es gibt jedoch einige Verbesserungsmöglichkeiten.

1) In Python wird standardmäßig eine { {X0}} Schleife. Wenn Sie eine for -Schleife verwenden, müssen Sie keine Schleifenzählervariablen definieren und diese selbst verfolgen, um die Dinge zu durchlaufen. Stattdessen schreibst du so etwas

for line in lines:
    print line

Um alle Elemente in einer Liste von Zeichenfolgen zu durchlaufen und zu drucken.

2) In den meisten Fällen sehen Ihre for - Schleifen so aus. Es gibt jedoch Situationen, in denen Sie tatsächlich die Anzahl der Schleifen verfolgen möchten. Ihr Fall ist eine solche Situation, da Sie nicht nur diese eine, sondern auch die nächsten drei Zeilen benötigen und daher den Zähler für die Indizierung verwenden müssen (lst[i]). Dafür gibt es enumerate(), das eine Liste der Elemente und ihren Index, über den Sie dann eine Schleife erstellen können.

for i, line in enumerate(lines):
    print i
    print line
    print lines[i+7]

Wenn Sie den Schleifenzähler wie in Ihrem Beispiel manuell verfolgen, gibt es zwei Dinge:

3) Das i = i+1 sollte aus den Blöcken if und else verschoben werden. Sie machen es in beiden Fällen, also setzen Sie es nach if/else. In Ihrem Fall macht der Block else dann nichts mehr und kann entfernt werden:

while i < 500:
    if Lines[i] == searchquery:
        f2.write(Lines[i])
        f2.write(Lines[i+1])
        f2.write(Lines[i+2])
    i = i+1

4) Dies führt nun zu einem IndexError mit Dateien, die kürzer als 500 Zeilen sind. Anstatt eine Schleifenzahl von 500 fest zu codieren, sollten Sie die tatsächliche Länge der Sequenz verwenden, über die Sie iterieren. len(lines) gibt Ihnen diese Länge. Verwenden Sie jedoch anstelle einer while - Schleife eine for - Schleife und range(len(lst)), um eine Liste des Bereichs von Null bis len(lst) - 1 zu durchlaufen.

for i in range(len(lst)):
    print lst[i]

5) open() kann als Kontextmanager, der das Schließen von Dateien für Sie übernimmt. Kontextmanager sind ein ziemlich fortgeschrittenes Konzept, aber ziemlich einfach zu verwenden, wenn sie bereits für Sie bereitgestellt wurden. Indem Sie so etwas tun

with open('test.txt') as f:
    f.write('foo')

Die Datei wird geöffnet und ist für Sie als f in diesem with Block zugänglich. Nachdem Sie den Block verlassen haben, wird die Datei automatisch geschlossen, sodass Sie nicht vergessen müssen, die Datei zu schließen.

In Ihrem Fall öffnen Sie zwei Dateien. Dies kann erreicht werden, indem nur zwei with Anweisungen verwendet und verschachtelt werden

with open('one.txt') as f1:
    with open('two.txt') as f2:
        f1.write('foo')
        f2.write('bar')

Oder in Python 2.7 / Python 3.x durch Verschachteln von zwei Kontextmanagern in einer einzigen with - Anweisung:

    with open('one.txt') as f1, open('two.txt', 'a') as f2:
        f1.write('foo')
        f2.write('bar')

6) Je nach Betriebssystem, auf dem die Datei erstellt wurde, unterscheiden sich die Zeilenenden. Auf UNIX-ähnlichen Plattformen sind es \n, Macs vor OS X \r und Windows \r\n. Damit Lines[i] == searchquery nicht für Mac- oder Windows-Zeilenenden übereinstimmt. file.readline() kann mit allen drei umgehen, aber da die am Ende der Zeile vorhandenen Zeilenenden beibehalten werden, schlägt der Vergleich fehl. Dies wird durch die Verwendung von str.strip() gelöst, wodurch die Zeichenfolge aller Leerzeichen bei entfernt wird den Anfang und das Ende und vergleichen Sie ein Suchmuster ohne das Zeilenende damit:

searchquery = 'am'
# ...
            if line.strip() == searchquery:
                # ...

(Das Lesen der Datei mit file.read() und str.splitlines() wäre eine andere Alternative.)

Da Sie jedoch erwähnt haben, dass Ihre Suchzeichenfolge tatsächlich am Anfang der Zeile angezeigt wird, können Sie dies mithilfe von {tun {X0}}:

if line.startswith(searchquery):
    # ...

7) Der offizielle Styleguide für Python, PEP8, empfiehlt die Verwendung CamelCase für Klassen, lowercase_underscore für so ziemlich alles andere (Variablen, Funktionen, Attribute, Methoden, Module, Pakete). Verwenden Sie also anstelle von Lines lines. Dies ist definitiv ein kleiner Punkt im Vergleich zu den anderen, aber es lohnt sich immer noch, es gleich richtig zu machen.


In Anbetracht all dieser Dinge würde ich Ihren Code so schreiben:

searchquery = 'am'

with open('Test.txt') as f1:
    with open('Output.txt', 'a') as f2:
        lines = f1.readlines()
        for i, line in enumerate(lines):
            if line.startswith(searchquery):
                f2.write(line)
                f2.write(lines[i + 1])
                f2.write(lines[i + 2])

Wie @TomK hervorhob, geht der gesamte Code davon aus, dass mindestens zwei Zeilen folgen, wenn Ihre Suchzeichenfolge übereinstimmt. Wenn Sie sich nicht auf diese Annahme verlassen können, können Sie diesen Fall mithilfe eines try...except behandeln Block wie von @poorsod vorgeschlagen ist der richtige Weg.

25
Lukas Graf 6 Okt. 2012 im 15:31

Ajon hat die richtige Antwort, aber solange Sie nach Anleitung suchen, nutzt Ihre Lösung nicht die übergeordneten Konstrukte, die Python anbieten kann. Wie wäre es mit:

searchquery = 'am\n'

with open('Test.txt') as f1:
  with open(Output.txt, 'a') as f2:

    Lines = f1.readlines()

    try:
      i = Lines.index(searchquery)
      for iline in range(i, i+3):
        f2.write(Lines[iline])
    except:
      print "not in file"

Die beiden "with" -Anweisungen schließen die Dateien am Ende automatisch, selbst wenn eine Ausnahme auftritt.

Eine noch bessere Lösung wäre, zu vermeiden, dass die gesamte Datei auf einmal eingelesen wird (wer weiß, wie groß sie sein könnte?) Und stattdessen Zeile für Zeile mithilfe der Iteration eines Dateiobjekts zu verarbeiten:

  with open('Test.txt') as f1:
    with open(Output.txt, 'a') as f2:
      for line in f1:
        if line == searchquery:
          f2.write(line)
          f2.write(f1.next())
          f2.write(f1.next())

All dies setzt voraus, dass sich mindestens zwei zusätzliche Linien hinter Ihrer Ziellinie befinden.

1
TomK 6 Okt. 2012 im 01:11

Das zeilenweise Schreiben kann beim Arbeiten mit großen Datenmengen langsam sein. Sie können die Lese- / Schreibvorgänge beschleunigen, indem Sie mehrere Zeilen gleichzeitig lesen / schreiben.

from itertools import slice

f1 = open('Test.txt')
f2 = open('Output.txt', 'a')

bunch = 500
lines = list(islice(f1, bunch)) 
f2.writelines(lines)

f1.close()
f2.close()

Wenn Ihre Zeilen zu lang sind und von Ihrem System abhängen, können Sie möglicherweise nicht 500 Zeilen in eine Liste aufnehmen. Wenn dies der Fall ist, sollten Sie die Größe bunch reduzieren und so viele Lese- / Schreibschritte wie nötig ausführen, um das Ganze zu schreiben.

0
computerist 4 Okt. 2017 im 16:03

Haben Sie versucht, etwas anderes als 'Output.txt' zu verwenden, um Probleme mit dem Dateisystem als Problem zu vermeiden?

Wie wäre es mit einem absoluten Weg, um unvorhergesehene Probleme bei der Diagnose zu vermeiden?

Dieser Rat ist einfach aus diagnostischer Sicht. Schauen Sie sich auch das OS X dtrace und dtruss an.

Siehe: Äquivalent zu strace -feopen auf Mac OS X.

1
Community 23 Mai 2017 im 12:18

Ich denke, Ihr Problem sind die Registerkarten der unteren Datei.

Sie müssen von if Lines[i] bis nach i=i+1 einrücken, z.

while i < 500:
    if Lines[i] == searchquery:
        f2.write(Lines[i])
        f2.write(Lines[i+1])
        f2.write(Lines[i+2])
        i = i+1
    else:
        i = i+1
2
ajon 6 Okt. 2012 im 00:24