with tutorial the para latest framework español desde cero applications python sqlite unicode utf-8

python - tutorial - the django project



Datos SQLite, python, unicode y no utf (5)

Todavía no sé si hay una forma de convertir correctamente ''ó'' de latin-1 a utf-8 y no destruirlo

repr () y unicodedata.name () son tus amigos cuando se trata de depurar tales problemas:

>>> oacute_latin1 = "/xF3" >>> oacute_unicode = oacute_latin1.decode(''latin1'') >>> oacute_utf8 = oacute_unicode.encode(''utf8'') >>> print repr(oacute_latin1) ''/xf3'' >>> print repr(oacute_unicode) u''/xf3'' >>> import unicodedata >>> unicodedata.name(oacute_unicode) ''LATIN SMALL LETTER O WITH ACUTE'' >>> print repr(oacute_utf8) ''/xc3/xb3'' >>>

Si envía oacute_utf8 a un terminal configurado para latin1, obtendrá A-tilde seguido de superscript-3.

Cambié a cadenas Unicode.

¿A qué estás llamando cadenas Unicode? UTF-16?

¿Lo que da? Después de leer esto, describiendo exactamente la misma situación en la que estoy, parece que el consejo es ignorar los otros consejos y usar cadenas de bytes de 8 bits después de todo.

No puedo imaginar cómo te parece a ti. La historia que se transmitía era que los objetos Unicode en Python y la codificación UTF-8 en la base de datos eran el camino a seguir. Sin embargo, Martin respondió la pregunta original, dando un método ("fábrica de textos") para que el OP pueda usar latin1. ¡Esto NO constituía una recomendación!

Actualice en respuesta a estas preguntas adicionales planteadas en un comentario:

No entendí que los caracteres Unicode todavía contenían una codificación implícita. ¿Estoy diciendo eso verdad?

No. Una codificación es un mapeo entre Unicode y algo más, y viceversa. Un carácter Unicode no tiene una codificación, implícita o de otro tipo.

Me parece que unicode ("/ xF3") y "/ xF3" .decode ("latin1") son los mismos cuando se evalúan con repr ().

¿Que qué? A mí no me lo parece

>>> unicode("/xF3") Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: ''ascii'' codec can''t decode byte 0xf3 in position 0: ordinal not in range(128) >>> "/xF3".decode(''latin1'') u''/xf3'' >>>

Quizás quisiste decir: u''/xf3'' == ''/xF3''.decode(''latin1'') ... esto es ciertamente cierto.

También es cierto que unicode(str_object, encoding) hace lo mismo que str_object.decode(encoding) ... incluyendo la explosión cuando se proporciona una codificación inapropiada.

¿Es eso una feliz circunstancia?

Que los primeros 256 caracteres en Unicode sean los mismos, código para el código, ya que los 256 caracteres en latin1 son una buena idea. Debido a que los 256 caracteres latin1 posibles se asignan a Unicode, significa que CUALQUIER byte de 8 bits, CUALQUIER objeto de Python str se puede decodificar en Unicode sin que se genere una excepción. Esto es como debería ser.

Sin embargo, existen ciertas personas que confunden dos conceptos bastante separados: "mi script se ejecuta hasta su finalización sin que se generen excepciones" y "mi script está libre de errores". Para ellos, latin1 es "una trampa y un engaño".

En otras palabras, si tiene un archivo que está realmente codificado en cp1252 o gbk o koi8-u o lo que sea y lo decodifica con latin1, el Unicode resultante será una basura y Python (o cualquier otro idioma) no marcará un error - - No tiene forma de saber que has cometido una tontería.

o ¿Unicode ("str") va a devolver siempre la descodificación correcta?

Solo así, con la codificación predeterminada como ascii, devolverá el unicode correcto si el archivo está realmente codificado en ASCII. De lo contrario, explotará.

De forma similar, si especifica la codificación correcta, o una que sea un superconjunto de la codificación correcta, obtendrá el resultado correcto. De lo contrario, obtendrás un galimatías o una excepción.

En resumen, la respuesta es no.

Si no, cuando recibo un str python que tiene cualquier posible conjunto de caracteres en él, ¿cómo sé cómo decodificarlo?

Si el objeto str es un documento XML válido, se especificará por adelantado. El valor predeterminado es UTF-8. Si se trata de una página web construida correctamente, debe especificarse por adelantado (busque "juego de caracteres"). Desafortunadamente, muchos escritores de páginas web mienten a través de sus dientes (ISO-8859-1 también conocido como latin1, debe ser Windows-1252 también conocido como cp1252; no desperdicie recursos tratando de decodificar gb2312, use gbk en su lugar). Puede obtener pistas de la nacionalidad / idioma del sitio web.

UTF-8 siempre vale la pena intentarlo. Si los datos son ascii, funcionarán bien, porque ascii es un subconjunto de utf8. Una cadena de texto que se ha escrito utilizando caracteres que no son ASCII y que se ha codificado en una codificación distinta de utf8 casi con seguridad fallará con una excepción si intenta decodificarla como utf8.

Todas las heurísticas anteriores y más y muchas estadísticas están encapsuladas en chardet , un módulo para adivinar la codificación de archivos arbitrarios. Por lo general, funciona bien. Sin embargo, no puede hacer que el software sea a prueba de idiotas. Por ejemplo, si concatenas archivos de datos escritos algunos con codificación A y algunos con codificación B, y alimenta el resultado a chardet, es probable que la respuesta sea la codificación C con un nivel reducido de confianza, por ejemplo, 0.8. Siempre revise la parte de confianza de la respuesta .

Si todo lo demás falla:

(1) Intente preguntar aquí, con una pequeña muestra del frente de sus datos ... print repr(your_data[:400]) ... y cualquier información colateral sobre su procedencia que tenga.

(2) La reciente investigación rusa sobre técnicas para recuperar contraseñas olvidadas parece ser bastante aplicable para deducir codificaciones desconocidas.

Actualización 2 Por cierto, ¿no es hora de que abra otra pregunta? -)

Una cosa más: aparentemente hay caracteres que Windows usa como Unicode para ciertos caracteres que no son el Unicode correcto para ese personaje, por lo que puede tener que asignar esos caracteres a los correctos si desea usarlos en otros programas que son esperando esos personajes en el lugar correcto.

No es Windows quien lo está haciendo; es un grupo de desarrolladores de aplicaciones locos. Es posible que, comprensiblemente, no se haya parafraseado, sino que se haya citado el párrafo inicial del artículo de Efbot al que se refiere:

Algunas aplicaciones agregan caracteres CP1252 (Windows, Europa Occidental) a documentos marcados como ISO 8859-1 (Latín 1) u otras codificaciones. Estos caracteres no son caracteres ISO-8859-1 válidos y pueden causar todo tipo de problemas en el procesamiento y las aplicaciones de visualización.

Fondo:

El rango U + 0000 a U + 001F inclusive se designa en Unicode como "Caracteres de control C0". También existen en ASCII y latin1, con los mismos significados. Incluyen cosas tan familiares como el retorno de carro, el avance de línea, el timbre, el retroceso, la pestaña y otros que rara vez se usan.

El rango U + 0080 a U + 009F inclusive se designa en Unicode como "Caracteres de control C1". También existen en latin1, e incluyen 32 caracteres que nadie fuera de unicode.org puede imaginar para un posible uso.

En consecuencia, si ejecuta un recuento de frecuencia de caracteres en sus datos Unicode o Latin1 y encuentra caracteres en ese rango, sus datos están corruptos. No hay una solución universal; depende de cómo se corrompió. Los caracteres pueden tener el mismo significado que los caracteres cp1252 en las mismas posiciones, por lo que la solución del effbot funcionará. En otro caso que he estado viendo recientemente, los caracteres dudosos parecen haber sido causados ​​por concatenar archivos de texto codificados en UTF-8 y otra codificación que debía deducirse en función de las frecuencias de letras en el lenguaje (humano) en el que se encontraban los archivos. escrito en.

Empecé tratando de almacenar cadenas en sqlite usando python, y recibí el mensaje:

sqlite3.ProgrammingError: no debe usar cadenas de bytes de 8 bits a menos que use text_factory que pueda interpretar cadenas de bytes de 8 bits (como text_factory = str). Es muy recomendable que simplemente cambie su aplicación a cadenas Unicode.

Ok, cambié a cadenas Unicode. Entonces comencé a recibir el mensaje:

sqlite3.OperationalError: no se pudo decodificar a la columna UTF-8 ''tag_artist'' con el texto ''Sigur Rós''

al tratar de recuperar datos de la base de datos. Más investigación y comencé a codificarlo en utf8, pero luego ''Sigur Rós'' empieza a parecerse a ''Sigur Rós''

nota: mi consola estaba configurada para mostrarse en ''latin_1'' como señaló @John Machin.

¿Lo que da? Después de leer this , describiendo exactamente la misma situación en la que estoy, parece que el consejo es ignorar los otros consejos y usar cadenas de bytes de 8 bits después de todo.

No sabía mucho sobre unicode y utf antes de comenzar este proceso. He aprendido bastante en las últimas dos horas, pero sigo sin saber si hay una forma de convertir correctamente ''ó'' de latin-1 a utf-8 y no destruirlo. Si no lo hay, ¿por qué sqlite ''recomienda mucho'' que cambie mi aplicación a cadenas Unicode?

Voy a actualizar esta pregunta con un resumen y un código de ejemplo de todo lo que he aprendido en las últimas 24 horas para que alguien en mi lugar pueda tener una guía fácil (er). Si la información que publico es incorrecta o engañosa de alguna manera, dígame y la actualizaré, o uno de ustedes puede actualizar.

Resumen de respuestas

Permítanme primero exponer el objetivo tal como lo entiendo. El objetivo al procesar varias codificaciones, si está tratando de convertirlas, es entender cuál es su codificación fuente, luego convertirla a unicode usando esa codificación fuente y luego convertirla a su codificación deseada. Unicode es una base y las codificaciones son asignaciones de subconjuntos de esa base. utf_8 tiene espacio para cada personaje en Unicode, pero como no están en el mismo lugar que, por ejemplo, latin_1, una cadena codificada en utf_8 y enviada a una consola latin_1 no se verá de la forma esperada. En python, el proceso de llegar a Unicode y a otra codificación se ve así:

str.decode(''source_encoding'').encode(''desired_encoding'')

o si el str ya está en unicode

str.encode(''desired_encoding'')

Para sqlite, en realidad no quería volver a codificarlo, quería decodificarlo y dejarlo en formato unicode. Aquí hay cuatro cosas que puede necesitar tener en cuenta a medida que trata de trabajar con codificaciones y unicode en python.

  1. La codificación de la cadena con la que desea trabajar y la codificación a la que desea acceder.
  2. La codificación del sistema.
  3. La codificación de la consola.
  4. La codificación del archivo fuente

Elaboración:

(1) Cuando lee una cadena de una fuente, debe tener alguna codificación, como latin_1 o utf_8. En mi caso, recibo cadenas de nombres de archivos, así que, desafortunadamente, podría obtener cualquier tipo de codificación. Windows XP usa UCS-2 (un sistema Unicode) como su tipo de cadena nativa, lo que me parece una trampa. Afortunadamente para mí, los caracteres en la mayoría de los nombres de archivo no van a estar compuestos por más de un tipo de codificación fuente, y creo que todos los míos fueron completamente latin_1, completamente utf_8, o simplemente ascii (que es un subconjunto de ambos) aquellos). Así que solo los leí y los decodifiqué como si todavía estuvieran en latin_1 o utf_8. Sin embargo, es posible que pueda tener latin_1 y utf_8 y cualquier otro carácter mezclado en un nombre de archivo en Windows. A veces esos personajes pueden aparecer como cuadros, otras veces se ven destrozados y otras veces se ven correctos (caracteres acentuados y otras cosas). Continuando.

(2) Python tiene una codificación de sistema predeterminada que se establece cuando Python se inicia y no se puede cambiar durante el tiempo de ejecución. Mira here para más detalles. Sumario sucio ... bueno, aquí está el archivo que agregué:

/# sitecustomize.py /# this file can be anywhere in your Python path, /# but it usually goes in ${pythondir}/lib/site-packages/ import sys sys.setdefaultencoding(''utf_8'')

Esta codificación del sistema es la que se utiliza cuando se utiliza la función Unicode ("str") sin ningún otro parámetro de codificación. Para decirlo de otra manera, Python intenta decodificar "str" ​​a unicode en función de la codificación predeterminada del sistema.

(3) Si está utilizando IDLE o la línea de comando python, creo que su consola se mostrará de acuerdo con la codificación predeterminada del sistema. Estoy usando pydev con eclipse por alguna razón, así que tuve que entrar en la configuración de mi proyecto, editar las propiedades de configuración de inicio de mi script de prueba, ir a la pestaña Común y cambiar la consola de latin-1 a utf-8 para que Pude confirmar visualmente que lo que estaba haciendo estaba funcionando.

(4) Si desea tener algunas cadenas de prueba, por ejemplo

test_str = "ó"

en su código fuente, entonces tendrá que decirle a python qué tipo de codificación está utilizando en ese archivo. (FYI: cuando escribí mal una codificación tuve que ctrl-Z porque mi archivo se volvió ilegible.) Esto se logra fácilmente poniendo una línea como en la parte superior de su archivo de código fuente:

# -*- coding: utf_8 -*-

Si no tiene esta información, python intenta analizar el código como ascii de manera predeterminada, y así:

SyntaxError: Non-ASCII character ''/xf3'' in file _redacted_ on line 81, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

Una vez que su programa esté funcionando correctamente, o, si no está utilizando la consola de Python o cualquier otra consola para mirar el resultado, entonces probablemente solo le importe el # 1 en la lista. Los valores predeterminados del sistema y la codificación de la consola no son tan importantes a menos que necesite ver la salida y / o esté utilizando la función incorporada Unicode () (sin ningún parámetro de codificación) en lugar de la función string.decode (). Escribí una función de demostración que pegaré en el fondo de este lío gigantesco que espero que demuestre correctamente los elementos de mi lista. Aquí está algo de la salida cuando ejecuto el caracter ''ó'' a través de la función de demostración, que muestra cómo varios métodos reaccionan al carácter como entrada. La codificación de mi sistema y la salida de la consola están configurados en utf_8 para esta ejecución:

''�'' = original char <type ''str''> repr(char)=''/xf3'' ''?'' = unicode(char) ERROR: ''utf8'' codec can''t decode byte 0xf3 in position 0: unexpected end of data ''ó'' = char.decode(''latin_1'') <type ''unicode''> repr(char.decode(''latin_1''))=u''/xf3'' ''?'' = char.decode(''utf_8'') ERROR: ''utf8'' codec can''t decode byte 0xf3 in position 0: unexpected end of data

Ahora cambiaré la codificación del sistema y la consola a latin_1, y obtengo esta salida para la misma entrada:

''ó'' = original char <type ''str''> repr(char)=''/xf3'' ''ó'' = unicode(char) <type ''unicode''> repr(unicode(char))=u''/xf3'' ''ó'' = char.decode(''latin_1'') <type ''unicode''> repr(char.decode(''latin_1''))=u''/xf3'' ''?'' = char.decode(''utf_8'') ERROR: ''utf8'' codec can''t decode byte 0xf3 in position 0: unexpected end of data

Observe que el carácter ''original'' se muestra correctamente y la función integrada Unicode () funciona ahora.

Ahora cambio la salida de mi consola a utf_8.

''�'' = original char <type ''str''> repr(char)=''/xf3'' ''�'' = unicode(char) <type ''unicode''> repr(unicode(char))=u''/xf3'' ''�'' = char.decode(''latin_1'') <type ''unicode''> repr(char.decode(''latin_1''))=u''/xf3'' ''?'' = char.decode(''utf_8'') ERROR: ''utf8'' codec can''t decode byte 0xf3 in position 0: unexpected end of data

Aquí todo funciona igual que la última vez pero la consola no puede mostrar la salida correctamente. Etc. La función a continuación también muestra más información que esto y, con suerte, ayudaría a alguien a descubrir dónde está la brecha en su comprensión. Sé que toda esta información está en otros lugares y tratada más detalladamente allí, pero espero que este sea un buen punto de partida para alguien que intenta codificar con python y / o sqlite. Las ideas son geniales, pero a veces el código fuente puede ahorrarle uno o dos días tratando de descubrir qué funciones hacen qué.

Descargo de responsabilidad: no soy un experto en codificación, lo armé para ayudar a mi propia comprensión. Seguí trabajando en ello cuando debería haber empezado a pasar funciones como argumentos para evitar tanto código redundante, así que si puedo lo haré más conciso. Además, utf_8 y latin_1 no son de ninguna manera los únicos esquemas de codificación, son solo los dos con los que estaba jugando porque creo que manejan todo lo que necesito. Agregue sus propios esquemas de codificación a la función de demostración y pruebe su propia entrada.

Una cosa más: aparentemente hay desarrolladores de aplicaciones locos que dificultan la vida en Windows.

#!/usr/bin/env python # -*- coding: utf_8 -*- import os import sys def encodingDemo(str): validStrings = () try: print "str =",str,"{0} repr(str) = {1}".format(type(str), repr(str)) validStrings += ((str,""),) except UnicodeEncodeError as ude: print "Couldn''t print the str itself because the console is set to an encoding that doesn''t understand some character in the string. See error:/n/t", print ude try: x = unicode(str) print "unicode(str) = ",x validStrings+= ((x, " decoded into unicode by the default system encoding"),) except UnicodeDecodeError as ude: print "ERROR. unicode(str) couldn''t decode the string because the system encoding is set to an encoding that doesn''t understand some character in the string." print "/tThe system encoding is set to {0}. See error:/n/t".format(sys.getdefaultencoding()), print ude except UnicodeEncodeError as uee: print "ERROR. Couldn''t print the unicode(str) because the console is set to an encoding that doesn''t understand some character in the string. See error:/n/t", print uee try: x = str.decode(''latin_1'') print "str.decode(''latin_1'') =",x validStrings+= ((x, " decoded with latin_1 into unicode"),) try: print "str.decode(''latin_1'').encode(''utf_8'') =",str.decode(''latin_1'').encode(''utf_8'') validStrings+= ((x, " decoded with latin_1 into unicode and encoded into utf_8"),) except UnicodeDecodeError as ude: print "The string was decoded into unicode using the latin_1 encoding, but couldn''t be encoded into utf_8. See error:/n/t", print ude except UnicodeDecodeError as ude: print "Something didn''t work, probably because the string wasn''t latin_1 encoded. See error:/n/t", print ude except UnicodeEncodeError as uee: print "ERROR. Couldn''t print the str.decode(''latin_1'') because the console is set to an encoding that doesn''t understand some character in the string. See error:/n/t", print uee try: x = str.decode(''utf_8'') print "str.decode(''utf_8'') =",x validStrings+= ((x, " decoded with utf_8 into unicode"),) try: print "str.decode(''utf_8'').encode(''latin_1'') =",str.decode(''utf_8'').encode(''latin_1'') except UnicodeDecodeError as ude: print "str.decode(''utf_8'').encode(''latin_1'') didn''t work. The string was decoded into unicode using the utf_8 encoding, but couldn''t be encoded into latin_1. See error:/n/t", validStrings+= ((x, " decoded with utf_8 into unicode and encoded into latin_1"),) print ude except UnicodeDecodeError as ude: print "str.decode(''utf_8'') didn''t work, probably because the string wasn''t utf_8 encoded. See error:/n/t", print ude except UnicodeEncodeError as uee: print "ERROR. Couldn''t print the str.decode(''utf_8'') because the console is set to an encoding that doesn''t understand some character in the string. See error:/n/t",uee print print "Printing information about each character in the original string." for char in str: try: print "/t''" + char + "'' = original char {0} repr(char)={1}".format(type(char), repr(char)) except UnicodeDecodeError as ude: print "/t''?'' = original char {0} repr(char)={1} ERROR PRINTING: {2}".format(type(char), repr(char), ude) except UnicodeEncodeError as uee: print "/t''?'' = original char {0} repr(char)={1} ERROR PRINTING: {2}".format(type(char), repr(char), uee) print uee try: x = unicode(char) print "/t''" + x + "'' = unicode(char) {1} repr(unicode(char))={2}".format(x, type(x), repr(x)) except UnicodeDecodeError as ude: print "/t''?'' = unicode(char) ERROR: {0}".format(ude) except UnicodeEncodeError as uee: print "/t''?'' = unicode(char) {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee) try: x = char.decode(''latin_1'') print "/t''" + x + "'' = char.decode(''latin_1'') {1} repr(char.decode(''latin_1''))={2}".format(x, type(x), repr(x)) except UnicodeDecodeError as ude: print "/t''?'' = char.decode(''latin_1'') ERROR: {0}".format(ude) except UnicodeEncodeError as uee: print "/t''?'' = char.decode(''latin_1'') {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee) try: x = char.decode(''utf_8'') print "/t''" + x + "'' = char.decode(''utf_8'') {1} repr(char.decode(''utf_8''))={2}".format(x, type(x), repr(x)) except UnicodeDecodeError as ude: print "/t''?'' = char.decode(''utf_8'') ERROR: {0}".format(ude) except UnicodeEncodeError as uee: print "/t''?'' = char.decode(''utf_8'') {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee) print x = ''ó'' encodingDemo(x)

Muchas gracias por las respuestas a continuación y especialmente a @John Machin por responder tan a fondo.


Mis problemas de Unicode con Python 2.x (Python 2.7.6 para ser específico) arreglaron esto:

#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import unicode_literals import sys reload(sys) sys.setdefaultencoding(''utf-8'')

También resolvió el error que estás mencionando justo al comienzo de la publicación:

sqlite3.ProgrammingError: no debe usar cadenas de bytes de 8 bits a menos que ...

EDITAR

sys.setdefaultencoding es un hack sucio . Sí, puede resolver problemas UTF-8, pero todo tiene un precio. Para más detalles, consulte los siguientes enlaces:


Por supuesto que hay Pero sus datos ya están rotos en la base de datos, por lo que deberá corregirlos:

>>> print u''Sigur Rós''.encode(''latin-1'').decode(''utf-8'') Sigur Rós


Solucioné este problema de pysqlite estableciendo:

conn.text_factory = lambda x: unicode(x, ''utf-8'', ''ignore'')

De forma predeterminada, text_factory está establecido en unicode (), que usará la codificación predeterminada actual (ascii en mi máquina)


UTF-8 es la codificación predeterminada de las bases de datos SQLite. Esto aparece en situaciones como "SELECT CAST (x''52C3B373 ''AS TEXT);". Sin embargo, la biblioteca de SQLite C no comprueba realmente si una cadena insertada en un DB es válida UTF-8.

Si inserta un objeto Unicode de Python (o un objeto str en 3.x), la biblioteca de Python sqlite3 lo convertirá automáticamente a UTF-8. Pero si inserta un objeto str, asumirá que la cadena es UTF-8, porque Python 2.x "str" ​​no conoce su codificación. Esta es una razón para preferir cadenas Unicode.

Sin embargo, no te ayuda si tus datos están rotos para empezar.

Para arreglar tus datos, hazlo

db.create_function(''FIXENCODING'', 1, lambda s: str(s).decode(''latin-1'')) db.execute("UPDATE TheTable SET TextColumn=FIXENCODING(CAST(TextColumn AS BLOB))")

para cada columna de texto en su base de datos.