Programm fertig, aber Schleife einbauen klappt nicht

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.
BlackJack

Das kann bei Rebecca's Quelltext, nach Berichtigung der beiden Fehler bei `open()`, nur passieren, wenn in der Datei immer der gleiche Dateiname steht.

Und da landen dann auch nicht alle Daten drin, sondern nur die des letzten Schleifendurchlaufs.

Allerdings werde ich das Gefühl nicht los, dass Du das Dateiformat nicht richtig beschrieben hast. Wenn nach der ersten Zeile Binärdaten kommen, wie sehen die denn aus? Das ist schon eigenartig, wenn Binärdaten durch Zeilenumbrüche in Zeilen aufgeteilt werden, weil in der Regel da ja *alles* drin vorkommen kann, inklusive das Byte, was einen Zeilenumbruch darstellt. Wie soll man dass dann von den anderen unterscheiden?

Wie sieht denn die Spezifikation des Formats genau aus? Eine Beispieldatei wäre super.
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Ich glaube den Fehler gefunden zu haben. Python meckert zwar immer noch bei Zeile 7, aber jetzt führt es alles korrekt aus, zumindest nach meinem ersten Überblick.
Ich lese jetzt von Spalte 1 bis 17 ein, also

Code: Alles auswählen

outfile = open(erstezeile[1:17], "wb")
Irgendwie war davor noch ein Binärzeichen, das hat Probleme gemacht.
Ein weiteres Problem was mir allerdings noch aufgefallen ist, weswegen die Sache nicht lief, ist dass einige Dateien 223 Zeilen bzw. 225 Zeilen hatten. Das ist dann natürlich ungünstig wenn ich immer davon ausgehen 224 Zeilen zu haben. Falls das nun keine Einzelfälle sind, habe ich ohnehin ein ziemlich großes Problem.

Nachdem ich diese beiden Problemquellen in meinem Testdatensatz beseitigt habe läuft es.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Herzlich willkommen beim Debuggen.
Mein erster Schritt wäre, mir mal mit

Code: Alles auswählen

print erstezeile[:17]
anzuschauen, was wirklich aus der Zeile ausgelesen wird.
EDIT: OK, hat sich erledigt...
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Also die ganze Sache scheint jetzt zu laufen. Meine Datenmenge sind leider so groß, dass ich nicht alles nachprüfen kann, aber es sieht gut aus.
Mir ist nur aufgefallen, anstatt von Zeile 1 bis 224 hätte ich von Zeile 1 Spalte 2 bis Zeile 225 Spalte 1 einlesen müssen. Das letzte Zeichen der Datei ist bei mir immer das erste, aber das scheint meinem Bild nichts auszumachen, von daher wohl egal.
An alle nochmal vielen Dank bei der Hilfe zur Lösung meines "kleinen" Programmierungsproblems.
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Ich muss den Thread nochmal hoch holen.
Ungünstigerweise habe ich noch Daten, die ein Umbau des Skripts erfordern. Anstatt alle 225 Zeilen, beginnen nun Bilder mitten in der Datei. Also beispielsweise erst in Zeile 227 Spalte 150...
Nun muss ich mich an einem String orientieren. Der String EOT (End of Transmission) zeigt immer das Ende vom Bild an und direkt in der anschließenden Spalte beginnt ein neues Bild.
Der Unicode von EOT schaut so aus

Code: Alles auswählen

 u"\u0004"
Als Skript hatte ich diesen verwendet.

Code: Alles auswählen

infile = open("C:/Test/Testfile", "rb")

while True:
    erstezeile = infile.readline() 
    if not erstezeile: break 

    outfile = open(erstezeile[1:17], "wb")  
    outfile.write(erstezeile) 
    for i in range (1, 224): 
        outfile.write(infile.readline()) 
    outfile.close()
infile.close()
Wollte nun also nur die Änderung dort einbauen. Zerbrech mir aber schon den ganzen Tag darüber den Kopf ohne wirklich weiter zu kommen. Hauptproblem ist, dass ich keine Methode finde, die optional die Angabe eines Strings zulässt. Bei read() und readline() kann man nur die bytes optional angeben.
Ich suche also etwas wie:
Lese Inhalt von Datei bis zu String "x" und speichere Inhalt in neuer Datei und als Dateiname nimm die ersten 17 Spalten des eingelesen Inhalts.
Ich hatte den Skript schon mal etwas umgeändert, aber da kam nichts gescheites bei raus, weil eben read und readline keine Strings akzeptieren :evil:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Eine einfache (und nicht unbedingt effektive) Lösung ist es, in ``read()`` immer nur ein Byte zu lesen und es mit EOT zu vergleichen. Ansonsten kann man es blockweise einlesen und nach EOT in den Blöcken suchen und diese ggf. bei Treffern an dieser Stelle zu teilen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Danke für deine Tipps.

Also eine Funktion um bis zu einem bestimmten String zu lesen gibt es scheinbar nicht?!
Ich habe nochmal einen Bekannten gefragt der sich etwas besser mit Python auskennt als ich, er wusste aber auch keine Patentlösung für die genannte Problematik.
Ich hab mir überlegt, dass es auch einfacher ist, erst die Datei zu erstellen und dann mit einem zweiten Skript die Dateien umzubenennen, weil sich dann der String des Dateinamens wieder immer an der gleichen Stelle befindet in den Dateien.
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Hallo,

ich bin mir nicht ganz sicher, ob ich auf dem Holzweg bin mit meiner Programmierung.
Ich habe eine Schleife erstellt, bei dem in einer Datei immer Zeilenweise in Spalte 1 der Zeile nach einem String

Code: Alles auswählen

u"\u0004"
gesucht werden soll. Günstigerweise kommt dieser String wenn immer nur in Spalte 1 vor. Wenn der String gefunden wird soll nun, der gesamte Zeileninhalt von diesem String bis 224 Zeilen davor in eine neue Datei eingelesen werden. Eigentlich bis zu einem bestimmten String 224 davor, aber ich komme erst einmal bei einem anderen Problem nicht weiter.
Wie kann ich ermitteln, in welcher Zeile der gesuchte String

Code: Alles auswählen

u"\u0004"
auftaucht. Die Zeilenangabe brauche ich, um zu wissen von wo bis wo eingelesen werden muss (siehe Code)

Code: Alles auswählen

infile = open("C:/Test/Testfile", "rb")

y = u"\u0004"

while True:
    erstes_zeichen = infile.readline ()[0:1]
    if any(str(x) == y for x in erstes_zeichen):
        # zeilenbestimmung von y      
        infile.close()
        infile = open("C:/Test/Testfile", "rb")
        inhalt = infile.readlines()[i:-224] # i = Zeile in der y gefunden wurde
        infile.close()
        outfile = open("C:/Test/Outfile", "wb")
        outfile.writelines(inhalt)
        outfile.close()
        break
Dann ist noch die Frage. Kommt man irgendwie wieder in die Schleife rein um das für die weiteren Zeilen erneut auszuführen. Die Suche darf nicht mehr von vorne beginnen, sondern muss beim zuletzt gefundenen String ansetzen.
BlackJack

Als erstes solltest Du aufhören hier Zeichenketten, also eigentlich Byteketten, mit Unicode zu mischen. Wenn Du nach dem Bytewert 4 suchst, ist der Weg über ein Unicode-Objekt ein Umweg, den man sich sparen kann. Dein `y` wäre dann also '\x04'.

Dann wäre eine formelle(re) Beschreibung des Formats nicht schlecht. Möglichst vorher, damit sich nicht ständig die Rahmenbedingungen für den Code ändern.

Und das müsste man dann etwas planvoller in Code umsetzen. Alleine die Zeilen 7 und 8 sehen nicht aus, als würdest Du verstehen was Du da tust.
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Also ich hatte mir u"\u0004" von unicode.org geholt.
Welche Rahmenbedingungen? Du meinst, wie meine Datei aufgebaut ist. Das Problem, ist dass die ziemlich "vermurckst" ist. Vorher hatte ich eine Datei mit Daten, bei denen ich alle 225 Zeilen eine Bilddatei bestehend aus Header und Binärcode in eine neue Datei gespeichert habe.
Nun besteht dieser feste Zeilenabstand aber nicht mehr, weil zwischen den Bildern irgendwelcher Datenmüll dazwischen liegt, den ich nicht brauche. Nun muss ich mich an einem String orientieren u"\u0004" , welche das Ende der Bilddatei angibt, von diesem Bildcode rückwärts 225 Zeilen bis zum Beginn des Bildheaders, gibt die Bilddatei, die ich wieder extra abspeichern muss.
In Zeile 7 gebe ich von jeder Zeile das erste Zeichen aus, da u"\u0004" wenn immer direkt am Anfang der Zeile steht. In Zeile 8 sage, ich wenn Pyhton den String findet, dann soll er Zeile 9 bis 16 ausführen.
Nur damit Pyhton nun weiß, welche Zeilen es in eine neue Datei kopieren soll, muss ich irgendwie raus kriegen in welcher Zeile nun der String u"\u0004" auftaucht, und genau danach suche ich schon den ganzen Tag. Das muss dann in Zeile 12 für das i eingesetzt werden, bzw. i soll dafür die Variable sein.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Vielleicht so:

Code: Alles auswählen

infile = open("C:/Test/Testfile", "rb")
lines = infile.readlines()
infile.close()

for i, line in enumerate(lines):
    if line.startswith('\x04'):
        inhalt = lines[i - 224: i + 1]
        # Hier Inhalt speichern o.ä.
Wobei mir das Dateiformat nicht klar ist. Handelt es sich da wirklich um Zeilen? Muss '\x04' mit dazugehören etc.?
MfG
HWK
BlackJack

@Chris82: u'\u0004' ist ein Unicode-Objekt, Du liest die Datei als Zeichenketten, also als Bytewerte. Da wird also ständig Das Unicode-Objekt für die Vergleiche in eine Zeichenkette umgewandelt, da kann man also gleich die entsprechende Zeichenkette verwenden.

Wo kommt denn der Datenmüll plötzlich her? Woraus besteht der? Warum kann man das Bild nicht am Header erkennen? Endet jeder Datenmüll garantiert mit einem Zeilenende-Zeichen?

In Zeile 7 wird nichts ausgegeben, sondern eine Zeile *eingelesen* und davon das erste Zeichen/Byte an den Namen `erstes_zeichen` gebunden.

Und in Zeile 8, nun ja Du hast schon irgendwie recht, aber *wie* machst Du das denn!? Ist Dir klar, dass das extrem umständlich ist!? Schau Dir doch mal die Methoden an, die Zeichenketten so bieten.

Die ganze Datei bei einem Fund dann noch einmal ein zu lesen ist ziemlicher Overkill und das Slicing funktioniert so auch nicht. Ein negativer Slice-EndIndex ist eine Adressierung relativ zum Ende der Liste, nicht relativ zum Startindex. Du speicherst Da also von Zeile `i` bis 224 Zeilen vor Dateiende, also eigentlich fast alles *nach* dem ersten Bild.
tanren
User
Beiträge: 1
Registriert: Montag 4. August 2008, 11:19

Du solltest dich mal von der Vorstellung von Zeilen und Spalten in einer Binär Datei verabschieden. Sowas gibt es einfach nicht.
Wenn in deiner Beschreibung des Formats also irgendwo die Wörter Zeile oder Spalte vorkommen ist es schon a priori falsch.
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

@ HWK

Ja, sowas hab ich gesucht. Mein Ansatz war wohl doch etwas zu arg kompliziert.

Ich hab den Skript noch etwas erweitert.
Einzig meine Schleife will nicht so wie ich will. Wobei ich das so verstehe, dass man eigentlich keine Schleife braucht, da gleich die ganze Datei eingelesen wird und dann immer nach '\x04 gesucht wird. Normal müsst er das bis zum Dateiende machen, aber er tut es nur bis Zeile 1867. Ich habe aber immer an die 2 Mio Zeilen. Deswegen hab ich nochmal extra eine for Schleife eingebaut, aber es ändert sich nichts.

Code: Alles auswählen

infile = open("C:/Test/2005/Testfile", "rb")

dateiende = 100000

lines = infile.readlines()
infile.close()


for i, line in enumerate(lines):
    if line.startswith('\x04'):
        for line in range (dateiende):
            inhalt = lines[i - 224: i + 1]
            Dateiname = lines[i - 224] 
            outfile = open(Dateiname[0:17], "wb") # Dateiname
            outfile.writelines(inhalt)
        outfile.close()
@BlackJack

Warum da Datenmüll drin ist, weiß ich auch nicht. Auf jeden Fall geht ein Bild immer vom Header bis EOT, dann kommt ein paar Zeilen Datenmüll, jedoch immer unterschiedlich groß und dann gehts wieder mit dem Header los.

@tanren

vielleicht hast du recht und ich verstehe hier etwas falsch, fakt ist aber, lasse ich mir die Datei mit einem Editor wie Notepad++ anzeigen, so habe ich einzelne Bilddateien die eine Länge von 225 Zeilen haben (Header bis EOT).
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Die For-Schleife ab Zeile 12 ist völlig unverständlich. Was soll die denn bewirken? Zumal Du hier dieselbe Variable (line) wie in der übergeordneten For-Schleife verwendest. Im Moment speichert sie dateiende-mal dieselben Daten, was Du ja auch gemerkt hast. Zeile 17 ist falsch eingerückt. Vielleicht solltest Du einmal Deine Daten in einer kleinen Beispieldatei posten.

MfG
HWK
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

Ich hab den Fehler gefunden.
An einigen Stellen kommt einige Zeilen nach dem EOT ein weiteres EOT vor, also im Datenmüll, weil dazwischen kein neuer Header mehr ist.
An dieser Stelle bricht das Skript immer ab. Mir war das bisher nur noch nicht aufgefallen, weil das bei ca. 9000 Bildern die sich in einer Datei befinden, nur bei ein paar Prozent vorkommt.
Das sollte eigentlich nicht so sein und ist natürlich extrem ungünstig!
Muss ich mal überlegen, was ich da machen kann.
Chris82
User
Beiträge: 21
Registriert: Sonntag 13. Juli 2008, 17:06

So jetzt hab ich's endlich.
Gestern hatte ich meine Daten mit einem Notepad++ Makro bearbeitet und den Header immer an den Zeilenanfang gesetzt.
Dadurch muss ich EOT nicht mehr zwangsläufig verwenden.
Der Header beginnt immer mit "PF" und dann folgt ein Datum. "PF" kommt zwar ansonsten auch in der Datei vor, aber niemals am Zeilenanfang. Das hab ich eben nochmal schnell überflogen und dann getestet und es wurden alle Bilder mit richtigem Dateinamen neu abgespeichert.

Code: Alles auswählen

infile = open("C:/Test/testfile", "rb")                

while True:
    erstezeile = infile.readline()
    if not erstezeile: break #dateiende erreicht

    if erstezeile.startswith('PF'):
        outfile = open(erstezeile[0:17], "wb")
        outfile.write(erstezeile)
        for i in range(1, 225):
            outfile.write(infile.readline())
        outfile.close()

infile.close()
Vielen Dank für die Tipps und Hilfe. Hoffe mein Datenformat ändert sich jetzt nicht mehr.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

'More pythonic' wäre:

Code: Alles auswählen

for erstezeile in infile:
MfG
HWK
BlackJack

@HWK: Da muss man in diesem Fall aber aufpassen. Wenn man direkt über `infile` iteriert wird immer ein Stück im Voraus gelesen, dass heisst, das ``infile.readline()`` innerhalb der Schleife liest dann die falschen Zeilen.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Woran liegt das? Ich dachte: 'for line in filestream' liest immer genau eine Zeile. readline() liest dann die nächste Zeile und beim nächsten Schleifendurchlauf folgt die darauffolgende Zeile. Ich habe das auch schon in Scripts ohne Probleme verwendet. Als Beispiel zum Ausprobieren:

Code: Alles auswählen

import random, StringIO
f = StringIO.StringIO('\n'.join(str(x + 1) for x in range(20)))
for line in f:
    print line,
    for _ in range(random.randrange(4)):
        temp = f.readline()
        if temp:
            print '#', temp,
Bei allen Testläufen war bei mir die Kontinuität erhalten.
MfG
HWK
Antworten