Textdatei bei Auftreten einer bestimmten Sequenz mehrere Zeilen kopieren

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.
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Hallo zusammen,

ich möchte gerne eine Thunderbird Textdatei auseinandernehmen und in Struktur bringen. Das bedeutet, mich interessiert keine Header oder mailspezifisches Element sondern nur der Text der im Base64 Format vorliegt. Den Anfang finde ich immer aber ich weiß dann leider nicht wie ich weiterkomme.

ich versuche ein Beispiel des Willens:

Code: Alles auswählen

fin = open("inbox", "rt")
fout = open("testergebnis.txt", "wt")
for line in fin:
	#wenn die erste Zeile base64 gefunden ist
	if line.strip() == 'PHA+RXM':
		fout.write(line.rstrip()
	#nun möchte ich gerne eine unklare Anzahl von Zeilen kopieren, eben soweit wie der base64 String geht und habe als Anfänger keine gute Idee 	

am liebesten würde ich laufe die Zeilen mit If durch aber dann aber dann kann ich die "For line in" - Funktion nicht nutzen und hänge da auch wieder fest.
Kann mir jemand einen guten Tipp geben? Besten Dank

Wolke
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

AIHelfer
User
Beiträge: 2
Registriert: Sonntag 22. Januar 2023, 15:24

Hallo Wolke,

es klingt so, als ob du noch ganz am Anfang des Prozesses des Auseinandersetzens von Base64-Formatierten Texten bist. Daher würde ich empfehlen, zuerst einmal ein wenig mehr über das Thema zu lernen, bevor du versuchst, es in deinem Projekt zu implementieren.

Um hier konkret etwas zu deiner Frage beitragen zu können, kannst du versuchen, die Zeilen in einem String-Array aufzunehmen. Möglicherweise kannst du dann jede Zeile nach dem gleichen Format überprüfen und zu einem anderen Array hinzufügen. Wenn alle Zeilen mit dem gleichen Format übereinstimmen, kannst du sie abschließend in einem einzigen String zusammenführen. Um Base64-Formatierten Text auseinanderzunehmen, musst du einige Schritte durchlaufen.

Erstens musst du dafür sorgen, dass du die Zeilen vollständig in einem String-Array speicherst. Dazu kannst du eine for-Schleife verwenden, um jede Zeile des ursprünglichen Dokuments zu überprüfen und sie anschließend in einem neuen Array zu speichern. Danach kannst du das String-Array nach dem gleichen Format überprüfen, um sicherzustellen, dass jedes Zeichen der Zeilen an die gleiche Position des neuen Arrays kopiert wird.

Abschließend kannst du den Inhalt des neuen Arrays verwenden, um den kodierte Text wiederherzustellen und ihn in einen lesbaren Format zu übersetzen.

Code: Alles auswählen

fin = open("inbox", "rt")
fout = open("testergebnis.txt", "wt")

# String-Array initialisieren
string_array = []

# Zeilen in einem String-Array speichern
for line in fin:
	# Prüfen, ob die erste Zeile base64 ist
	if line.strip() == 'PHA+RXM':
		string_array.append(line)
	# Andere Zeilen hinzufügen so lange base64-Format gefunden wird
	elif string_array and line.strip().endswith('=='):
		string_array.append(line.rstrip())

# Alle Elemente des Arrays auf einmal ausgeben
fout.write(''.join(string_array))

fin.close()
fout.close()

Hoffe, ich konnte ein wenig helfen!
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Hallo Sirus3,

ich hatte mich damit beschäftigt aber mir schien das noch komplizierter, dabei habe ich ja herausgefunden, dass TB eine sehr simple Textdatei erzeugt. Außerdem möchte ich durchaus lernen - stehe ja noch sehr am Anfang. Viel allgemeiner ist das Problem Texte zu bearbeiten. Ich denke ich brauche also eigentlich nur ein Kommanda mit dem ich direkt die nächste Zeile springe und den Inhalt auswerten kann, sowas wie skip
Andernfalls müsste man etwas an der For Schleife ändern - hat noch jemand eine Idee?

Wolke
__deets__
User
Beiträge: 14540
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es ist garantiert nicht einfacher, das Format selbst zu parsen, als die Bibliothek zu benutzen.
Benutzeravatar
__blackjack__
User
Beiträge: 13107
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@wolkejo: TB erzeugt keine simple Textdatei. Du *denkst* die sei simpel, ist sie aber nicht wirklich. Dafür gibt es eine Spezifikation, und dafür wiederum gibt es Module in der Standardbibliothek. Der Umstand, dass da bestimmte Teile Base64 kodiert, sind ist ja nicht einmal die Entscheidung vom TB. Der speichert da was andere Mailprogramme zusammengeklöppelt haben. Man könnte sogar in Frage stellen, ob das überhaupt eine Textdatei ist, weil verschiedene Teile der Datei unterschiedliche Textkodierungen verwenden können, womit man das als Binärdatei verarbeiten muss, oder zumindest in einer 1-Byte-pro-Zeichen-Kodierung die alle 256 Werte abbildet und wo man dann notfalls noch mal verlustfrei in die tatsächliche Kodierung umkodieren kann. Und diese ganzen Fälle sind in den vorhandenen Modulen schon berücksichtigt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Okay, angenommen das ist also so, dass es besser wäre mit TB und mbox, kannst mir jemand eine Codezeile zeigen die den Body jeder Mail liest?
Das wäre hier eine konkrete Hilfe zum Thema, trotzdem würde ich gerne einen Hinweis bekommen, wie ich eine Textdatei durchlauf in der ich eine Element finde, und dann eine Anzahl x Textzeilen kopiere, das würde nur die base64 Codes einlesen und diese sind ja der Body.

Hat vielleicht noch jemand eine Idee? Dickes Danke
__deets__
User
Beiträge: 14540
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bis zu den Beispielen aus der offiziellen Dokumentation darf man durchaus mal eigenstaendig vorlaufen: https://docs.python.org/3/library/mailbox.html#examples
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Hallo AIHelfer,

das hat schon super geholfen - dafür meinen herzlichen Dank und ja, base64 ist der nächtste Teil mit dem ich mich beschäftige.
Dass ich Anfänger bin hatte ich ja schon geschrieben und in sofern benötige ich eher ab und an einen Denkanstoss als eine komplette Lösung. ;-)
Leider habe ich Deinen Post zu spät gesehen - das Beispiel verstehe ich allerdings funktionierte es so nicht weil der base64 code nicht konstant mit '==' endet und die Anfangszeile auch nur mit "PHA+RXM" beginnt. Nun funktioniert es so und für alle die es brauchen, so schaut es aus. 1000 Dank!

EDIT: noch nicht so ganz aber wenn ich es habe, dann poste ich es.

Code: Alles auswählen

fin = open("messages.csv", "rt")
fout = open("testergebnis.txt", "wt")

# String-Array initialisieren
string_array = []
gefunden = 0
# Zeilen in einem String-Array speichern
for line in fin:
	# Prüfen, ob die erste Zeile base64 ist
	if line.startswith('PHA+RXM') :
		string_array.append(line.strip())
	# Andere Zeilen hinzufügen so lange base64-Format gefunden wird
		gefunden = 1
	elif gefunden==1 and (len(line))>1 :
			string_array.append(line.rstrip())
	else :
		gefunden = 0  
		
# Alle Elemente des Arrays auf einmal ausgeben
fout.write(''.join(string_array))

fin.close()
fout.close()
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@AIHelfer: das was Du string_array nennst ist eine Liste. Arrays sind ein anderer Datentyp und es verwirrt sehr, wenn im Code etwas anderes steht als der Kommentar behauptet.
Eingerückt wird immer mit vier Leerzeichen pro Ebene, keine Tabs.

@wolkoe: statt 1 und 0 kennt Python True und False. Dateien öffnet man innerhalb eines with-Statements.

Code: Alles auswählen

with open("messages.csv", "rt") as lines:
    lines_found = []
    copying =False
    for line in lines:
        if copying:
            if len(lines) <= 1:
 	        copying =False
            else:
                lines_found.appen(line)
        elif line.startswith('PHA+RXM'):
            copying =True
            lines_found.append(line)
with open("testergebnis.txt", "wt") as output:
    output.writelines(lines_found)
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Hallo Sirius3,

danke für Deine Hinweise, die ich besser finde als mich hinzuweisen, dass ich selber lesen soll, das tue ich seit Tagen und manchmal hängt mal halt mal fest und braucht Hilfe.
Zu deinem Script.... auch da hatte sich ein TAB versteckt aber das ist gar nicht das Problem. Wenn ich es aufrufe erhalte ich was ich schon kenne, nämlich das len() nicht funktioniert. Bei mir noch nie und deshalb habe ich die "" Form gewählt. Nun erhalte ich bei Ausführung Deines Codes:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\wolkejo\Downloads\pyProjekt\sirius.py", line 6, in <module>
    if len(lines) <= 1:
       ^^^^^^^^^^
TypeError: object of type '_io.TextIOWrapper' has no len()
und auch hier komme ich mit Suchen nicht weiter. Kann noch mal jemand einen Blick draufwerfen? Fehlt ein import oder warum funktioniert bei mir keine Längenermittlung? Sind das am Ende gar keien "echten" Stringvariablen? Ja, ich stehe noch am Anfang..... Danke!
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Bevor du dir weiter versuchst selbst in den Fuß sch schießen: diese Lösung sieht auf den ersten Blick richtig aus.
Du wurdest ja schon darauf hingewiesen, dass das so einfach, wie du dir das vorstellst nicht funktioniert. Erst recht bei Multipart-Mails.

Was den Fehler in deinem Post angehst: Warum ist für dich die Anzahl der Zeilen wichtig?
__deets__
User
Beiträge: 14540
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Code ist Quatsch und nur notdürftig repariert von Sirius3. Da steht appen, statt append, len(lines) müsste wenn len(line.strip()) sein. Und dann ist das immer noch fragil, weil Emails verschieden formatiert sein können.

Aber schön, dass dir das besser gefällt, statt dich mal mit den Beispielen aus der Dokumentation auseinanderzusetzen. Nur weil ich die nicht hier rein kopiert habe 🙃
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Hallo zusammen,

erstmal möchte ich mich bedanken für den Antworten, möchte aber auch gerne etwas loswerden.
Ich bin keine 15, habe vor 30 Jahren schon programmiert und ich habe auch schon ein bekanntes deutsches Softwareforum sowohl als Moderator als auch Administrator betreut. Irgendwie werde ich den Eindruck nicht los, es wird einem Faulheit oder fehlender Wille unterstellt. Wenn man mir helfen will, und als soetwas verstehe ich ein Forum, dann bitte nicht mit "Hab ich gleich gesagt" oder zu Links die mir nicht helfen weil ich sie schon kenne und meine - mit meiner eben NICHT tiefen Kenntnis von Python, das sie zum Anfang noch komplexer sind. Aber ich bin ja nicht unbelehrbar, also nehme ich den Gedanken an und versuche es nochmal - mit mbox.
Ein Problem ist ja schon, dass ich gar nicht weiß ob der Ordner aus Thunderbirds "Lokale Ordner", nenne wir ihn TEST, die MBox Datei repräsentiert. Dort gibt es dies Datei TEST und TEST.msf. Die Mail liegen in TEST.

Wenn das nun also die mbox Datei wäre - was ich vermute, dann müsste in:

Code: Alles auswählen


mbox = mailbox.mbox(path_to_mailbox, factory=BytesParser(policy=default).parse)

der Pfad stehen, oder der Pfad und TEST, bei mir kommt aber

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\wolkejo\Downloads\pyProjekt\mboxtest.py", line 5, in <module>
    mbox = mailbox.mbox(TEST, factory=BytesParser(policy=default).parse)
                      			     ^^^^^^
NameError: name 'TEST' is not defined
gleiches mit Pfad.

Kann mir bitte jemand die Frage beantworten ob ich die mbox Datei korrekt lokalisert habe und nur den Pfad oder die Datei falsch deklariere oder ist mein Verständnis des Begriffes mbox falsch?

Gruß und Danke,
Wolke
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Fehlermeldung sagt, dass die Konstante TEST nirgends definiert ist. Du mußt also irgendwo dieser einen String zuweisen, der das entsprechende Verzeichnis enthält.
Benutzeravatar
__blackjack__
User
Beiträge: 13107
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@wolkejo: Dein Verständnis von Variablen(namen) ist falsch. Du schreibst da den Namen TEST und hast den nirgends definiert. Das hat überhaupt nichts mit `mbox` oder so zu tun, ein simples ``print(TEST)`` würde genau den gleichen Fehler bringen weil da so überhaupt nicht klar ist was `TEST` bedeuten soll. In welcher oder welchen Programmiersprachen hast Du denn schon programmiert? Das man Namen an irgendeinen Wert binden muss, bevor man sie/den Wert benutzen kann, ist ja keine Python-Besonderheit. Jetzt hätte ich fast gesagt das war schon bei meiner ersten Programmiersprache so, aber da sind unbekannte Namen bei der ersten Verwendung einfach so mit einem Defaultwert ”entstanden”. Selbst wenn das in Python passieren würde, wäre das hier ja nutzlos und die Auswahl an Werten die das annehmen könnte, wäre eigentlich auch auf `None` beschränkt.

Das es sich um das mbox-Format handelt, steht bei meinem Thunderbird irgendwo in den Servereinstellungen unter/hinter „Message Store Type: file per folder (mbox)“.

Der Pfad zur Datei muss mit angegeben werden, denn das Python-Modul weiss nix von Thunderbird oder anderen Mailprogrammen, das ist für das/die verschiedenen Formate, die ja von einer ganzen Reihe von Mailprogrammen verwendet wird. mbox ist deutlich älter als Thunderbird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Hallo Blackjack,

TEST war der Name der Datei, da im Skript - es ist ja nicht von mir - Pfad zur Datei - steht, habe ich das eben genau so verstanden. Und print(TEST) geht wenn TEST deklariert wurde genause wie print("TEST") wenn TEST ein string ist der gedruckt werden sollte. Das ist tatsächlich in den meisten Sprachen so. Mein Verständnis ist so falsch also nicht. TEST lag im gleichen Verzeichnis wir das Skript (die Datei weiß also nichts von TB ;-))und das hat bei allen open Befehlen auch so funktioniert. - Ich weiß, ist keine gute Idee aber das ist ja nur für mich.
Und um Deine Frage zu beantworten, Clipper, navision, bash, basic, autoit, batch... lange her
Benutzeravatar
__blackjack__
User
Beiträge: 13107
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@wolkejo: Ich bin jetzt ein bisschen verwirrt, weil Du ja selbst sagst, dass ``print(TEST)`` nur geht wenn `TEST` definiert ist. Das hat ja nix mit `print()` zu tun, auch ``int(TEST)`` oder ``some_module.some_function(TEST)`` und eben auch ``mailbox.mbox(TEST, …)`` geht nur wenn man vor der Verwendung definiert hat, wofür `TEST` stehen soll.

Das Verhalten das ``TEST`` ein Dateiname bzw. eine Zeichenkette ist, auch ohne das man das durch entsprechende Begrenzer anzeigt, ist zwar bei einigen Sprachen aus der Liste so, dafür muss man bei denen Variablennamen als solche Kennzeichnen, damit da keine Verwirrung aufkommt. Was man bei Python ja nicht machen muss oder gar kann. In Python ist ``TEST`` immer ein Variablenname (bzw. per Konvention ein Konstantenname, weil KOMPLETT_GROSS). In Bash kann das eine Zeichenkette sein, dafür kann man das dort aber nicht mit einem Variablennamen verwechseln, weil der ``$TEST`` lauten würde, wenn man den Wert dahinter referenzieren will.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

Ahh, das glaube ich Dir gerne und damit habe ich dann auch gleich wieder etwas gelernt.
Also nochmal und richtiger, TEST ist der Dateiname hier im Forum, den realen Namen habe ich nicht verraten und damit habe ich auch nicht TEST verwendet sondern den realen Namen. So konnte ich es dann nicht selber feststellen aber ich wollte Dich / Euch auch nicht verwirren. Sorry und vielen Dank!
wolkejo
User
Beiträge: 9
Registriert: Sonntag 22. Januar 2023, 14:27

BTW: kann man den Thread als "gelöst" setzen?
Antworten