Seite 1 von 1
jythoncode mit unicode-argumenten aus python starten?
Verfasst: Freitag 6. November 2009, 05:28
von miss ineffektiv
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
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

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 川俣正" --
Verfasst: Freitag 6. November 2009, 06:32
von snafu
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()
Verfasst: Freitag 6. November 2009, 08:19
von ms4py
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.
Verfasst: Freitag 6. November 2009, 18:01
von miss ineffektiv
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?
Verfasst: Freitag 6. November 2009, 23:32
von miss ineffektiv
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
Verfasst: Samstag 7. November 2009, 09:52
von 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.
Verfasst: Samstag 7. November 2009, 17:56
von miss ineffektiv
Hi Blackjack,
ja, die erste Zeile beim Empfänger ist unnötig, stimmt, die ist noch ein Überbleibsel vom rumprobieren.

Und, klar wusste ich nicht was ich tue, sonst hätte ich ja nicht gefragt.
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?
Verfasst: Samstag 7. November 2009, 18:51
von 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?
Verfasst: Samstag 7. November 2009, 20:04
von miss ineffektiv
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...
Verfasst: Samstag 7. November 2009, 20:14
von miss ineffektiv
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.
Verfasst: Samstag 7. November 2009, 21:02
von 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.
Verfasst: Samstag 7. November 2009, 21:18
von miss ineffektiv
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.

Verfasst: Samstag 7. November 2009, 23:35
von 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.
Verfasst: Sonntag 8. November 2009, 21:10
von Hyperion
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
Verfasst: Dienstag 10. November 2009, 22:16
von miss ineffektiv
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...
Verfasst: Dienstag 10. November 2009, 23:06
von 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…
Verfasst: Dienstag 10. November 2009, 23:36
von miss ineffektiv
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.

Verfasst: Mittwoch 11. November 2009, 09:09
von mkesper
Wenn man einmal die Unicode-/Encoding-Mauer durchbrochen hat, ist es eigentlich einfach. Aber man muss es einmal richtig verstanden haben.
Verfasst: Mittwoch 11. November 2009, 09:25
von Hyperion
miss ineffektiv hat geschrieben:
@Hyperion, danke fürs posten. Super Erklärung, besser als das Howto in der Doku!
Bedanke Dich bei Leonidas

Verfasst: Mittwoch 11. November 2009, 12:27
von Leonidas
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
