6 Stimmen

Bash oder Python, um zurückzugehen?

Ich habe eine Textdatei, in der sehr häufig die Zeichenfolge @STRING_A vorkommt, und ich wäre daran interessiert, ein kurzes Skript zu schreiben, das nur einige davon entfernt. Insbesondere eines, das die Datei durchsucht und sobald es eine Zeile findet, die mit dieser Zeichenfolge beginnt, wie

@STRING_A

dann überprüft, ob es 3 Zeilen rückwärts ein weiteres Vorkommen einer Zeile gibt, die mit derselben Zeichenfolge beginnt, wie

@STRING_A

@STRING_A

und wenn dies geschieht, das Vorkommen 3 Zeilen rückwärts löscht. Ich dachte an bash, aber ich weiß nicht, wie ich damit "rückwärts gehen" soll. Ich bin also sicher, dass dies mit bash nicht möglich ist. Ich dachte auch an python, aber dann müsste ich alle Informationen im Speicher speichern, um rückwärts zu gehen und dann wäre es bei langen Dateien unpraktikabel.

Was denkst du? Ist es möglich, dies in bash oder python zu tun?

Danke

4voto

Alex Martelli Punkte 805329

Lustig, dass nach all diesen Stunden noch niemand eine Lösung für das Problem gegeben hat, wie es tatsächlich formuliert ist (wie @John Machin in einem Kommentar feststellt) - nur das führende Marker entfernen (wenn gefolgt von einem weiteren Marker 3 Zeilen weiter unten), nicht die gesamte Zeile, die es enthält. Natürlich ist es nicht schwer - hier ist zum Beispiel eine kleine Modifikation von @truppo's 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,

Natürlich würde man im echten Leben anstelle des doppelten Lesens der Datei einen iterator.tee verwenden, diesen Code in einer Funktion haben, die Markerkonstante nicht endlos wiederholen usw. ;-).

0 Stimmen

Warum würdest du den Code von truppo so neu veröffentlichen? Und warum beschwert ihr euch immer über unsere vollkommenen Lösungen, wenn die Frage von Anfang an nicht sehr klar ist?

0 Stimmen

@Martin, Ich stimme John Machin hinsichtlich der wahrscheinlichen Interpretation der Frage zu -- obwohl du recht hast, dass sie etwas mehrdeutig ist, und deine oder truppos Lösungen unter einer anderen Interpretation akzeptabel wären, dachte ich, dass es besser wäre, die Lösung zur wahrscheinlichsten Interpretation zu posten, anstatt sie unbeantwortet zu lassen. Ich habe truppos Antwort (natürlich mit vollem Kredit!) als Basis ausgewählt, weil ich mit deinem Kommentar übereinstimme, dass sie cool ist, und habe sie nicht bearbeitet, da dies gegen die Bearbeitungsrichtlinien verstoßen würde. Hoffe, das hilft!

2voto

AlbertoPL Punkte 11396

Natürlich funktioniert auch Python. Speichern Sie einfach die letzten drei Zeilen in einem Array und überprüfen Sie, ob das erste Element im Array mit dem Wert übereinstimmt, den Sie gerade lesen. Löschen Sie dann den Wert und geben Sie das aktuelle Array aus. Anschließend würden Sie die Elemente verschieben, um Platz für den neuen Wert zu schaffen, und den Vorgang wiederholen. Natürlich, wenn das Array gefüllt ist, müssen Sie sicherstellen, dass Sie weiterhin Werte aus dem Array entfernen 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 unterhaltsamere Lösung, bei der zwei Iteratoren mit einem Dreielement-Offset verwendet werden :)

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") und line.startswith("@STRING_A")):
        print line,

0 Stimmen

Sehr cool! :-) Mit der tee-Funktion von itertools ("T", wie bei der Art von Rohr, das Sie verwenden, um ein Wasserrohr in zwei Rohre aufzuteilen), können Sie zwei Iteratoren für die Datei erhalten und so vermeiden, die Datei zweimal lesen zu müssen. Ich glaube nicht, dass es hier viel ausmachen wird, da das Betriebssystem die Datei sowieso puffern würde, aber es macht Spaß, mit Iteratoren zu spielen :-)

1voto

DevSolar Punkte 63096

Warum sollte das in Bash nicht möglich sein? Du musst nicht die ganze Datei im Speicher behalten, sondern nur die letzten drei Zeilen (wenn ich richtig verstanden habe) und das passende in die Standardausgabe schreiben. Leite das in eine temporäre Datei um, überprüfe, ob alles wie erwartet funktioniert hat, und überschreibe die Quelldatei mit der temporären.

Gleiches gilt für Python.

Ich könnte mein eigenes Skript zur Verfügung stellen, aber das wäre nicht getestet. ;-)

1voto

Martin Geisler Punkte 71257

Dieser Code durchsucht die Datei und entfernt Zeilen, die mit dem Marker beginnen. Standardmäßig werden nur drei Zeilen im Speicher behalten:

from collections import deque

def delete(fp, marker, gap=3):
    """Lösche Zeilen aus *fp*, wenn sie mit *marker* beginnen und von einer anderen Zeile gefolgt werden, die *marker* *gap* Zeilen später beginnt.
    """
    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 folgendem 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

0 Stimmen

This translation includes both the original text and the translated text in German: Original text: Doesn't look correct to me. The OP said nothing about deleting a region. He did say: """random ocurrences of the string @STRING_A, and I would be interested in writing a short script which removes only some of them""" and """delete the ocurrence 3 lines backward""". Translated text: Sieht für mich nicht richtig aus. Der OP hat nichts über das Löschen eines Bereichs gesagt. Er sagte: """zufällige Vorkommen der Zeichenfolge @STRING_A, und ich wäre daran interessiert, ein kurzes Skript zu schreiben, das nur einige davon entfernt""" und """das Auftreten 3 Zeilen rückwärts löschen""".

0 Stimmen

Nun, es war trivial, den Code zu aktualisieren, um die Frage anzupassen :-)

0 Stimmen

S/trivial update/Neuschreiben/ ... und du hast immer noch nicht verstanden, dass der OP gesagt hat, dass er alle Vorkommen des Strings entfernen wollte, NICHT die ganze Zeile.

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