jythoncode mit unicode-argumenten aus python starten?

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
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Hallo,

ich habe ein kleines Jythonscript, das ich von meinem Pythonprogramm aus mit einem Unicode-Argument aufrufen will. Bis jetzt habe ich leider nur die Möglichkeit gefunden, das mit subprocess.Popen über die Kommandozeile laufen zu lassen (bin ganz frisch bei Jython; wenn jemand eine bessere Möglichkeit kennt, bitte her damit! wie gerne würd ichs einfach importieren :(). Ungefähr so sieht der Aufruf aus:

Code: Alles auswählen

process=subprocess.Popen(
    r"C:\jython2.5.1\jython.bat C:\jython2.5.1\icuTranslit.py 川俣正")
Das Argument ist ein Beispiel; normalerweise steht da ein vergleichbarer utf-8-String aus einer Schleife. Wenn ich mir mit

Code: Alles auswählen

print process.communicate()
sagen lassen will was da raus kommt, ist das ein Tupel (None, None). Ich dachte mir schon, dass es wie so oft am Encoding liegt, und habe das in meiner Kommandozeile getestet (mit "blá", weil ich natürlich die chinesischen Zeichen nicht tippen kann (wenn ich grad dabei bin- wie stelle ich das Encoding bei Windows ein? Kann man das überhaupt? Mache ich mit sys.setdefaultencoding() was kaputt?)). Hab daraufhin im Jython-Script (ach ja:

Code: Alles auswählen

def icuTransliterate(nonLatin):
    return Transliterator.getInstance(
    "Any-Latin; nfd; [:nonspacing mark:] remove; nfc").transliterate(
    String(nonLatin)).encode("utf-8")

if __name__=="__main__":
    print icuTransliterate(u"%s"%sys.argv[1].decode("utf-8"))
, würde für 川俣正 chuan yu zheng ausgeben) das input-Encoding nach cp437 geändert. Damit bekomme ich einen Output mit blá, aber aus dem Programm heraus immer noch nicht- das jythonfenster geht aber auch immer so schnell zu... Das Wort Traceback kann ich aber grade noch so erkennen :roll: Mein Versuch, den Popen-Aufrufstring zu decoden, war leider auch nix.

Ich bin gerade mit meinem Encoding-Latein am Ende. Weiß schon gar nicht mehr, was ich alles versucht habe... (Und nein, alles in Jython schreiben geht grad echt nicht. Verwende so viele externe Py-Libraries - Lxml, Orange, unverzichtbar - da ist ein Encodingproblem das kleinere Übel.) Windows-CL encoden können wär super. Puuh... Bitte etwas Hirninput.

müde Grüße & Bedankt

-- Ein Nachtrag: Die Codetags "verfälschen" meinen Popenaufruf gerade etwas. im Code sieht der derzeit so aus: r"C:\jython2.5.1\jython.bat C:\jython2.5.1\icuTranslit.py 川俣正" --
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Zunächst einmal sollten Argumente eigentlich als Liste übergeben werden, vielleicht möchtest du auch lieber os.path.join() zum Zusammensetzen verwenden. Und dann musst du Python aus sagen, dass du die Ausgabe umleiten/auslesen willst. Der gesamte Aufruf sähe dann in etwa so aus (ungetestet):

Code: Alles auswählen

import os
import subprocess

jython_path = os.path.join('C:', 'jython2.5.1')
cmd = [os.path.join(jython_path, 'jython.bat'), os.path.join(jython_path, 'icuTranslit.py'), '川俣正']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
proc.communicate()
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Das Windows-Kommandozeilenfenster verwendet CP850.
http://de.wikipedia.org/wiki/Codepage_850

AFAIK kann man daran auch nicht wirklich was ändern. Gibt zum Ändern der Codepage sogar einen Batch-Befehl, bei mir hat der allerdings nicht funktioniert. Aber vielleicht kommst du ja weiter, würde mich auch interessieren, ob sich da etwas machen lässt.
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Na, das ist zumindest mal nützlich stdin, stdout und stderr über subprocess umzuleiten, danke für den Tipp. Jetzt sehe ich auch mal den Traceback von der Jython-Mainfunktion:

Code: Alles auswählen

UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 0-2: invalid data
War schon klar dass da das Problem liegt, aber wie decode ich da stattdessen?
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Hm, schade, meine Frage vorhin ist wohl irgendwie untergegangen. Oder war zu einfach. Habs jedenfalls inzwischen gelöst, und falls irgendwann mal jemand ein ähnliches Problem hat - so funktionierts:

Code: Alles auswählen

cmd = [os.path.join(jython_path, 'jython.bat'), os.path.join(
        jython_path, 'icuTranslit.py'), repr('川俣正')]
proc = subprocess.Popen(
        cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print re.sub("^'|'$","",proc.communicate()[0].replace("\r\n",""))
Ich übergebe also repr(unicodeString), das sind dann Escapes, mit der die CL auch umgehen kann ('川俣正' -> '\xe5\xb7\x9d\xe4\xbf\xa3\xe6\xad\xa3').
Das "Empfängerprogramm" in Jython liest das dann so:

Code: Alles auswählen

    inp=u"%s"%sys.argv[1]
    print icuTransliterate(inp.decode('string_escape').decode("utf-8"))
Grüße&schönen Abend
BlackJack

@miss ineffektiv: Das sieht alles sehr merkwürdig und teilweise ziemlich unnötig aus, und nicht so als wenn Du verstanden hast, was Du da tust.

Unicode existiert immer nur innerhalb eines Programms, weil das eher eine abstrakte Idee ist. Wenn Daten ein Programm verlassen oder betreten, dann immer nur in Form von Bytes, weil das letztendlich der kleinste gemeinsame Nenner ist, den der Computer versteht. Das heisst man muss die Unicode-Zeichenketten vor dem externen Aufruf mit einer bestimmten Kodierung in Bytes umwandeln und beim aufgerufenen Programm die Bytes wieder in eine Unicode-Zeichenkette. Und das auf beiden seiten mit der selben Kodierung. Das war's eigentlich auch schon.

Dass Du letztlich nur herumprobiert hast, bis es klappte, sieht man zum Beispiel an der Zeile 1 bei dem Empfängercode und dass Du verschiedene Wege zum En- und Dekodieren auf den beiden Seiten nimmst. Und beim Sender noch nicht einmal Unicode, d.h. das Dekodieren von 'UTF-8' beim Empfänger hängt hier davon ab, dass der *Quelltext* des Aufrufers diese Kodierung verwendet.
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Hi Blackjack,

ja, die erste Zeile beim Empfänger ist unnötig, stimmt, die ist noch ein Überbleibsel vom rumprobieren. :oops: Und, klar wusste ich nicht was ich tue, sonst hätte ich ja nicht gefragt. 8)

Ich will mich aber auch mal verteidigen. Es ist ja nicht so, als hätte ich es ursprünglich anders gemacht als von dir beschrieben - die Zeichen beim Sender encoden, beim Empfänger decoden, beides in Utf-8. An welcher Stelle würdest du denn jetzt de- und encodieren?
BlackJack

@miss ineffektiv: Genau so würde ich das machen, das Argument als UTF-8 kodieren und beim aufgerufenen Programm halt wieder dekodieren. In Deinem allerersten, und im letzten Beispiel sind die Ausgangsdaten ja aber zum Beispiel schon keine Unicode-Objekte. Welche Kodierung haben die denn?
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Ich bin immer verwirrter. Wie weiss ich denn, welches Encoding ein String hat? Die kommen aus einem utf-8-Xml, und normalerweise kann ich sie auch in der Gegend rumschicken wie ich will. Wäre die repr-Funktion nicht anders, wenn sie nicht utf-8-decodiert wären? Oder unescaped? Ach ja: beim encoden bekomme ich einen

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)

Beim einfach-so übergeben und auf der anderen Seite decoden gibt es den

UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 0-2: invalid data

Was ja wieder bedeutet, dass es nicht utf-8 ist.
Ja, was ist es denn dann? Und wenn ich die Escapes decode, ist das ja auch erstmal nicht Utf-8, darum decode ich ja nochmal, um sie mit der ICU-Klasse weiterzuverarbeiten. Welches Encoding kommt denn bei decode("string_escape") überhaupt raus?? Ist das einfach ein Ascii-String, mit den Escapes? Wie wird das interpretiert?

Entschuldige meine Ignoranz, aber im tiefsten Inneren ist mir das alles ganz egal. Bin froh dass ich mit dem Workaround wenigstens bekomme was ich will...
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Mit "in der Gegend rumschicken" meine ich übrigens, dass ich nirgends im Code etwas am Encoding verändere, und nach den Modifikationen alles in ein utf-8-Xml zurückschreibe.
BlackJack

@miss ineffektiv: Welche Kodierung eine Zeichenkette hat musst Du *wissen*. Sonst musst Du raten, aber das ist nicht besonders sicher.

Und Du musst Dir auf jeden Fall den Unterschied zwischen Zeichenketten (`str`) und Unicode-Objekten (`unicode`) klarmachen. Ich habe nämlich den Verdacht, dass die Kodierungsfehlermeldung mit dem 'ascii' kommt, weil Du fälschlicherweise versuchst ein `str`-Objekt zu *enkodieren*. Das macht keinen Sinn, denn das enthält ja schon Bytes und keine "echten" Zeichen. `str` *dekodiert* man nach `unicode` und `unicode` *enkodiert* man nach `str`.

Wenn Du von "UTF-8-XML" sprichst, meinst Du wahrscheinlich XML als Serialisierungsformat. Denn XML als Dokumentenmodell ist über Unicode definiert, hat also keine Kodierung.
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Du verwirrst mich immer mehr!
Denn XML als Dokumentenmodell ist über Unicode definiert, hat also keine Kodierung.
Das musst du mir erklären.

Wenn ich meine Strings (ja, es sind welche) mit unicode() zu Unicode-Objekten machen will, bekomme ich aber den selben Fehler mit "ascii codec can't decode...". Wenn ich zuerst decode und dann encode (also beides beim Sender), kommt auf der anderen Seite der "invalid data" Fehler.

:?: :x
BlackJack

@miss ineffektiv: XML ist einmal das serialisierte Byteformat was man als XML-Dateien auf der Platte stehen hat, da muss es eine Kodierung haben. XML ist aber auch ein Dokumentenmodell. Und auf dieser Ebene ist es über Unicode spezifiziert. Ein Dokumentenmodell, das ein XML-Parser erstellt, arbeitet mit Unicode.

Wenn Du mit Strings arbeitest, dann arbeitest Du nicht mit XML-Dokumenten, sondern nur mit der serialisierten Form davon. Und wenn Du `unicode()` verwendest, ohne eine Kodierung anzugeben, fliegt Dir halt die entsprechende Ausnahme um die Ohren, wenn das `str`-Objekt etwas anderes als ASCII enthält. Wenn Du beim Sender sowohl dekodierst als auch enkodierst, dann kodierst Du ja wohl von einer Kodierung in eine andere und nicht jedesmal mit der selben Kodierung, das wäre dann ja eine Nulloperation. Du sagtest Deine XML-Dateien liegen als UTF-8 vor!? In was kodierst Du dann um und vor allem warum? Als was versuchst Du auf der anderen Seite zu dekodieren? Als was kommt es denn überhaupt bei Jython in `sys.argv` an? Java kennt ja keine Bytestrings wie Python's `str()` und versucht IMHO zu schlau zu sein, und nimmt einfach "die" Plattformkodierung zum dekodieren wenn man `java.lang.String()`\s irgendwie von aussen als Bytes bekommt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich poste hier mal das Dokument, das mir neben den wiki-Seiten am meisten Erleuchtung gebracht hat:
http://wiki.python-forum.de/User%20Grou ... folien.pdf
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Wollt mich doch auch mal wieder hier blicken lassen.

@BlackJack, argh, vergiss das mit dem "ascii-codec can't decode"-Fehler, der kam nur weil ich den Beispielstring in den Testcode reinkopiert hab. Mein Argument wird tatsächlich encodiert, aber dann eben in der repr-Repräsentation übergeben (repr(child.text.encode("utf-8"))) . Und nein, ich kodiere nirgends um, hab ich nie behauptet!

@Hyperion, danke fürs posten. Super Erklärung, besser als das Howto in der Doku! Zumindest das mit dem "Unicode ist ein Konzept und !=Utf-8" hab ich jetzt mal verstanden.

Leider erklärt mir das den "invalid data"-Fehler auch nicht. Und da mache ich doch lieber den Umweg mit der repr-Funktion und decode doppelt, als dass ich mich stundenlang wundere was an den Daten invalid sein soll...
BlackJack

@miss ineffektiv: Du hast nirgens behauptet, dass Du umkodierst, aber in den Beispielen hast Du auch immer `str`-Objekte gezeigt und da macht dekodieren gefolgt von enkodieren nur Sinn, wenn man etwas umkodiert. Wenn Du nicht sagst, was Du *wirklich* machst, dann kommt man halt zu solchen Schlüssen. ``repr(child.text.encode("utf-8"))`` sieht jedenfalls mehr danach aus, dass Du einen XML-Parser verwendest und damit `unicode`-Objekte als Ausgangspunkt hast.

Und nun hast Du etwas das funktioniert, aber Du weisst nicht warum. Wenn man damit zufrieden ist…
miss ineffektiv
User
Beiträge: 9
Registriert: Freitag 6. November 2009, 03:16

Mhm, bin ich. Wenn ich wüsste woher der invalid-data-Fehler kommt, dann wär ich auch nicht unglücklich.

Ja, wie gesagt, vergiss das mit dem String als Input; wollte die Exceptions sehen ohne im Code rumzumalen und hab den String in ein Codesnippet kopiert. Das war Unsinn zum Rekonstruieren, wie ich jetzt auch sehe, weil das eben ein String ist und der Parser mit Unicode arbeitet.

Dann mach mich doch schlauer und erkläre den Fehler, und warum es nur mit dem Workaround klappt. Ansonsten lasse ich meine Ansprüche nämlich unten. :twisted:
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Wenn man einmal die Unicode-/Encoding-Mauer durchbrochen hat, ist es eigentlich einfach. Aber man muss es einmal richtig verstanden haben.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

miss ineffektiv hat geschrieben: @Hyperion, danke fürs posten. Super Erklärung, besser als das Howto in der Doku!
Bedanke Dich bei Leonidas :-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hyperion hat geschrieben:
miss ineffektiv hat geschrieben: @Hyperion, danke fürs posten. Super Erklärung, besser als das Howto in der Doku!
Bedanke Dich bei Leonidas :-)
Ich habs auch nur lokalisiert und leicht angepasst :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten