ubuntuusers.de

Python-Webprogrammierung und Unicode

Status: Gelöst | Ubuntu-Version: Nicht spezifiziert
Antworten |

Goomba

Anmeldungsdatum:
11. Januar 2010

Beiträge: 203

Wohnort: Düsseldorf

Hallo allerseits,

ich programmiere grade ein CGI-Script mit Python und habe folgendes Problem:

Ich habe einen String, der zur Weiterverarbeitung in Unicode kodiert sein muss. Dies mache ich mit:

mein_text.unicode(mein_text, 'UTF-8')

Das funktioniert auch soweit.
Nun muss ich diese Zeichenkette aber auch wieder auf der Website ausgeben, was ich in folgender Form mache:

print "Content-Type: text/html"
    print
    print """\
    (...)
    %s
    (...)
    """ % mein_text     

An dieser Stelle bekomme ich allerdings folgende Fehlermeldung:

<type 'exceptions.UnicodeDecodeError'>: 'ascii' codec can't decode byte 0xc3 in position 1439: ordinal not in range(128) 

Trotz Google und viel rumprobieren, habe ich es nicht geschafft den Fehler zu beseitigen.
Würde mich sehr freuen, wenn ihr mir da weiterhelfen könnt...

Gruß,
Tobias

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2133

Wohnort: Gelsenkirchen

Ohne mehr Code kann man da wenig zu sagen. An sich sollte es nämlich klappen, denn das geht ja auch:

1
2
3
>>> spam = unicode('späm', 'utf-8')
>>> print "%s" % spam
späm

Übrigens sagt die Doku:

unicode(string [, encoding[, errors]]) → object

Create a new Unicode object from the given encoded string. encoding defaults to the current default string encoding. errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'.

Das heißt für das Beispiel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> unicode('späm', errors='ignore')
u'spm'
>>> unicode('späm', errors='replace')
u'sp\ufffd\ufffdm'
>>> print unicode('späm', errors='replace')
sp��m
>>> unicode('späm', errors='strict')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2: ordinal not in range(128)

Goomba

(Themenstarter)

Anmeldungsdatum:
11. Januar 2010

Beiträge: 203

Wohnort: Düsseldorf

Ich habe hier mal ein kurzes Skript, mit dem ich den Fehler reproduzieren konnte.
Man gibt etwas in das Formular ein bestätigt mit Enter. Das Skript ruft sich selbst wieder auf und zeigt an, was zuletzt eingegeben wurde...

#!/usr/bin/python
# -*- coding: utf-8 -*-

import cgi
import cgitb; cgitb.enable()  # for troubleshooting

form = cgi.FieldStorage()
message = form.getvalue("message", "(no message)")
message =  unicode(message, 'UTF-8')


print "Content-type: text/html"
print

print """
<html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sample CGI Script</title>
</head>
<body>
  <h3> Sample CGI Script </h3>

  <p>Previous message: %s</p>

  <form method="post" action="cgitest.py">
    <p>message: <input type="text" name="message"/></p>
  </form>

</body>
</html>
""" % message

Sobald man z.B. einen Umlaut eingibt, bekommt man folgende Fehlermeldung:

Traceback (most recent call last):
  File "/usr/lib/cgi-bin/cgitest.py", line 35, in <module>
    """ % message
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 282: ordinal not in range(128)

Hilft das irgendwie weiter?

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4681

Wohnort: Berlin

@Goomba: Dass Du wirklich mein_text.unicode(mein_text, 'UTF-8') schreibst, glaube ich nicht so ganz. Welchen Typ hat mein_text denn hier!?

Für die Ausgabe musst Du das unicode-Objekt halt wieder kodieren. HTTP kennt nur Bytes und keine Zeichen. Und wenn Du nicht explizit unter Angabe eines Zeichensatzes kodierst, dann versucht's Python als Rückfalloption mit ASCII. Und das geht daneben wenn etwas ausserhalb von ASCII kodiert werden soll.

@snafu1: Dass das mit dem print so bei Dir funktioniert liegt daran, dass der Interpretierer in diesem Fall auf einem Terminal ausgibt und ermitteln konnte welche Kodierung das erwartet. Diese Kodierung ist unter sys.stdout.encoding hinterlegt. Bei CGI geht das print auf eine Pipe oder direkt eine Socket-Verbindung und die haben keine Kodierung, also wird dort ASCII angenommen und das 'ä' löst die Ausnahme aus.

DasIch

Avatar von DasIch

Anmeldungsdatum:
2. November 2005

Beiträge: 1130

Das HTML welches du ausgibst ist in einem byte string, deswegen wird dein unicode string mit sys.getdefaultencoding() kodiert, welches in 2.x ASCII ist. Da Umlaute nicht durch ASCII repräsentiert werden können gibt es einen UnicodeEncodeError.

barcc

Avatar von barcc

Anmeldungsdatum:
13. Juli 2007

Beiträge: 696

Wohnort: Dortmund

Konvertierungen zwischen Unicode und Byte-Strings macht man am besten mit den encode und decode-Methoden:

1
2
3
4
5
6
7
>>> x = 'jörg'
>>> x_unicode = x.decode('utf8')
>>> x_bytes = x_unicode.encode('utf8')
>>> x_unicode, x_bytes
(u'j\xf6rg', 'j\xc3\xb6rg')
>>> print x_unicode, x_bytes
jörg jörg

Goomba

(Themenstarter)

Anmeldungsdatum:
11. Januar 2010

Beiträge: 203

Wohnort: Düsseldorf

Marc 'BlackJack' Rintsch schrieb:

@Goomba: Dass Du wirklich mein_text.unicode(mein_text, 'UTF-8') schreibst, glaube ich nicht so ganz. Welchen Typ hat mein_text denn hier!?

Sorry, das war ein Schreibfehler von mir. Ich meinte:

mein_text = unicode(mein_text, 'UTF-8')

mein_text kommt wie bei meinem Beispiel oben, aus dem cgi.FieldStorage().getvalue().

barcc schrieb:

Konvertierungen zwischen Unicode und Byte-Strings macht man am besten mit den encode und decode-Methoden:

1
2
3
4
5
6
7
>>> x = 'jörg'
>>> x_unicode = x.decode('utf8')
>>> x_bytes = x_unicode.encode('utf8')
>>> x_unicode, x_bytes
(u'j\xf6rg', 'j\xc3\xb6rg')
>>> print x_unicode, x_bytes
jörg jörg

Okay, so klappts. Jetzt hab ichs verstanden. ☺
Mein Beispiel aus dem ersten Beitrag sähe dann so aus:

mein_text = mein_text.decode('UTF-8')

print "Content-Type: text/html"
    print
    print """\
    (...)
    %s
    (...)
    """ % mein_text.encode('UTF-8')     

Das werd ich jetzt mal bei meinem richtigen Skript ausprobieren.

Ich hatte es schonmal so ähnlich versucht, allerdings hatte ich da decode und encode vertauscht. xD

Vielen, vielen Dank für eure kompetente und super schnelle Hilfe!

Antworten |