2010-07-11 10 views
14

Duplicar posible:
Python UnicodeDecodeError - Am I misunderstanding encode?Python: Sanitize una cadena para Unicode?

tengo una cadena que estoy tratando de hacer un uso seguro de la función unicode():

>>> s = " foo “bar bar ” weasel" 
>>> s.encode('utf-8', 'ignore') 

Traceback (most recent call last): 
    File "<pyshell#8>", line 1, in <module> 
    s.encode('utf-8', 'ignore') 
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128) 
>>> unicode(s) 

Traceback (most recent call last): 
    File "<pyshell#9>", line 1, in <module> 
    unicode(s) 
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128) 

Lo que más me agitándose aquí. ¿Qué debo hacer para eliminar los caracteres inseguros de la cadena?

Algo relacionado con este question, aunque yo era incapaz de resolver mi problema de ella.

Esto también falla:

>>> s 
' foo \x93bar bar \x94 weasel' 
>>> s.decode('utf-8') 

Traceback (most recent call last): 
    File "<pyshell#13>", line 1, in <module> 
    s.decode('utf-8') 
    File "C:\Python25\254\lib\encodings\utf_8.py", line 16, in decode 
    return codecs.utf_8_decode(input, errors, True) 
UnicodeDecodeError: 'utf8' codec can't decode byte 0x93 in position 5: unexpected code byte 
+0

Me pregunto por qué 'str' tiene una función' encode' en absoluto, y si el parámetro "codificación" especifica la codificación del resultado, o la codificación de la entrada. ¿Qué estás intentando hacer exactamente aquí? – Thanatos

+0

Compruebe [this] (http://stackoverflow.com/questions/368805/python-unicodedecodeerror-am-i-misunderstanding-encode/370199#370199) responda a una pregunta relacionada: "Python UnicodeDecodeError - ¿Estoy entendiendo mal la codificación? " – tzot

+0

Para aquellos que buscan una solución para desinfectar caracteres especiales Unicode en (X) HTML, intente' u'my unicode str'.encode ('ascii', 'xmlcharrefreplace') '. – toszter

Respuesta

4

EDITAR. Parece que la cadena se codifica de tal manera que (IZQUIERDA DOBLE COMILLA) se convierte en \x93 y (DERECHO comillas dobles) se convierte en \x94. Hay un número de páginas de códigos con dicha cartografía, CP1250 es uno de ellos, por lo que puede utilizar esto:

s = s.decode('cp1250') 

Para todas las páginas de códigos qué mapa a \x93 ver here (todos ellos mapa también a \x94 , que se puede verificar here).

+0

Esa llamada falla para mí (ver arriba) –

+0

@Rosarch OK, ahora veo la cadena original. He actualizado la respuesta (y mientras tanto @darkporter había encontrado la misma solución). – Bolo

+0

Buen enlace en las páginas de códigos. Sin embargo, parece que todas son variaciones en "ventanas". Si eres "occidental", yo diría que solo te quedas con 1252. – jpsimons

37

Buena pregunta. Los problemas de codificación son complicados. Comencemos con "Tengo una cadena". Las cadenas en Python 2 no son realmente "cadenas", son matrices de bytes. Entonces, ¿de dónde viene y en qué codificación está? Tu ejemplo muestra comillas en el literal, y ni siquiera estoy seguro de cómo lo hiciste. Intento pegarlo en un intérprete de Python, o escribirlo en OS X con Option- [, y no aparece.

En cuanto a su segundo ejemplo, sin embargo, que tienen un carácter de hexagonal 93. Eso no puede ser UTF-8, porque en UTF-8, cualquier byte superior a 127 es parte de una secuencia de varios bytes. Así que supongo que se supone que es Latin-1. El problema es que x93 no es un personaje del conjunto de caracteres Latin-1. Existe este rango "inválido" en Latin-1 de x7f a x9f que se considera ilegal. Sin embargo, Microsoft vio ese rango no utilizado y decidió poner "cotizaciones rizadas" allí. Al hacerlo crearon una codificación similar llamada "windows-1252", que es como Latin-1 con cosas en ese rango inválido.

Supongamos que es windows-1252. ¿Ahora que? String.decode convierte bytes en Unicode, por lo que es el que quieres. Su segundo ejemplo estaba en el camino correcto, pero falló porque la cadena no era UTF-8. Proveedores:

>>> uni = 'foo \x93bar bar\x94 weasel'.decode("windows-1252") 
u'foo \u201cbar bar\u201d weasel' 
>>> print uni 
foo “bar bar” weasel 
>>> type(uni) 
<type 'unicode'> 

Eso es correcto, ya que la apertura de la cita rizado es Unicode U + 201C. Ahora que tiene Unicode, puede serializarlo en bytes en cualquier codificación que elija (si necesita pasarlo por el cable) o simplemente mantenerlo como Unicode si permanece dentro de Python. Si desea convertir a UTF-8, use la función oponer, string.encode.

>>> uni.encode("utf-8") 
'foo \xe2\x80\x9cbar bar \xe2\x80\x9d weasel' 

Las comillas rizadas toman 3 bytes para codificar en UTF-8. Podrías usar UTF-16 y solo tendrían dos bytes. Sin embargo, no puede codificar como ASCII o Latin-1 porque no tienen comillas.

+1

+1, pero también debes mencionar que esta respuesta es específica de Python 2.x. En 3.x, el tipo 'str' se renombra a' bytes' y 'unicode' se renombra a' str'. Si bien al principio es confuso, este cambio hace que este tipo de cosas sea menos probable que suceda. –

+0

+1 para "comencemos con 'Tengo una cadena'" jaja –

+1

@Daniel No quiero ser incestuoso, pero acabo de votar su explicación de voto. Es cierto: lo anterior es específico de Python 2.x. – jpsimons

Cuestiones relacionadas