Leerzeilen einer Datei Entfernen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
shdw
User
Beiträge: 3
Registriert: Dienstag 21. Juni 2016, 14:03

Hallo liebe Community,

wie ihr seht, der hier ist mein erster Post. Ich arbeite hauptsächlich mit LUA, möchte ich aber auf Py umsteigen. :D

Nachdem ich 22 Seiten von diesem Forum durchgelesen, und leider die Antwort für mein "Problem" nicht gefunden habe, stelle ich euch meine Frage:

Ich habe eine datei, die heißt test (reine Text-Datei).

test Inhalt:

Code: Alles auswählen

erste linie
zweite linie

dritte linie
Meine Aufgabe hier ist die Leerzeile von der Datei zu entfernen und zu überschreiben.

Dafür habe ich ein kleines "Python Script" geschrieben:

Code: Alles auswählen

with open('/home/*******/test/test','w') as file:
        for line in file:
                ohneLeerzeilen = line.strip()
                if ohneLeerzeilen:
                        file.write(ohneLeerzeilen)
                        print ohneLeerzeilen
Für mich macht das Sinn, für euch wahrscheinlich nicht. Tatsächlich wenn ich file.write(ohneLeerzeilen) auskommentiere, printet mir Python die Datei genauso wie ich sie haben möchte, überschreiben tut er sie nicht. mit 'w+' überschreibt er die Datei, allerdings mit nichts.

Vielen Dank im Voraus wenn ihr mir helfen könnt, wenn nicht, bedanke ich mich fürs lesen zumindest :).

Gruß
Shdw
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@shdw: wenn Du Dateien im Modus 'w' öffnest, wird eine neue leere Datei erzeugt. Lesen schlägt dabei fehl (IOError: File not open for reading). Wenn Du die Datei zum Schreiben und Lesen öffnest 'w+', kannst Du zwar lesen und schreiben, das löst aber Dein Problem nicht wirklich, weil es nur ein Dateipositionszeiger gibt, so dass gleichzeitiges Lesen und Schreiben wahrscheinlich Quark ergibt. Dieses Verhalten ist Systembedingt, also Lua verhält sich da ganz ähnlich.

Das übliche Vorgehen ist es, eine neue Datei zu öffnen, aus der ersten Datei liest Du, in die zweite schreibst Du.
shdw
User
Beiträge: 3
Registriert: Dienstag 21. Juni 2016, 14:03

Yo!

ich dachte ich könnte die Datei lesen, bearbeiten und schreiben mit r+ oder w. Es ist leider nicht der Fall.

Das wäre meine Lösung dazu:

Code: Alles auswählen

with open('/home/*******/test/test_output','w') as output_file:
with open('/home/******/test/test_input','r+') as file:
    for line in file:
        ohneLeerzeile = line.strip()
        if len(ohneLeerzeile) > 0:
            file.write(ohneLeerzeile + "\n")
            # print("*"+line+"*"+ohneLeerzeile+"*")
Ziemlich blöd, da ich das bräuchte nur um "unnötigen" Leerzeilen von .msg-Dateien (outlook Mails) die als .txt gespeichert werden (MIME encoding kann Cyrus nicht so gut verarbeiten. Wenn Mails wieder in Outlook eingebunden werden hat jede ursprüngliche Leerzeile sich mal 5 mutipliziert...).

Ich werde wahrscheinlich mit Bash mich beschäftigen müssen :( :( :(
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@shdw: ich habe nicht geschrieben, dass es nicht geht (solange etwas gelöscht wird). Aber diese Operation in einer Datei auszuführen ist sehr viel komplexer und damit fehleranfälliger. Das 'r+' sollte übrigens ein 'r' sein, da Du nur lesen willst. «ohneLeerzeile» ist der falsche Name für die Variable, weil das nicht etwas ohne Leerzeilen ist, sondern diese Zeile soll ja darauf geprüft werden, ob es eine Leerzeile ist, oder nicht. Du löscht auch nicht nur Leerzeilen aus der Datei, sondern auch alle Leerzeichen am Anfang oder Ende der Zeile. Ist das so gewollt?
Sollten die Dateien nicht zu groß sein, kann man sie auch alternativ komplett in den Speicher lesen und dort bearbeiten.

Übrigens ist das Lesen und Schreiben etwas, das das Betriebsystem bereitstellt, da hilft Dir also auch nicht auf Bash auszuweichen, oder Lua oder sonst eine Programmiersprache.
shdw
User
Beiträge: 3
Registriert: Dienstag 21. Juni 2016, 14:03

@Sirius3

Ich verstehe, was du meinst...

Mein Fall ist der folgende: Ich benutze Cyrus Bboards um Mails von Outlook zu archivieren / als functional Mailbox (mehrere Personen dürfen darauf zugreifen usw).

Wenn z.B. Mails von Outlook in diese Mailboxen verschoben werden, speichert Cyrus diese als .txt im Server. Diese Dateien bleiben im Outlook-Cache für eine Weile, während sie noch im Cache sind, stimmt alles (Formatierung, Anhänge, Signaturen, usw...). Sobald diese Mails nicht mehr im Cache sind und man sie wieder öffnet, ist die ganze Formatierung am A.

Ein Beispiel vom OriginalMail

Code: Alles auswählen

Hallo Sirius3,

ich bedanke mich für deine Hilfe.

Viele Grüße
Shdw
und hier ein Beispiel vom nicht mehr gecachten Mail

Code: Alles auswählen

Hallo Sirius3,




ich bedanke mich für deine Hilfe.






Viele Grüße
shdw




Deswegen wollte ich ein Skript schreiben, der diese Leerzeilen einfach löscht und die selbe Datei überschreibt. Da viele Mails Anhänge haben, sind die .txt Dateien teilweise ziemlich groß.

Ich wollte eigentlich nur verstehen wie Python das macht um später eventuell eine Schleife zu erzeugen, die nicht ALLE Leerzeilen löscht, sondern erst prüft wenn eine Leerzeile von einer anderen gefolgt wird, nur eine davon gelöscht wird (sorry wg. des Ausdruckes, Deutsch ist nicht meine Muttersprache :) ).

Hoffentlich könntest du was dazu sagen, mich würde es interessieren wie du das besiegen würdest!

Gruß
shdw
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@shdw: es ist übliches vorgehen, eine Datei nicht einfach zu überschreiben, weil bei einem Fehler Daten verloren gehen können. Ich glaube kaum, dass irgendein Programm Leerzeilen nach dem Zufallsprinzip einfügt. Statt also Symptome zu behandeln wäre es doch besser, nach der Ursache oder wenigstens nach einer Systematik zu suchen.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Da viele Mails Anhänge haben, sind die .txt Dateien teilweise ziemlich groß.
Wobei eine Leerzeile normalerweise nur 1 oder 2 Bytes sind. D.h. effektiv Speicherplatz sparst du so kaum.

Gruß, noisefloor
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Zum Weglassen mehrerer Leerzeilen könnte man es so machen:

Code: Alles auswählen

def remove_multiple_blanks(lines):
    lines = iter(lines)
    for line in lines:
        if not line.strip():
            blank_line = line
            for line in lines:
                if line.strip():
                   # Found non-blank line
                   break 
            else:
                # No more lines (EOF)
                return
            yield blank_line
        yield line

def main():
    text = 'Hallo Sirius3,\n\n\n\n\nich bedanke mich für deine Hilfe.\n\n\n\n\nViele Grüße\n\n\nshdw\n\n\n\n\n'
    print('Original:')
    print(text)
    lines = text.splitlines(True)
    clean_text = ''.join(remove_multiple_blanks(lines))
    print('Bereinigt:')
    print(clean_text)


if __name__ == '__main__':
    main()
Hierbei werden alle Zeilen im Original ausgegeben, wenn in ihnen mindestens ein Zeichen enthalten ist, das nicht als Whitespace zählt (mit anderen Worten: wenn die Zeile beschrieben ist). Bei Leerzeilen wird die erste gefundene Leerzeile zurückgeliefert und alle direkt darauf folgenden Leerzeilen werden übersprungen. Leerzeilen am Textende, auf die keine beschriebene Zeile mehr folgt, werden ebenfalls übersprungen.

EDIT:
Der Quelltext ist relativ kurz, arbeitet aber auch mit diversen Sprachfeatures, die einem möglicherweise nicht gleich einleuchten, wenn man von einer anderen Programmiersprache kommt. Für einen Anfänger könnte es einfacher sein, wenn die eine oder andere Stelle mit etwas mehr Quelltext ausgedrückt wird. Spätestens dann sollte man den Code aber in mindestens eine weitere Funktion auslagern, wenn es übersichtlich bleiben soll.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt verschachtelter for-Schleifen finde ich ein Flag übersichtlicher:

Code: Alles auswählen

def remove_multiple_blanks(lines):
    skip_blank_lines = True
    for line in lines:
        blank_line = not line.strip()
        if not skip_blank_lines or not blank_line:
            yield line
        skip_blank_lines = blank_line
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Klar, meine kürzere Variante wäre nahezu identisch:

Code: Alles auswählen

def remove_multiple_blanks(lines):
    blank_seen = False
    for line in lines:
        blank = not line.strip()
        if not blank or not blank_seen:
            yield line
        blank_seen = blank
Dann müsste man Leerzeilen am Dateiende nachträglich entfernen.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: achso, da braucht es eine vor- und zurückschauende for-Schleife:

Code: Alles auswählen

def remove_multiple_blanks(lines):
    non_blank_seen = blank_seen = False
    for line in lines:
        blank = not line.strip()
        if not blank:
            if non_blank_seen and blank_seen:
                yield "\n"
            yield line
            non_blank_seen = True
        blank_seen = blank
BlackJack

Wobei ich die Idee mit dem erst mal schauen was dieses Phänomen verursacht immer noch am besten finde. Ich vermute mal irgendein beteiligtes Programm macht keinen sauberen Unterschied zwischen Text und Binärdateien und öffnet beim kopieren beispielsweise die Quelldatei im Binärmodus und schreibt dann im Textmodus. Da hat man unter Windows dann schnell mal etwas was nach zu vielen Zeilenumbrüchen aussieht.
BlackJack

Ungetestet:

Code: Alles auswählen

from itertools import groupby
from operator import itemgetter


def collapse_blank_lines(lines):
    for is_blank, group in groupby(
        ((not bool(line.strip()), line) for line in lines), itemgetter(0)
    ):
        if is_blank:
            yield '\n'
        else:
            for line in group:
                yield line
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wobei not bool(x) das selbe ist wie not x...
BlackJack

@snafu: Du hast zielsicher die Stelle gefunden, an der ich ganz kurz vorm Absenden noch ein Schlüsselwort eingefügt hatte damit die Logik stimmt. :-D
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dachte ich mir schon, dass es ursprünglich mal ein bool(...) war. :)
Antworten