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

1voto

goger Punkte 590

Wie AlbertoPL sagte, speichern Sie Zeilen in einem FIFO für spätere Verwendung - gehen Sie nicht "rückwärts". Dafür würde ich auf jeden Fall Python über bash+sed/awk oder ähnliches verwenden.

Ich habe mir einen Moment Zeit genommen, um diesen Schnipsel zu codieren:

from collections import deque
line_fifo = deque()
for line in open("test"):
    line_fifo.append(line)
    if len(line_fifo) == 4:
        # "blicke 3 Zeilen zurück"
        if line_fifo[0] == line_fifo[-1] == "@STRING_A\n":
            # diese Übereinstimmung loswerden
            line_fifo.popleft()
        else:
            # gib den obersten Wert des FIFO aus
            print line_fifo.popleft(),
# vergiss nicht, den FIFO am Ende der Datei auszugeben
for line in line_fifo: print line,

0 Stimmen

Der OP sagt, dass er nur das Vorkommen von "@STRING_A" am Anfang der Zeile gelöscht haben möchte ... "Zeile beginnend mit", "das Vorkommen löschen" ('Vorkommen' wird überall verwendet, um diesen String zu bedeuten). Alle scheinen anzunehmen, dass die gesamte Zeile (a) gegen (b) getestet und gelöscht werden soll. Punkt 2: Warum selbst einen FIFO erstellen, wenn bereits eine Deque vorhanden ist?

0 Stimmen

@John: Ich denke, der Ersteller des Originalbeitrags hätte die Dinge genauer machen können, indem er ein Beispiel dafür gibt, wie die Datei vor und nach dem Löschen aussehen sollte. Sowohl mein obiger Code als auch der Code von goger sollten ausreichen, um das Problem zu lösen.

1 Stimmen

@John: Meiner Meinung nach ist der erste Punkt ein Implementierungsdetail für den OP, das sich am Rand der Frage befindet. Dein zweiter Punkt bezüglich der deque ist gut und ich habe meinen Code aktualisiert.

0voto

jkerian Punkte 15571

Mein awk-fu war noch nie so gut... aber das Folgende könnte dir das bieten, wonach du in Form einer bash-shell/shell-utility suchst:

sed `awk 'BEGIN{ORS=";"}
/@STRING_A/ {
  if(LAST!="" && LAST+3 >= NR) print LAST "d"
  LAST = NR
}' test_file` test_file

Im Grunde genommen... awk erstellt einen Befehl für sed, um bestimmte Zeilen zu entfernen. Ich bin mir sicher, dass es einen relativ einfachen Weg gibt, um awk alles verarbeiten zu lassen. Aber das scheint zu funktionieren.

Der Nachteil? Es liest die test_file zweimal.

Der Vorteil? Es ist eine bash/shell-utility Implementierung.

Bearbeitung: Alex Martelli weist darauf hin, dass die obige Beispieldatei mich möglicherweise verwirrt hat. (mein obiger Code löscht die ganze Zeile, anstatt nur die @STRING_A Flagge)

Dies kann leicht behoben werden, indem der sed-Befehl angepasst wird:

sed `awk 'BEGIN{ORS=";"}
/@STRING_A/ {
  if(LAST!="" && LAST+3 >= NR) print LAST "s/@STRING_A//"
  LAST = NR
}' test_file` test_file

0voto

John Machin Punkte 78125

Diese "Antwort" ist für lyrae ... Ich werde meinen vorherigen Kommentar ändern: Wenn sich die Nadel in den ersten 3 Zeilen der Datei befindet, wird Ihr Skript entweder einen IndexError verursachen oder auf eine Zeile zugreifen, auf die es nicht zugreifen sollte, manchmal mit interessanten Nebenwirkungen.

Beispiel dafür, dass Ihr Skript einen IndexError verursacht:

>>> lines = "@string line 0\nblah blah\n".splitlines(True)
>>> needle = "@string "
>>> for i,line in enumerate(lines):
...     if line.startswith(needle) and lines[i-3].startswith(needle):
...         lines[i-3] = lines[i-3].replace(needle, "")
...
Traceback (most recent call last):
  File "", line 2, in 
IndexError: list index out of range

und dieses Beispiel zeigt nicht nur, dass die Erde rund ist, sondern auch, warum Ihr "Fix" für das Problem "lösche nicht die ganze Zeile" .replace(needle, "", 1) oder [len(needle):] anstelle von .replace(needle, "") verwenden sollte

>>> lines = "NEEDLE x NEEDLE y\nnoddle\nnuddle\n".splitlines(True)
>>> needle = "NEEDLE"
>>> # Erwartetes Ergebnis: keine Änderung an der Datei
... for i,line in enumerate(lines):
...     if line.startswith(needle) and lines[i-3].startswith(needle):
...         lines[i-3] = lines[i-3].replace(needle, "")
...
>>> print ''.join(lines)
 x  y   <<<=== hoppla!
noddle
nuddle
        <<<=== immer noch unerwünschte neue Zeile hier
>>>

-1voto

Andrew Punkte 1823

In Bash kannst du sort -r filename und tail -n filename verwenden, um die Datei rückwärts zu lesen.

$LINES=`tail -n filename | sort -r`
# jetzt durch die Zeilen iterieren und deine Überprüfungen durchführen

0 Stimmen

Wie zum Teufel löst das alphabetische Sortieren einer Datei (sort -r) oder das Ausgeben der letzten n Zeilen einer Datei (tail -n) dieses Problem?

0 Stimmen

Welches Betriebssystem verwenden Sie? sort -r sortiert unter Linux nicht alphabetisch. Ich habe für Klarheit bearbeitet. thelinuxblog.com/linux-man-pages/1/sort thelinuxblog.com/linux-man-pages/1/tail

-1voto

sqram Punkte 6791

Dies könnte das sein, wonach Sie suchen?

lines = open('sample.txt').readlines()

needle = "@string "

for i,line in enumerate(lines):
    if line.startswith(needle) and lines[i-3].startswith(needle):
        lines[i-3] = lines[i-3].replace(needle, "")
print ''.join(lines)

dies gibt aus:

string 0 extra text
string 1 extra text
string 2 extra text
string 3 extra text
--replaced --  4 extra text
string 5 extra text
string 6 extra text
@string 7 extra text
string 8 extra text
string 9 extra text
string 10 extra text

0 Stimmen

Ersetzt die Nadel anstelle sie zu entfernen. Liest die gesamte Datei in den Speicher und macht DANN EINE ANDERE Kopie während der Druckanweisung am Ende. Schreibt eine zusätzliche Zeilenumbruch am Ende der Ausgabe. Wird abstürzen (IndexError), wenn die Nadel in den ersten 3 Zeilen auftritt.

0 Stimmen

Leicht behebbar. Er kann die Ersetzung "" vornehmen. Damit wird die Nadel aus der Zeile gelöscht. Verursacht keinen Indexfehler. Muss nicht am Ende gedruckt werden; kann direkt in eine andere Datei schreiben. Es kopiert jedoch die gesamte Datei in den Speicher.

0 Stimmen

Kann verursacht werden, um einen IndexError zu verursachen. Kann gemacht werden, um eine unschuldige Zeile zu löschen. Das Ersetzen von "" reicht nicht aus. Siehe die "Antwort" zur Demonstration.

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