6 Stimmen

Bash oder Python zum Rückwärtsgehen?

Ich habe eine Textdatei mit vielen zufälligen Vorkommen der Zeichenkette @STRING_A, und ich wäre daran interessiert, ein kurzes Skript zu schreiben, das nur einige von ihnen entfernt. Insbesondere eines, das die Datei durchsucht und sobald es eine Zeile findet, die mit dieser Zeichenfolge beginnt, wie

@STRING_A

prüft dann, ob 3 Zeilen zurück eine weitere Zeile vorkommt, die mit der gleichen Zeichenkette beginnt, etwa

@STRING_A

@STRING_A

und wenn es passiert, das Vorkommen 3 Zeilen rückwärts zu löschen. Ich dachte an Bash, aber ich weiß nicht, wie man damit "rückwärts" geht. Ich bin mir also sicher, dass dies mit Bash nicht möglich ist. Ich habe auch an Python gedacht, aber dann müsste ich alle Informationen im Speicher ablegen, um rückwärts gehen zu können, und dann wäre es bei langen Dateien nicht machbar.

Was meinen Sie dazu? Ist es möglich, es in Bash oder Python zu tun?

Gracias

4voto

Alex Martelli Punkte 805329

Seltsam, dass nach all den Stunden noch niemand eine Lösung für das Problem in seiner eigentlichen Form gefunden hat (wie @John Machin in einem Kommentar anmerkt) - nur die führende Markierung zu entfernen (wenn 3 Zeilen weiter unten eine weitere solche Markierung folgt), nicht die ganze Zeile, die sie enthält. Es ist natürlich nicht schwer - hier ist zum Beispiel eine kleine Modifikation von @truppos lustiger Lösung:

from itertools import izip, chain
f = "foo.txt"
for third, line in izip(chain("   ", open(f)), open(f)):
    if third.startswith("@STRING_A") and line.startswith("@STRING_A"):
        line = line[len("@STRING_A"):]
    print line,

Im wirklichen Leben würde man natürlich eine iterator.tee statt die Datei zweimal zu lesen, diesen Code in einer Funktion zu haben, nicht die Markierungskonstante endlos zu wiederholen, &c;-).

2voto

AlbertoPL Punkte 11396

Natürlich funktioniert auch Python. Speichern Sie einfach die letzten drei Zeilen in einem Array und prüfen Sie, ob das erste Element des Arrays mit dem Wert übereinstimmt, den Sie gerade lesen. Löschen Sie dann den Wert und geben Sie das aktuelle Array aus. Anschließend verschieben Sie Ihre Elemente, um Platz für den neuen Wert zu schaffen, und wiederholen den Vorgang. Wenn das Array gefüllt ist, müssen Sie natürlich sicherstellen, dass Sie weiterhin Werte aus dem Array verschieben und die neu gelesenen Werte einfügen, wobei Sie jedes Mal überprüfen, ob der erste Wert im Array mit dem Wert übereinstimmt, den Sie gerade lesen.

2voto

mthurlin Punkte 24849

Hier ist eine lustigere Lösung, die zwei Iteratoren mit einem Offset von drei Elementen verwendet :)

from itertools import izip, chain, tee
f1, f2 = tee(open("foo.txt"))
for third, line in izip(chain("   ", f1), f2):
    if not (third.startswith("@STRING_A") and line.startswith("@STRING_A")):
        print line,

1voto

DevSolar Punkte 63096

Warum sollte das in der Bash nicht möglich sein? Sie müssen nicht die gesamte Datei im Speicher behalten, sondern nur die letzten drei Zeilen (wenn ich das richtig verstanden habe), und schreiben Sie, was für Standard-Out geeignet ist. Leiten Sie das in eine temporäre Datei um, überprüfen Sie, ob alles wie erwartet funktioniert hat, und überschreiben Sie die Quelldatei mit der temporären Datei.

Das Gleiche gilt für Python.

Ich würde ein eigenes Skript zur Verfügung stellen, aber das würde nicht getestet werden ;-)

1voto

Martin Geisler Punkte 71257

Dieser Code durchsucht die Datei und entfernt Zeilen, die mit der Markierung beginnen. Es werden standardmäßig nur drei Zeilen im Speicher gehalten:

from collections import deque

def delete(fp, marker, gap=3):
    """Delete lines from *fp* if they with *marker* and are followed
    by another line starting with *marker* *gap* lines after.
    """
    buf = deque()
    for line in fp:
        if len(buf) < gap:
            buf.append(line)
        else:
            old = buf.popleft()
            if not (line.startswith(marker) and old.startswith(marker)):
                yield old
            buf.append(line)
    for line in buf:
        yield line

Ich habe es mit getestet:

>>> from StringIO import StringIO
>>> fp = StringIO('''a
... b
... xxx 1
... c
... xxx 2
... d
... e
... xxx 3
... f
... g
... h
... xxx 4
... i''')
>>> print ''.join(delete(fp, 'xxx'))
a
b
xxx 1
c
d
e
xxx 3
f
g
h
xxx 4
i

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X