2011-06-01 29 views
27

Estoy tratando de enviar datos de cuentas de usuario desde 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.Escribir cadenas UTF-8 en MySQL con Python

El Active Directory devuelve una cadena que utiliza este formato de muestra: M\xc3\xbcller

En realidad, esto es la codificación UTF-8 para Müller, pero yo quiero escribir Müller a mi base de datos no M\xc3\xbcller.

He intentado convertir la cadena con esta línea, pero da lugar a la misma cadena en la base de datos: tempEntry[1] = tempEntry[1].decode("utf-8")

Si me quedo print "M\xc3\xbcller".decode("utf-8") en la consola Python la salida es correcta.

¿Hay alguna manera 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 utilizando MySQLdb; La tabla y la columna de codificación es utf8_general_ci

Respuesta

15

Suponiendo que está usando MySQLdb que necesita para pasar use_unicode = Verdadero y charset = "UTF-8" al crear la conexión.

ACTUALIZACIÓN: Si funciono el siguiente contra una tabla de prueba llego -

>>> 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',),) 

Este es "el camino correcto", los caracteres están siendo almacenados y recuperados correctamente, su amigo a escribir el guión php solo no está manejando la codificación correctamente cuando se está produciendo.

Como señala Rob, use_unicode y charset combinados son muy detallados sobre la conexión, pero tengo una paranoia natural incluso sobre las bibliotecas de Python más útiles fuera de la biblioteca estándar, así que intento ser explícito para hacer que los errores sean fáciles de encontrar si la biblioteca cambia

+0

Sí, estoy usando MySQLdb. Olvidé mencionar eso en mi publicación anterior. Probé configurar charset y use_unicode manualmente (aunque el primero parece implicar el último), pero el resultado es el mismo. También intenté configurar .decode ("utf-8") en la instrucción INSERT. Aún tiene el mismo formato ... – Raptor

+0

¿Cuál es la codificación establecida en la columna de su base de datos? Pruebe utf8-bin. Es posible que esté transmitiendo los datos perfectamente, pero está escrito en alguna codificación que no incluye los caracteres que está utilizando. – marr75

+0

La codificación era 'utf8_general_ci', intenté configurar la tabla y las columnas para' utf8_bin' sin ningún efecto. – Raptor

40

Como @ marr75 sugiere, asegúrese de establecer charset='utf8' en sus conexiones. La configuración use_unicode=True no es estrictamente necesaria ya que está implícita al establecer el juego de caracteres.

Luego asegúrese de estar pasando unicode objetos 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.

Por lo tanto, algo así 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 utilizar UTF-8 al pasar el parámetro init_command, aunque estoy seguro si esto es necesario. 5 minutos de prueba deberían ayudarte a decidir.

conn = MySQLdb.connect(charset='utf8', init_command='SET NAMES UTF8') 

También, y esto es apenas vale la pena mencionar que 4.1 es tan viejo, asegúrese de que está utilizando MySQL> = 4.1

+0

¿Es eso cierto para todas las sentencias MySQLdb.execute - que debe decodificar ('utf8') para unicode y enviar en cadena como tal? Los resultados en mi prueba son luego unicode en la base de datos y no están codificados en utf8, pero obtengo una basura latina1 si trato de enviar unicode directamente. –

0

y db.set_character_set ('utf8'), implica que use_unicode = ¿Verdadero?

+0

Disculpe la respuesta tardía: Sí, el juego de caracteres implica use_unicode = True – Raptor

2

(desea responder a la respuesta anterior, pero no tienen la reputación suficiente ...)

La razón por la que no obtiene resultados Unicode en este caso:

>>> print c.fetchall() 
(('M\xc3\xbcller',),) 

es una fallo de MySQLdb 1.2.x con * cotejo _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 & ayuda = 2663436 & group_id = 22307 & lavóh = 374932

En este caso particular (cotejo utf8_bin - o [nada] _bin ...) hay que esperar que el valor "en bruto", aquí UTF-8 (sí, esto es una mierda ya que no hay una solución genérica).

8
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 
+1

Esta es la única respuesta que realmente incluye todos los caracteres Unicode posibles, incluso los emojis. Gracias. – jamescampbell

5

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.

general

En general todo lo que uno tiene que hacer para tener valores Unicode de un cursor, es pasar charset argumento para el constructor de conexión y tienen campos de la tabla no binarios (por ejemplo utf8_general_ci). Pasar use_unicode es inútil porque se establece en verdadero siempre que charset tenga un valor.

MySQLdb respeta Descripción cursor tipos de campo, por lo que si tiene una columna DATETIME en cursor los valores serán convertidos a Python datatime.datetime casos, DECIMAL a decimal.Decimal y así sucesivamente, pero los valores binarios se representará como es, por cadenas de bytes. La mayoría de los decodificadores se definen en MySQLdb.converters, y uno puede anularlos en base a instancia proporcionando el argumento conv al constructor de conexión.

Pero los decodificadores unicode son una excepción aquí, lo que probablemente sea una falla de diseño. Son appended directly para convertidores de instancia de conexión en su constructor. Por lo tanto, solo es posible anularlos en instancia básica.

Solución temporal

Veamos el código de emisión.

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 b campo se devuelve como una cadena de bytes en lugar de Unicode. Sin embargo, no es binario, MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1] (MySQLdb field flags). Parece un error en la biblioteca (abierto #90). Pero la razón por la que veo como MySQLdb.constants.FIELD_TYPE.LONG_BLOB (cursor.description[1][1] == 251, MySQLdb field types) 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) 

lo tanto mediante la manipulación de instancia de conexión converter dict, es posible lograr un comportamiento de descodificación Unicode deseado.

Si desea sobrescribir 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.

0

hay otra situación tal vez un poco rara.

si crea un esquema en mysqlworkbench en primer lugar, obtendrá el error de codificación y no puede resolverlo agregando la configuración del conjunto de caracteres.

es porque mysqlworkbench crea un esquema por latin1 de forma predeterminada, por lo que debe establecer el juego de caracteres al principio. enter image description here

Cuestiones relacionadas