Python: ¿Desinfectar una cadena para Unicode?

Posible duplicado:
Python UnicodeDecodeError – ¿Estoy entendiendo mal la encoding?

Tengo una cadena que estoy tratando de hacer segura para la función unicode() :

 >>> s = " foo “bar bar ” weasel" >>> s.encode('utf-8', 'ignore') Traceback (most recent call last): File "", line 1, in  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 "", line 1, in  unicode(s) UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128) 

Estoy sobre todo agitándome por aquí. ¿Qué debo hacer para eliminar los caracteres no seguros de la cadena?

Algo relacionado con esta pregunta , aunque no pude resolver mi problema a partir de ella.

Esto también falla:

 >>> s ' foo \x93bar bar \x94 weasel' >>> s.decode('utf-8') Traceback (most recent call last): File "", line 1, in  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 

Buena pregunta. Los problemas de encoding son difíciles. Vamos a empezar con “Tengo una cadena”. Las cadenas en Python 2 no son realmente “cadenas”, son matrices de bytes. Entonces, tu cadena, ¿de dónde viene y en qué encoding está? Su ejemplo muestra citas en el literal, y ni siquiera estoy seguro de cómo lo hizo. Intento pegarlo en un intérprete de Python, o escribirlo en OS X con Option- [, y no aparece.

Sin embargo, mirando su segundo ejemplo, tiene un carácter de hex 93. Eso no puede ser UTF-8 , porque en UTF-8, cualquier byte mayor que 127 es parte de una secuencia multibyte. Así que supongo que se supone que es Latin-1. El problema es que x93 no es un carácter en el conjunto de caracteres Latin-1. Existe este rango “no válido” en Latin-1 de x7f a x9f que se considera ilegal. Sin embargo, Microsoft vio ese rango sin usar y decidió poner “comillas rizadas” allí. Al hacerlo, crearon esta encoding similar llamada “windows-1252”, que es como Latin-1 con cosas en ese rango no válido.

Entonces, asummos que es windows-1252 . ¿Ahora que? String.decode convierte los bytes en Unicode, así que ese es el que quieres. Su segundo ejemplo estaba en el camino correcto, pero falló porque la cadena no era UTF-8. Tratar:

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

Eso es correcto, porque la cita de apertura es Unicode U + 201C. Ahora que tiene Unicode, puede serializarlo a bytes en cualquier encoding que elija (si necesita pasarlo a través del cable) o simplemente mantenerlo como Unicode si permanece dentro de Python. Si desea convertir a UTF-8, use la función de oposición, 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 serían dos bytes. Sin embargo, no puede codificar como ASCII o Latin-1, porque no tienen comillas.

EDITAR . Parece que su cadena está codificada de tal manera que (MARCA DE DOBLE CITA IZQUIERDA) se convierte en \x93 y (MARCA DE CITA DE DOBLE DERECHA) se convierte en \x94 . Hay una serie de páginas de códigos con dicha asignación, CP1250 es una de ellas, por lo que puede usar esto:

 s = s.decode('cp1250') 

Para todas las páginas de códigos que mapean a \x93 vea aquí (todas ellas también mapean a \x94 , que pueden verificarse aquí ).