python - tutorial - the django project
Escribir cadena UTF-8 en MySQL con Python (8)
(Me gustaría responder a la respuesta anterior, pero no tengo suficiente reputación ...)
La razón por la que no obtiene resultados Unicode en este caso:
>>> print c.fetchall()
((''M/xc3/xbcller'',),)
es un error de MySQLdb 1.2.x con intercalación * _bin, ver:
http://sourceforge.net/tracker/index.php?func=detail&aid=1693363&group_id=22307&atid=374932 http://sourceforge.net/tracker/index.php?func=detail&aid=2663436&group_id=22307&atid=374932
En este caso particular (colación utf8_bin - o [cualquier cosa] _bin ...) debe esperar el valor "en bruto", aquí utf-8 (sí, esto apesta ya que no hay una solución genérica).
Estoy tratando de enviar los datos de la cuenta de usuario de un Directorio Activo a nuestro servidor MySQL. Esto funciona sin problemas, pero de alguna manera las cadenas terminan mostrando una versión codificada de diéresis y otros caracteres especiales.
El Active Directory devuelve una cadena usando este formato de muestra: M/xc3/xbcller
Esta es realmente la codificación UTF-8 para Müller
, pero quiero escribir Müller
en mi base de datos no M/xc3/xbcller
.
Traté de convertir la cadena con esta línea, pero resulta en la misma cadena en la base de datos: tempEntry[1] = tempEntry[1].decode("utf-8")
Si ejecuto print "M/xc3/xbcller".decode("utf-8")
en la consola de python, la salida es correcta.
¿Hay alguna forma de insertar esta cadena de la manera correcta? Necesito este formato específico para un desarrollador web que quiera tener este formato exacto, no sé por qué no puede convertir la cadena usando PHP directamente.
Información adicional: estoy usando MySQLdb; La codificación de tabla y columna es utf8_general_ci
Como sugiere @ marr75, asegúrese de establecer charset=''utf8''
en sus conexiones. Establecer use_unicode=True
no es estrictamente necesario ya que está implícito al configurar el juego de caracteres.
Luego asegúrese de pasar objetos Unicode a su conexión db, ya que la codificará utilizando el juego de caracteres que pasó al cursor. Si está pasando una cadena codificada en utf8, estará doblemente codificada cuando llegue a la base de datos.
Entonces, algo como:
conn = MySQLdb.connect(host="localhost", user=''root'', password='''', db='''', charset=''utf8'')
data_from_ldap = ''M/xc3/xbcller''
name = data_from_ldap.decode(''utf8'')
cursor = conn.cursor()
cursor.execute(u"INSERT INTO mytable SET name = %s", (name,))
También puede intentar forzar la conexión para usar utf8 pasando el parámetro init_command, aunque no estoy seguro de si es necesario. 5 minutos de prueba deberían ayudarte a decidir.
conn = MySQLdb.connect(charset=''utf8'', init_command=''SET NAMES UTF8'')
Además, y esto apenas vale la pena mencionar ya que 4.1 es tan viejo, asegúrese de que está utilizando MySQL> = 4.1
Encontré la solución a mis problemas. Descifrando la cadena con .decode(''unicode_escape'').encode(''iso8859-1'').decode(''utf8'')
funcionó al fin. Ahora todo está insertado como debería. La otra solución completa se puede encontrar aquí: Trabajar con cadenas codificadas en Unicode desde Active Directory a través de python-ldap
Recientemente tuve el mismo problema con el valor de campo que es una cadena de bytes en lugar de unicode. Aquí hay un pequeño análisis.
Visión de conjunto
En general, todo lo que uno debe hacer para tener valores Unicode desde un cursor, es pasar el argumento charset
al constructor de conexión y tener campos de tabla no binarios (por ejemplo, utf8_general_ci
). Pasar use_unicode
es inútil porque se establece en verdadero siempre que el juego de charset
tenga un valor.
MySQLdb respeta los tipos de campo de descripción del cursor, por lo que si tiene una columna DATETIME
en el cursor, los valores se convertirán en instancias de Python datatime.datetime
, DECIMAL
en decimal.Decimal
y así sucesivamente, pero los valores binarios se representarán tal cual, mediante cadenas de bytes. La mayoría de los decodificadores están definidos en MySQLdb.converters
, y uno puede anularlos en base a instancias proporcionando un argumento de conv
para el constructor de conexión.
Pero los decodificadores unicode son una excepción aquí, lo que probablemente sea un defecto de diseño. Se anexan directamente a los convertidores de instancia de conexión en su constructor. Por lo tanto, solo es posible anularlos en instancia básica.
Solución
Veamos el código de problema.
import MySQLdb
connection = MySQLdb.connect(user = ''guest'', db = ''test'', charset = ''utf8'')
cursor = connection.cursor()
cursor.execute(u"SELECT ''abcdё'' `s`, ExtractValue(''<a>abcdё</a>'', ''/a'') `b`")
print cursor.fetchone()
# (u''abcd/u0451'', ''abcd/xd1/x91'')
print cursor.description
# ((''s'', 253, 6, 15, 15, 31, 0), (''b'', 251, 6, 50331648, 50331648, 31, 1))
print cursor.description_flags
# (1, 0)
Muestra que el campo b
se devuelve como una cadena de bytes en lugar de unicode. Sin embargo, no es binario, MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1]
( Indicadores de campo MySQLdb ). Parece un error en la biblioteca (abierto #90 ). Pero el motivo que veo como MySQLdb.constants.FIELD_TYPE.LONG_BLOB
( cursor.description[1][1] == 251
, tipos de campo MySQLdb ) simplemente no tiene un convertidor en absoluto.
import MySQLdb
import MySQLdb.converters as conv
import MySQLdb.constants as const
connection = MySQLdb.connect(user = ''guest'', db = ''test'', charset = ''utf8'')
connection.converter[const.FIELD_TYPE.LONG_BLOB] = connection.converter[const.FIELD_TYPE.BLOB]
cursor = connection.cursor()
cursor.execute(u"SELECT ''abcdё'' `s`, ExtractValue(''<a>abcdё</a>'', ''/a'') `b`")
print cursor.fetchone()
# (u''abcd/u0451'', u''abcd/u0451'')
print cursor.description
# ((''s'', 253, 6, 15, 15, 31, 0), (''b'', 251, 6, 50331648, 50331648, 31, 1))
print cursor.description_flags
# (1, 0)
De este modo, manipulando el converter
instancia de conexión dict, es posible lograr el comportamiento de decodificación unicode deseado.
Si desea anular el comportamiento, aquí se muestra cómo se ve una entrada dict para el posible campo de texto después del constructor.
import MySQLdb
import MySQLdb.constants as const
connection = MySQLdb.connect(user = ''guest'', db = ''test'', charset = ''utf8'')
print connection.converter[const.FIELD_TYPE.BLOB]
# [(128, <type ''str''>), (None, <function string_decoder at 0x7fa472dda488>)]
MySQLdb.constants.FLAG.BINARY == 128
. Esto significa que si un campo tiene bandera binaria, será str
, de lo contrario se aplicará el decodificador Unicode. Por lo tanto, si desea intentar convertir valores binarios, puede mostrar la primera tupla.
Suponiendo que está utilizando MySQLdb, necesita pasar use_unicode = True y charset = "utf8" al crear su conexión.
ACTUALIZACIÓN: Si ejecuto lo siguiente en una tabla de prueba, obtengo -
>>> db = MySQLdb.connect(host="localhost", user=''root'', passwd=''passwd'', db=''sandbox'', use_unicode=True, charset="utf8")
>>> c = db.cursor()
>>> c.execute("INSERT INTO last_names VALUES(%s)", (u''M/xfcller'', ))
1L
>>> c.execute("SELECT * FROM last_names")
1L
>>> print c.fetchall()
((''M/xc3/xbcller'',),)
Esta es "la manera correcta", los personajes se almacenan y se recuperan correctamente, su amigo que escribe el script php simplemente no maneja la codificación correctamente cuando realiza la salida.
Como señala Rob, use_unicode y charset combinados son muy detallados sobre la conexión, pero tengo una paranoia natural sobre incluso las bibliotecas de Python más útiles fuera de la biblioteca estándar, así que trato de ser explícito para hacer que los errores sean fáciles de encontrar si la biblioteca cambia .
hay otra situación tal vez un poco rara.
si primero crea un esquema en mysqlworkbench, obtendrá el error de codificación y no podrá resolverlo agregando la configuración del conjunto de caracteres.
es porque mysqlworkbench crea el esquema por latin1 de forma predeterminada, ¡así que deberías establecer el juego de caracteres al principio!
y db.set_character_set (''utf8''), implica que use_unicode = ¿Verdadero?
import MySQLdb
# connect to the database
db = MySQLdb.connect("****", "****", "****", "****") #don''t use charset here
# setup a cursor object using cursor() method
cursor = db.cursor()
cursor.execute("SET NAMES utf8mb4;") #or utf8 or any other charset you want to handle
cursor.execute("SET CHARACTER SET utf8mb4;") #same as above
cursor.execute("SET character_set_connection=utf8mb4;") #same as above
# run a SQL question
cursor.execute("****")
#and make sure the MySQL settings are correct, data too