2012-08-01 7 views
10

Si exécuté ce code dans la console - il fonctionne bien (il est en russe), mais si elle fonctionne comme CGI sur le serveur Apache2 - il échoue: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode characters in position 8-9: ordinal not in range(128). Le code est:Pourquoi python-cgi échoue sur unicode?

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

import cgitb 
cgitb.enable() 

print "Content-Type: text/html;charset=utf-8" 
print 
s=u'Nikolja \u043d\u0435 \u0421\u0430\u0440\u043a\u043e\u0437\u0438!' 
print s#.encode('utf-8') 

Oui, la solution est décommenter .encode('utf-8'), mais je passe plus de temps pour comprendre pourquoi que je ne peux pas se produit et voir la réponse.

Répondre

10

Lors de l'exécution de la console Python peut détecter l'encodage de la console et convertit implicitement Unicode imprimé à la console à ce codage. Il peut toujours échouer si ce codage ne prend pas en charge les caractères que vous essayez d'imprimer. UTF-8 peut prendre en charge tous les caractères Unicode, mais pas d'autres codages de console courants comme cp437 sur Windows US. Lorsque stdout n'est pas une console, Python 2.X utilise ASCII par défaut lorsqu'il ne peut pas déterminer un encodage de console.

C'est pourquoi dans un serveur Web, vous devez être explicite et encoder vous-même votre sortie.

À titre d'exemple, essayez le script suivant à partir d'une console et de votre serveur Web:

import sys 
print sys.stdout.encoding 

A partir de la console, vous devriez obtenir un codage, mais à partir du serveur Web, vous devez obtenir None. Notez que Python 2.X utilise ascii mais Python 3.X utilise utf-8 lorsque le codage ne peut pas être déterminé.

Le problème peut également survenir sur une console lors de la redirection de la sortie. Ce script:

import sys 
print >>sys.stderr,sys.stdout.encoding 
print >>sys.stderr,sys.stderr.encoding 

renvoie le message suivant lorsqu'il est exécuté directement contre la redirection stdout:

C:\>test 
cp437 
cp437 

C:\>test >out.txt 
None 
cp437 

Remarque stderr n'a pas été affectée car elle n'a pas été redirigé.

La variable d'environnement PYTHONIOENCODING peut également être utilisée pour remplacer le codage stdout/stdin par défaut.

5

Essayez d'appliquer les utf-8 codecs sur stdin et stdout ...

#!/usr/bin/env python 
# -*- coding: UTF-8 -*- 

import cgitb 
import sys 
import codecs 

reload(sys) 
sys.setdefaultencoding('utf-8') 
sys.stdout = codecs.getwriter('utf-8')(sys.stdout) 
# If you need input too, read from char_stream as you would sys.stdin 
char_stream = codecs.getreader('utf-8')(sys.stdin) 

cgitb.enable() 

print "Content-Type: text/html;charset=utf-8" 
print 
s=u'Nikolja \u043d\u0435 \u0421\u0430\u0440\u043a\u043e\u0437\u0438!' 
print s.encode('utf-8') 
+0

Vous n'avez pas expliqué pourquoi cela se produit ... – scythargon

+2

La modification de l'encodage par défaut n'est pas recommandée. Il casse les bibliothèques qui dépendent de l'encodage par défaut. Ce n'est pas nécessaire dans tous les cas si vous remappez 'stdout' avec' codecs.getwriter'. –

+0

Je suis d'accord avec Mark, je n'ai pas besoin et je ne veux pas la ligne sys.setdefaultencoding ('utf-8'). – DrSkippy