J'essaie de pousser les données de compte d'utilisateur d'un Active Directory vers notre serveur MySQL. Cela fonctionne parfaitement, mais d'une manière ou d'une autre, les chaînes finissent par afficher une version codée des trémas et autres caractères spéciaux.
Active Directory renvoie une chaîne en utilisant cet exemple de format: M\xc3\xbcller
Il s'agit en fait de l'encodage UTF-8 pour Müller
, Mais je veux écrire Müller
Dans ma base de données et non M\xc3\xbcller
.
J'ai essayé de convertir la chaîne avec cette ligne, mais il en résulte la même chaîne dans la base de données: tempEntry[1] = tempEntry[1].decode("utf-8")
Si je lance print "M\xc3\xbcller".decode("utf-8")
dans la console python la sortie est correcte.
Existe-t-il un moyen d'insérer cette chaîne correctement? J'ai besoin de ce format spécifique pour un développeur web qui veut avoir ce format exact, je ne sais pas pourquoi il n'est pas capable de convertir la chaîne en utilisant PHP directement.
Informations supplémentaires: j'utilise MySQLdb; Le codage des tables et des colonnes est utf8_general_ci
J'ai trouvé la solution à mes problèmes. Le décodage de la chaîne avec .decode('unicode_escape').encode('iso8859-1').decode('utf8')
a finalement fonctionné. Maintenant, tout est inséré comme il se doit. L'autre solution complète peut être trouvée ici: Travailler avec des chaînes encodées en unicode depuis Active Directory via python-ldap
Comme le suggère @ marr75, assurez-vous de définir charset='utf8'
sur vos connexions. Réglage use_unicode=True
n'est pas strictement nécessaire car cela est implicite en définissant le jeu de caractères.
Assurez-vous ensuite que vous passez des objets nicode à votre connexion db car il les encodera en utilisant le jeu de caractères que vous avez passé au curseur. Si vous passez une chaîne encodée en utf8, elle sera doublement encodée lorsqu'elle atteindra la base de données.
Donc, quelque chose comme:
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,))
Vous pouvez également essayer de forcer la connexion à utiliser utf8 en passant le paramètre init_command, bien que je ne sois pas sûr si cela est nécessaire. Un test de 5 minutes devrait vous aider à décider.
conn = MySQLdb.connect(charset='utf8', init_command='SET NAMES UTF8')
De plus, et cela vaut à peine la peine d'être mentionné car la version 4.1 est si ancienne, assurez-vous que vous utilisez MySQL> = 4.1
En supposant que vous utilisez MySQLdb, vous devez passer use_unicode = True et charset = "utf8" lors de la création de votre connexion.
MISE À JOUR: Si je lance ce qui suit sur une table de test, j'obtiens -
>>> 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',),)
C'est "la bonne façon", les caractères sont stockés et récupérés correctement, votre ami qui écrit le script php ne gère tout simplement pas l'encodage correctement lors de la sortie.
Comme le souligne Rob, use_unicode et charset combinés sont verbeux sur la connexion, mais j'ai une paranoïa naturelle même sur les bibliothèques python les plus utiles en dehors de la bibliothèque standard, donc j'essaie d'être explicite pour rendre les bogues faciles à trouver si la bibliothèque change.
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
Récemment, j'ai eu le même problème avec la valeur du champ étant une chaîne d'octets au lieu d'unicode. Voici une petite analyse.
En général, tout ce qu'il faut faire pour avoir des valeurs unicode à partir d'un curseur, c'est passer l'argument charset
au constructeur de connexion et avoir des champs de table non binaires (par exemple utf8_general_ci
). Qui passe use_unicode
est inutile car il est défini sur true chaque fois que charset
a une valeur.
MySQLdb respecte les types de champs de description du curseur, donc si vous avez une colonne DATETIME
dans le curseur, les valeurs seront converties en Python datatime.datetime
instances, DECIMAL
à decimal.Decimal
et ainsi de suite, mais les valeurs binaires seront représentées telles quelles, par des chaînes d'octets. La plupart des décodeurs sont définis dans MySQLdb.converters
, et on peut les remplacer en fonction de l'instance en fournissant l'argument conv
au constructeur de connexion.
Mais les décodeurs Unicode sont une exception ici, ce qui est probablement un défaut de conception. Ils sont ajoutés directement aux convertisseurs d'instance de connexion dans son constructeur. Il n'est donc possible de les remplacer que sur instance-basic.
Voyons le code du problème.
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)
Il montre que le champ b
est retourné sous forme de chaîne d'octets au lieu d'unicode. Mais ce n'est pas binaire, MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1]
( drapeaux de champ MySQLdb ). Cela ressemble à un bug dans la bibliothèque (ouvert # 9 ). Mais la raison pour laquelle je vois que MySQLdb.constants.FIELD_TYPE.LONG_BLOB
(cursor.description[1][1] == 251
, types de champs MySQLdb ) n'a tout simplement pas de convertisseur du tout.
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)
Ainsi, en manipulant l'instance de connexion converter
dict, il est possible d'obtenir le comportement de décodage Unicode souhaité.
Si vous souhaitez remplacer le comportement, voici à quoi ressemble une entrée dict pour un champ de texte possible après le constructeur.
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
. Cela signifie que si un champ a un drapeau binaire, ce sera str
, sinon le décodeur unicode sera appliqué. Donc, vous voulez également essayer de convertir des valeurs binaires, vous pouvez faire apparaître le premier tuple.
(Je voudrais répondre à la réponse ci-dessus mais je n'ai pas assez de réputation ...)
La raison pour laquelle vous n'obtenez pas de résultats Unicode dans ce cas:
>>> print c.fetchall()
(('M\xc3\xbcller',),)
est un bogue de MySQLdb 1.2.x avec le classement * _bin, voir:
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
Dans ce cas particulier (classement utf8_bin - ou [n'importe quoi] _bin ...), vous devez vous attendre à la valeur "brute", ici utf-8 ( oui, ça craint car il n'y a pas de correctif générique).
et db.set_character_set ('utf8'), impliquent que use_unicode = True?
il y a une autre situation peut-être un peu rare.
si vous créez d'abord un schéma dans mysqlworkbench, vous obtiendrez l'erreur de codage et ne pourrez pas la résoudre en ajoutant une configuration charset.
c'est parce que mysqlworkbench crée un schéma par latin1 par défaut, vous devez donc définir le jeu de caractères au début!