coding - python utf 8 header
Bytes en una cadena de Python unicode (5)
En Python 2, las cadenas Unicode pueden contener tanto unicode como bytes:
a = u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /xd0/xb5/xd0/xba''
Entiendo que esto no es algo que deba escribirse en su propio código, pero esta es una cadena con la que tengo que lidiar.
Los bytes en la cadena de arriba son UTF-8 para ек
(Unicode /u0435/u043a
).
Mi objetivo es obtener una cadena Unicode que contenga todo en Unicode, es decir, Русский ек
( /u0420/u0443/u0441/u0441/u043a/u0438/u0439 /u0435/u043a
).
Codificándolo a rendimientos UTF-8
>>> a.encode(''utf-8'')
''/xd0/xa0/xd1/x83/xd1/x81/xd1/x81/xd0/xba/xd0/xb8/xd0/xb9 /xc3/x90/xc2/xb5/xc3/x90/xc2/xba''
Que luego descifrado de UTF-8 da la cadena inicial con bytes en ellos, lo cual no es bueno:
>>> a.encode(''utf-8'').decode(''utf-8'')
u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /xd0/xb5/xd0/xba''
Sin embargo, encontré una forma estrafalaria de resolver el problema:
>>> repr(a)
"u''//u0420//u0443//u0441//u0441//u043a//u0438//u0439 //xd0//xb5//xd0//xba''"
>>> eval(repr(a)[1:])
''//u0420//u0443//u0441//u0441//u043a//u0438//u0439 /xd0/xb5/xd0/xba''
>>> s = eval(repr(a)[1:]).decode(''utf8'')
>>> s
u''//u0420//u0443//u0441//u0441//u043a//u0438//u0439 /u0435/u043a''
# Almost there, the bytes are proper now but the former real-unicode characters
# are now escaped with /u''s; need to un-escape them.
>>> import re
>>> re.sub(u''////u([a-f//d]+)'', lambda x : unichr(int(x.group(1), 16)), s)
u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /u0435/u043a'' # Success!
Esto funciona bien, pero se ve muy raro debido a su uso de eval
, repr
, y luego regexing adicional de la representación de cadena Unicode. ¿Hay una manera más limpia?
En Python 2, las cadenas Unicode pueden contener tanto unicode como bytes:
No, es posible que no. Contienen caracteres Unicode.
Dentro de la cadena original, /xd0
no es un byte que es parte de una codificación UTF-8. Es el carácter Unicode con el código del punto 208. u''/xd0''
== u''/u00d0''
. Simplemente sucede que la repr
para cadenas Unicode en Python 2 prefiere representar caracteres con /x
escapes donde sea posible (es decir, puntos de código <256).
No hay forma de mirar la cadena y decir que se supone que el byte /xd0
es parte de algún carácter codificado en UTF-8, o si realmente representa el carácter Unicode por sí mismo.
Sin embargo, si supone que siempre puede interpretar esos valores como codificados, puede intentar escribir algo que analice cada carácter por turno (use ord
para convertir a un entero de punto de código), decodifique caracteres <256 como UTF-8 y pasa caracteres> = 256 como estaban.
(En respuesta a los comentarios anteriores): este código convierte todo lo que se parece a utf8 y deja otros puntos de código como están:
a = u''/u0420/u0443/u0441 utf:/xd0/xb5/xd0/xba bytes:bl/xe4/xe4''
def convert(s):
try:
return s.group(0).encode(''latin1'').decode(''utf8'')
except:
return s.group(0)
import re
a = re.sub(r''[/x80-/xFF]+'', convert, a)
print a.encode(''utf8'')
Resultado:
Рус utf:ек bytes:blää
Debería convertir unichr
s a chr
s, luego decodificarlos.
u''/xd0'' == u''/u00d0''
es True
$ python
>>> import re
>>> a = u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /xd0/xb5/xd0/xba''
>>> re.sub(r''[/000-/377]*'', lambda m:''''.join([chr(ord(i)) for i in m.group(0)]).decode(''utf8''), a)
u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /u0435/u043a''
-
r''[/000-/377]*''
coincidirá con unichrsu''[/u0000-/u00ff]*''
-
u''/xd0/xb5/xd0/xba'' == u''/u00d0/u00b5/u00d0/u00ba''
-
utf8
bytes codificados enutf8
como puntos de código Unicode ( este es el PROBLEMA ) - Resuelvo el problema pretendiendo esos unichars equivocados como los bytes correspondientes
- Busco todos estos unichars equivocados, y los convierto en caracteres, luego los decodifico.
Si estoy equivocado, por favor dígame.
El problema es que su cadena no está realmente codificada en una codificación específica . Su cadena de ejemplo:
a = u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /xd0/xb5/xd0/xba''
Está mezclando la representación interna de python de cadenas unicode con texto codificado en utf-8
. Si solo consideramos los caracteres ''especiales'':
>>> orig = u''/u0435/u043a''
>>> bytes = u''/xd0/xb5/xd0/xba''
>>> print orig
ек
>>> print bytes
ек
Pero dices, los bytes
están codificados en utf-8
:
>>> print bytes.encode(''utf-8'')
ек
>>> print bytes.encode(''utf-8'').decode(''utf-8'')
ек
¡Incorrecto! Pero que pasa:
>>> bytes = ''/xd0/xb5/xd0/xba''
>>> print bytes
ек
>>> print bytes.decode(''utf-8'')
ек
Hurra.
Asi que. Que significa esto para mi? Significa que estás (probablemente) resolviendo el problema equivocado. Lo que debería preguntarnos / tratando de averiguar es por qué sus cadenas están en esta forma, para empezar, y cómo evitarlo / arreglarlo antes de mezclarlo todo.
Ya has obtenido una respuesta, pero esta es una forma de descifrar secuencias Unicode similares a UTF-8 que es menos probable que decodifiquen secuencias latinas 1 Unicode por error. La función re.sub
:
- Coincide con caracteres Unicode <U + 0100 que se asemejan a secuencias UTF-8 válidas (ref: RFC 3629 ).
- Codifica la secuencia Unicode en su secuencia equivalente de latín-1.
- Decodifica la secuencia usando UTF-8 de nuevo en Unicode.
- Reemplaza la secuencia original de UTF-8 con el carácter Unicode coincidente.
Tenga en cuenta que esto aún podría coincidir con una secuencia Unicode si solo los caracteres correctos aparecen uno al lado del otro, pero es mucho menos probable.
import re
# your example
a = u''/u0420/u0443/u0441/u0441/u043a/u0438/u0439 /xd0/xb5/xd0/xba''
# printable Unicode characters < 256.
a += ''''.join(chr(n) for n in range(32,256)).decode(''latin1'')
# a few UTF-8 characters decoded as latin1.
a += ''''.join(unichr(n) for n in [2**7-1,2**7,2**11-1,2**11]).encode(''utf8'').decode(''latin1'')
# Some non-BMP characters
a += u''/U00010000/U0010FFFF''.encode(''utf8'').decode(''latin1'')
print repr(a)
# Unicode codepoint sequences that resemble UTF-8 sequences.
p = re.compile(ur''''''(?x)
/xF0[/x90-/xBF][/x80-/xBF]{2} | # Valid 4-byte sequences
[/xF1-/xF3][/x80-/xBF]{3} |
/xF4[/x80-/x8F][/x80-/xBF]{2} |
/xE0[/xA0-/xBF][/x80-/xBF] | # Valid 3-byte sequences
[/xE1-/xEC][/x80-/xBF]{2} |
/xED[/x80-/x9F][/x80-/xBF] |
[/xEE-/xEF][/x80-/xBF]{2} |
[/xC2-/xDF][/x80-/xBF] # Valid 2-byte sequences
'''''')
def replace(m):
return m.group(0).encode(''latin1'').decode(''utf8'')
print
print repr(p.sub(replace,a))
Salida
u ''/ u0420 / u0443 / u0441 / u0441 / u043a / u0438 / u0439 / xd0 / xb5 / xd0 / xba ! "# $% & /' () * +, -. / 0123456789 :; <=> ? @ ABCDEFGHIJKLMNOPQRSTUVWXYZ [ /] ^ _ `abcdefghijklmnopqrstuvwxyz {|} ~ / x7f / x80 / x81 / x82 / x83 / x84 / x85 / x86 / x87 / x88 / x89 / x8a / x8b / x8c / x8d / x8e / x8f / x90 / x91 / x92 / x93 / x94 / x95 / x96 / x97 / x98 / x99 / x9a / x9b / x9c / x9d / x9e / x9f / xa0 / xa1 / xa2 / xa3 / xa4 / xa5 / xa6 / xa7 / xa8 / xa9 / xaa / xab / xac / xad / xae / xaf / xb0 / xb1 / xb2 / xb3 / xb4 / xb5 / xb6 / xb7 / xb8 / xb9 / xba / xbb / xbc / xbd / xbe / xbf / xc0 / xc1 / xc2 / xc3 / xc4 / xc5 / xc6 / xc7 / xc8 / xc9 / xca / xcb / xcc / xcd / xce / xcf / xd0 / xd1 / xd2 / xd3 / xd4 / xd5 / xd6 / xd7 / xd8 / xd9 / xda / xdb / xdc / xdd / xde / xdf / xe0 / xe1 / xe2 / xe3 / xe4 / xe5 / xe6 / xe7 / xe8 / xe9 / xea / xeb / xec / xed / xee / xef / xf0 / xf1 / xf2 / xf3 / xf4 / xf5 / xf6 / xf7 / xf8 / xf9 / xfa / xfb / xfc / xfd / xfe / xff / x7f / xc2 / x80 / xdf / xbf / xe0 / xa0 / x80 / xf0 / x90 / x80 / x80 / xf4 / x8f / xbf / xbf ''
u ''/ u0420 / u0443 / u0441 / u0441 / u043a / u0438 / u0439 / u0435 / u043a ! "# $% & /' () * +, -. / 0123456789 :; <=> ? @ ABCDEFGHIJKLMNOPQRSTUVWXYZ [/] ^ _ `abcdefghijklmnopqrstuvwxyz {|} ~ / x7f / x80 / x81 / x82 / x83 / x84 / x85 / x86 / x87 / x88 / x89 / x8a / x8b / x8c / x8d / x8e / x8f / x90 / x91 / x92 / x93 / x94 / x95 / x96 / x97 / x98 / x99 / x9a / x9b / x9c / x9d / x9e / x9f / xa0 / xa1 / xa2 / xa3 / xa4 / xa5 / xa6 / xa7 / xa8 / xa9 / xaa / xab / xac / xad / xae / xaf / xb0 / xb1 / xb2 / xb3 / xb4 / xb5 / xb6 / xb7 / xb8 / xb9 / xba / xbb / xbc / xbd / xbe / xbf / xc0 / xc1 / xc2 / xc3 / xc4 / xc5 / xc6 / xc7 / xc8 / xc9 / xca / xcb / xcc / xd1 / xd2 / xd3 / xd4 / xd5 / xd6 / xd7 / xd8 / xd9 / xda / xdb / xdc / xdd / xde / xdf / xe0 / xe1 / xe2 / xe3 / xe4 / xe5 / xe6 / xe7 / xe8 / xe9 / xea / xeb / xec / xed / xee / xef / xf0 / xf1 / xf2 / xf3 / xf4 / xf5 / xf6 / xf7 / xf8 / xf9 / xfa / xfb / xfc / xfd / xfe / xff / x7f / x80 / u07ff / u0800 / U00010000 / U0010ffff ''