Konverter

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
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hallo @all

Ich möchte ein Programm schreiben, sodass durch die Eingabe:
$ python convert_encoding.py in_dir/fileXY.txt latin-1 out_dir/fileXY.txt utf-8
ein Textfile von (zB) latin-1 nach (zB) utf-8 enkodiert werden.

Hierzu habe ich folgenden Code beisammen:

Code: Alles auswählen


import os

class Encoding():
    
    def convert_encoding(infilename, from_enc, to_enc, eol=os.linesep,
                     outfilename=""):
        
        def error_cleanup():
            if hasattr(infilename, 'close'):
                infilename.close()
            if hasattr(outfilename, 'close'):
                outfilename.close()
            if os.path.isfile(outfilename) and os.path.isfile(infilename):
                os.remove(outfilename)

        warn("Processing %s ... " % infilename, nl=False)
        if os.path.isfile(infilename):
            #
            # choose temp file
            tempfilename = infilename + "." + to_enc
            while os.path.isfile(tempfilename):
                tempfilename = tempfilename + "x"
                    #
                    # open original file (infile) and tempfile (outfile)
                    infile = outfile = None
                    try:
                        infile = codecs.open(infilename, "rb", from_enc)
                    except Exception, details:
                        warn("Error opening %s: %s" % (infilename, details));
                        error_cleanup()
                        return None
                    try:
                        outfile = codecs.open(tempfilename, "wb", to_enc)                        
                    except Exception, details:
                        warn("Error opening %s: %s" % (tempfilename, details))
                        error_cleanup()
                        return None
                    #
                    # go through infile, convert, and write to outfile
                    try:                            
                        for line in infile:
                            try:
                                line = line.replace("\r\n", "\n") # win
                                line = line.replace("\n", eol)
                                outfile.write(line)
                            except Exception, details:
                                raise Exception, "Error writing to %s: %s" \
                                                    % (tempfilename, details);
                    except Exception, details:
                        warn("Error in I/O: %s" % details)
                        error_cleanup()
                    else:
                        #
                        # Finish up: overwrite original file with tempfile
                        try:
                            infile.close()
                            outfile.close()
                            shutil.copystat(infilename, tempfilename)
                            overwrite = False
                            if outfilename == "":
                                outfilename = infilename
                                overwrite = True
                            rename_file(tempfilename, outfilename, overwrite)
                            warn("%s was successfully converted from %s to %s" \
                                 % (infilename, from_enc, to_enc))
                            warn("") # finish a block
                        except Exception, details:
                            warn("Renaming %s to %s FAILED. File was not converted: %s" \
                                % (tempfilename, infilename, details))
                            error_cleanup()
                            warn("") # finish a block
            

    else:
        warn("File '%s' does not exist\n" % file)
    

    
Meine erste Frage bezieht sich auf den Code: warn("Processing %s ...") auf der Zeile 22 wird als fehlerhaft angebenen. Allerdings weiss ich nicht, wieso und warum.
Zudem: Mit meinem Code kann ich das Programm nicht so aufrufen, wie das vorgegeben ist. Wie müsste ich ihn verändern, damit das möglich ist?

MfG und besten Dank, Marcel.
Zuletzt geändert von Anonymous am Samstag 17. März 2012, 01:27, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Oha, der Code sieht wirklich schlimm aus.

Eine unnötige Klasse, offensichtlich keine self-Attribut (was wahrscheinlich auch den Fehler erklärt) bei der einzigen Methode, das blinde Abfangen aller Exceptions und dazu am Ende noch ein else, welches in der Luft hängt. Hinzu kommt, dass die Funktion viel zu lang ist und aufgeteilt werden sollte. Hast du mal ein Tutorial vernünftig durchgearbeitet und dich mit den Grundlagen von OOP beschäftigt?

Außerdem wäre es auch sinnvoll, wenn du den Fehler verrätst.
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

@unnötige Klasse: Wieso meinst du? Reichen die Funktionen?

@self-Attribute: Du meinst also, dass ich bei den Funktionen jeweils ein "self" in die Klammern setzen soll?

@else: Das sollte eigentlich nicht in der Luft hängen, sondern die else-Bedingung sein, falls das "korrekte" file nicht gefunden werden kann.

@Länge: Ich habe mir auf der Zugfahrt noch überlegt, ob es nicht irgendwie einfacher und kürzer gehen sollte mit encode und decode (bzw. mit codecs) - was meinst du dazu?

@Tutorial: Ja, bin zu etwa 75% fertig.

@OOP: Das kenn ich halt vor allem aus Java. Und die Unterschiede zu Python sind halt doch nicht sooo minim ;)

Der Fehler ist ein IdentationError (obwohl das irgenwie nicht sein kann...oder nicht nur):
infile = outfile = None
^
IndentationError: unexpected indent
MfG, Marcel
BlackJack

@MarcelF6: Schau Dir den Quelltext in Deinem ersten Beitrag noch einmal genau an. Der Einrückungsfehler ist dort ziemlich offensichtlich. Zu welchem Block gehört die Zeile denn, in der die Ausnahme gemeldet wird?

Du hast ausserdem eine Menge Quelltext geschrieben ohne ihn ausprobiert zu haben. Denn der Aufruf der Funktion als Methode auf einem Exemplar von `Encoding` funktioniert so nicht. Oder Du rufst es auf der Klasse auf, was aber keinen Sinn macht, denn warum steckt es dann in einer Klasse wenn die gar nicht verwendet wird!?

Die Funktion `warn()` wird nicht nur für Warnungen aufgerufen. Damit ist der Name falsch.

Um eine temporäre Datei anzulegen oder zumindest einen Namen zu generieren gibt es in der Standardbibliothek das `tempfile`-Modul.

Statt ``return None`` wären reine ``return``-Anweisungen hier deutlicher, denn Du willst ja nicht wirklich einen Wert zurück geben, sondern an den Stellen einfach nur die Ausführung der Funktion abrechen.

Die `error_cleanup()`-Funktion scheint davon auszugehen, dass die Argumente `infilename` und `outfilename` auch bereits offene Dateiobjekte sein können. Übergibt man so etwas tatsächlich, dann funktioniert der Code in der Funktion damit aber gar nicht. Du solltest vielleicht erst einmal eine einfacherere Version der Funktion nur mit Dateinamen zum laufen bekommen, bevor Du sie um so etwas erweiterst.

Dadurch, dass Du alle Ausnahmen abfängst, kann ein Aufrufer gar nicht feststellen ob der Aufruf erfolgreich war oder nicht. Wenn die Eingabedatei zum Beispiel nicht mit der angegebenen Kodierung dekodiert werden kann, dann bekommt der Aufrufer das nicht mit. Schreib- und Kodierungsfehler bei der Ausgabe kann der Aufrufer nicht unterscheiden, weil er für *alles* was dabei passieren kann, nur eine ganz generische `Exception` bekommt. Auch wenn der Speicher knapp geworden ist, der Benutzer das Programm über Strg+C abgebrochen hat, oder wenn sich der Programmierer in dem ``try``/``except``-Block bei einem Namen vertippt hat.

Die Funktion vermischt Programmlogik mit Ausgaben für den Benutzer. Damit ist sie schlechter wiederverwendbar. Ausgaben in Programmlogik sollten höchstens in Form von „Logging” passieren, damit man beeinflussen kann ob und wie sie stattfinden.

In dem Quelltext sind ein paar überflüssige Semikolons. Und Zeilenfortsetzung mittels '\' am Ende der Zeile ist nur nötig, wenn es keine offenen Klammern mehr gibt, die noch nicht geschlossen wurden. Denn in dem Fall erkennt der Übersetzer, dass die „logische Zeile” noch nicht zuende sein kann.

Schreib mal eine einfachere Version der Funktion — tatsächlich als Funktion. Vielleicht sogar erst einmal eine die nur die Datei kopiert ohne sie umzukodieren und teste die ausgiebig. Und dann füge Stück für Stück die Funktionalität hinzu, immer mit Tests zwischendurch, ob das soweit funktioniert. So einen Riesenhaufen Quelltext zu schreiben und erst am Ende aus zu probieren führt nur zu Frust, wenn der an allen Ecken und Enden nicht richtig funktioniert.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Vielen herzlichen Dank für den Input!

Du hast Recht gehabt..ich habe mich wirklich etwas in diesem WirrWarr verhaddert und somit den gesamten Code gekippt und nochmals von vorne begonnen.
Nun bin ich so weit:

Code: Alles auswählen

def convert_encoding(self, infile, outfile):
    path = 'infile'
    path2 = 'outfile'
    
    f = open(path, 'rb')
    content = unicode(f.read(), 'latin-1')
    f.close()
    f = open(path2, 'wb')
    f.write(content.encode('utf-8'))
    f.close()
Hierbei habe ich mal angenommen, das input-file sei in latin-1 geschrieben und sollte nach utf-8 umkodiert werden.
Zum Testing habe ich eine Frage: Das ist ja nun nur eine Funktion - kann ich trotzdem einfach $ python [filename] in die Konsole eingeben, um die Funktion zu testen?
Auf alle Fälle erhielt ich keine Fehlermeldung, d.h. dass zumindest syntaktisch bis jetzt alles korrekt sein sollte.
Nun soll man aber etwas in der Art:
$ python convert_encoding.py in_dir/fileXY.txt latin-1 out_dir/fileXY.txt utf-8
eingeben können. Kann ich also die Kodierungen auch einfach in die Attributangaben der Funktion nehmen?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

MarcelF6 hat geschrieben:

Code: Alles auswählen

def convert_encoding(self, infile, outfile):
    path = 'infile'
    path2 = 'outfile'
    [...]
Du hast dich schon wieder verheddert. Was sollen denn diese Zuweisungen darstellen?
MarcelF6 hat geschrieben:$ python convert_encoding.py in_dir/fileXY.txt latin-1 out_dir/fileXY.txt utf-8
Du solltest dir im Programm als erstes mal sys.argv ausgeben lassen. Da siehst du dann, woher du die Parameter im Python-Programm bekommst. Wenn du das später mal ausführlich mit Schnörkeln und Schleifchen haben möchtest, dann schau dir argparse an.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Danke für den Hint mit sys.argv.
Es kommt langsam aber sicher gut :)

Code: Alles auswählen

import sys 

def liesdatei(dateiname, format, output, format1):
    '''Gib eine Datei auf der Standardausgabe aus.'''
    f = file(dateiname)
    g = file(output)
    while True:
        content = unicode(f.read(), format)
        if len(f.read()) == 0:
            break
        return g.write(content.encode(format1))
    f.close()
    g.close()
    
if len(sys.argv) < 2:
    print 'Es wurden keine Parameter uebergeben.'
    sys.exit()

else:
    for dateiname in sys.argv[1:]:
        liesdatei(dateiname)
Ich hab nur noch ein Problem mit der Eingabe. Mit einem Text, der eingelesen wird und dann ausgegeben, hat es super und wunderbar funktioniert. Nun möchte ich den Text ja nicht nur ausgeben, sondern einlesen und dann umkodieren - und dazu brauch ich halt 4 Argumente.
Daher meine Frage: Stimmt die Implementation so?
Falls ja: Wie muss ich dann die Eingabe in der shell tätigen?
Falls nein: Was ist noch wie zu ändern?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ich liste einfach mal die Punkte auf:
- "dateiname", "format", "output" und "format1" sind relativ nichtssagende Bezeichner. Da solltest du dir etwas besseres überlegen.
- Dateien solltest du mit dem with-Statement öffnene, dann werden diese in jedem Fall auch wieder geschlossen.
- Was hast du dir bei dem ``while True`` gedacht? ``file.read`` liest immer die gesamte Datei, da brauchst du keine Schleife. Im Zweiten Durchlauf wird ``f.read()`` immer einen leeren String liefern. Zu einem weiteren Durchlauf wird es natürlich gar nicht kommen, da du in der nächsten Zeile beendest.
- Entscheide dich für eine Sprache. Entwender deutsche Namen oder englische.
- ``if len(f.read()) == 0`` ist das gleiche wie ``if f.read()``. Der Test ist hier aber generell überflüssig.
- Was denkst du, was bei

Code: Alles auswählen

for dateiname in sys.argv[1:]:
liesdatei(dateiname)
passiert? Ich meine ganz offensichtlich bekommst du hier eine Fehlermeldung. Vielleicht solltest du mal versuchen diese zu verstehen. ``liesdatei`` erwartet vier Argumente. Du iterierst hier über alle gegeben Argumente und übergist nur eins an die Funktion.

Insgesamt macht dein Code den Eindruck, als wüsstest du nicht so wirklich was du da machst und rätst dir deinen Code irgendwie zusammen. Lese doch bitte die Dokumentation zu den von dir verwendeten Funtionen und lasse dir mittels print Zwischenergebnisse ausgeben.

Sebastian
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hallo,
danke für die Tipps und Hinweise.

Ich habe die Namen nun soweit wie nötig geändert.
With-Statements haben wir noch nicht gehabt. Man würde es aber so verwenden, oder:

with open("input.txt") as f:
content = unicode(f.read(), formatIn) [...]

Ja ok, die while-Abfrage ist hier unnötig. Trotzdem möchte ich sie vorerst mal noch drinnen lassen.

Ich habe nun den folgenden Code:

Code: Alles auswählen

import sys 

def liesdatei(input, formatIn, output, formatOut):
    '''Gib eine Datei auf der Standardausgabe aus.'''
    f = file(input)
    g = file(output)
    while True:
        content = unicode(f.read(), formatIn)
#        if len(f.read()) == 0:
#            break
        return g.write(content.encode(formatOut))
    f.close()
    g.close()
    
if len(sys.argv) < 2:
    print 'Es wurden keine Parameter uebergeben.'
    sys.exit()

else:
    for input in sys.argv[1:]:
        for formatIn in sys.argv[1:]:
            for output in sys.argv[1:]:
                for formatOut in sys.argv[1:]:
                    liesdatei(input, formatIn, output, formatOut)
Syntaktisch ist er korrekt. Aber ich sehe beim besten Willen nicht, wo der Fehler liegt, der die folgende Fehlermeldung erzeugt:
Traceback (most recent call last):
File "convert_encoding.py", line 30, in <module>
liesdatei(input, formatIn, output, formatOut)
File "convert_encoding.py", line 14, in liesdatei
content = unicode(f.read(), formatIn)
LookupError: unknown encoding: [...]/Tst.txt
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

MarcelF6 hat geschrieben:With-Statements haben wir noch nicht gehabt. Man würde es aber so verwenden, oder:

with open("input.txt") as f:
content = unicode(f.read(), formatIn) [...]
Ja, die Verwendung ist so richtig. Dass das Statement noch nicht besprochen wurde heißt doch nicht, dass du es noch nicht einsetzen darfst.
MarcelF6 hat geschrieben:Ja ok, die while-Abfrage ist hier unnötig. Trotzdem möchte ich sie vorerst mal noch drinnen lassen.
Und warum? Die macht einfach gar keinen Sinn. Durch das return und die fehlende Verwendung von with werden deine Dateien nun auch nicht mehr geschlossen.
MarcelF6 hat geschrieben:

Code: Alles auswählen

    for input in sys.argv[1:]:
        for formatIn in sys.argv[1:]:
            for output in sys.argv[1:]:
                for formatOut in sys.argv[1:]:
                    liesdatei(input, formatIn, output, formatOut)
Wie kommst du darauf, dass hier for-Schleifen das richtige sind? Ersetze die Zeile ``liesdatei(input, ...)`` mal mit ``print input, formatIn, output, formatOut``. Dann siehst du deinen Fehler sofort.
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Shit...du hast Recht. (also nicht dass ich das angezweifelt hätte..aber den print-Befehl hätte ich schon etwas früher ausführen sollen :P).
Also die Ausgabe ist bei input (z.B.) utf-16 - sprich es wird sozusagen formatOut ausgegeben.
..und auch bei formatIn, output, formatOut: immer utf-16.
Ich habe mich nun über eine halbe Stunde gefragt, wo das Problem liegt und vieles probiert. Aber leider mit wenig Erfolg. Ich denke aber, dass das Problem hier liegt:
return g.write(content.encode(formatOut))

Allerdings würde ich mich dann fragen, wieso und warum..?
(bzw. fall dies nicht stimmt: Wo liegt sonst der Fehler? :S)
problembär

MarcelF6 hat geschrieben:sodass durch die Eingabe:
$ python convert_encoding.py in_dir/fileXY.txt latin-1 out_dir/fileXY.txt utf-8
ein Textfile von (zB) latin-1 nach (zB) utf-8 enkodiert werden.
Ginge auch mit dem GNU-Kommandozeilentool "recode":

Code: Alles auswählen

recode ISO-8859-1..UTF-8 in_dir/fileXY.txt
Dann braucht man's nicht selber zu schreiben. ;)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Könntest du deine letzte Frage vielleicht noch einmal in vollständigen und nachvollziehbaren deutschen Sätzen stellen? Ich habe es jetzt mehrmals gelesen und habe keine Ahnung was du sagen möchtest. Mit welchen Parametern wird das Programm aufgerufen, was erwartest du für ein Ergebnis, was kommt tatsächlicher heraus und wie sieht dein aktueller Code aus?
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

@problembär: hehe..stimmt. Aber ich möchte es schon gerne selber machen :)

@EyDu:
Der Code sieht momentan so aus:

Code: Alles auswählen

import sys 

def liesdatei(input, formatIn, output, formatOut):
    '''Gib eine Datei auf der Standardausgabe aus.'''
    with open(input) as f:
        with open(output) as g:
            content = unicode(f.read(), formatIn)
            return g.write(content.encode(formatOut))
    
if len(sys.argv) < 2:
    print 'Es wurden keine Parameter uebergeben.'
    sys.exit()

else:
    for input in sys.argv[4:]:
        #liesdatei(input, formatIn, output, formatOut)
        print input
Wie gesagt: Ich vermute, dass das Problem bei der Zeile:
return g.write(content.encode(formatOut))
liegt - aber ich weiss nicht, was hier falsch sein sollte.
Oder liegt der Fehler ganz woanders?
Danke für die Hilfe :)
Zuletzt geändert von Anonymous am Samstag 17. März 2012, 22:39, insgesamt 1-mal geändert.
Grund: Code-Tags korrigiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Schau dir doch mal die Dokumentation zu oben an. Wenn du eine Datei zum Schreiben öffnen willst, dann musst du das explizit mit "w" angeben: ``with open(output, "w") as g``.
Das Leben ist wie ein Tennisball.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Jupi dupi :) Es funktioniert perfekt. Dieses "w" bzw. "r" ist tatsächlich sehr relevant ;)
Danke für die Hilfe und die Geduld! :)
Antworten