Запись строки UTF-8 в MySQL с помощью Python
Я пытаюсь вывести данные учетной записи пользователя из Active Directory на наш MySQL-сервер. Это работает безупречно, но каким-то образом строки заканчиваются показом кодированной версии умляутов и других специальных символов.
Active Directory возвращает строку с использованием этого формата: M\xc3\xbcller
На самом деле это кодировка UTF-8 для Müller
, но я хочу написать Müller
в мою базу данных не M\xc3\xbcller
.
Я попытался преобразовать строку в эту строку, но в результате получается одна и та же строка в базе данных:
tempEntry[1] = tempEntry[1].decode("utf-8")
Если я запустил print "M\xc3\xbcller".decode("utf-8")
в консоли python, вывод будет правильным.
Есть ли способ правильно вставить эту строку? Мне нужен этот конкретный формат для веб-разработчика, который хочет иметь этот точный формат, я не знаю, почему он не может напрямую преобразовать строку, используя PHP.
Дополнительная информация: Я использую MySQLdb; Кодирование таблицы и столбца - utf8_general_ci
Ответы
Ответ 1
Я нашел решение своих проблем. Декодирование строки с помощью .decode('unicode_escape').encode('iso8859-1').decode('utf8')
действительно продолжалось. Теперь все вставлено так, как должно. Полное другое решение можно найти здесь: Работа с кодированными в Unicode строками из Active Directory через python-ldap
Ответ 2
Как предлагает @marr75, убедитесь, что вы установили charset='utf8'
в свои соединения. Установка use_unicode=True
не является строго необходимой, поскольку это подразумевается установкой кодировки.
Затем убедитесь, что вы передаете объекты юникода в ваше соединение db, поскольку оно будет кодировать его, используя кодировку, которую вы передали курсору. Если вы передаете строку, закодированную в utf8, она будет дважды закодирована, когда она достигнет базы данных.
Итак, что-то вроде:
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,))
Вы также можете попытаться заставить соединение использовать utf8, передав параметр init_command, хотя я не уверен, что это требуется. 5 минут тестирования должны помочь вам решить.
conn = MySQLdb.connect(charset='utf8', init_command='SET NAMES UTF8')
Кроме того, и это едва стоит упомянуть, поскольку 4.1 является настолько старым, убедитесь, что вы используете MySQL >= 4.1
Ответ 3
Предполагая, что вы используете MySQLdb, вам необходимо передать use_unicode = True и charset = "utf8" при создании вашего соединения.
UPDATE:
Если я запустил следующее против тестовой таблицы, я получаю -
>>> 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',),)
Это "правильный путь", символы сохраняются и извлекаются правильно, ваш друг, пишущий php script, просто неправильно обрабатывает кодировку при выводе.
Как указывает Rob, use_unicode и комбинированная комбинация содержат подробные сведения о соединении, но у меня есть естественная паранойя о даже самых полезных библиотеках python за пределами стандартной библиотеки, поэтому я стараюсь быть явным, чтобы сделать ошибки легко найти, если библиотека изменяется.
Ответ 4
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
Ответ 5
Недавно у меня была такая же проблема, когда значение поля было байтовой строкой вместо unicode. Вот небольшой анализ.
Обзор
В общем, все, что нужно сделать, чтобы иметь значения юникода из курсора, состоит в передаче аргумента charset
конструктору соединения и наличии не двоичных полей таблицы (например, utf8_general_ci
). Передача use_unicode
бесполезна, потому что она имеет значение true, когда charset
имеет значение.
MySQLdb уважает типы полей описания курсора, поэтому, если у вас есть столбец DATETIME
в курсоре, значения будут преобразованы в экземпляры Python datatime.datetime
, от DECIMAL
до decimal.Decimal
и т.д., но будут представлены двоичные значения как есть, по байтовым строкам. Большинство декодеров определены в MySQLdb.converters
, и их можно переопределить на основе экземпляра, предоставив аргумент conv
конструктору соединения.
Но unicode-декодеры здесь являются исключением, что, вероятно, является недостатком дизайна. Они прилагаются непосредственно к конвертерам экземпляра соединения в его конструкторе. Таким образом, возможно только переопределить их на основе instance-basic.
Обход
Посмотрите код проблемы.
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)
Это показывает, что поле b
возвращается как строка байта вместо unicode. Однако он не является двоичным, MySQLdb.constants.FLAG.BINARY & cursor.description_flags[1]
(флагов полей MySQLdb). Это похоже на ошибку в библиотеке (открыт # 90). Но причина этого я вижу как MySQLdb.constants.FIELD_TYPE.LONG_BLOB
(cursor.description[1][1] == 251
, типы полей MySQLdb) просто не имеет конвертера вообще.
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)
Таким образом, манипулируя экземпляром соединения converter
dict, можно добиться желаемого поведения декодирования в Юникоде.
Если вы хотите переопределить поведение здесь, как выглядит запись dict для возможного текстового поля после конструктора.
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
. Это означает, что если поле имеет двоичный флаг, оно будет str
, в противном случае будет применен декодер unicode. Таким образом, вы также захотите преобразовать двоичные значения, вы можете поместить первый кортеж.
Ответ 6
(хотел бы ответить на ответ выше, но не имеет достаточной репутации...)
Причина, по которой вы не получаете результаты Unicode в этом случае:
>>> print c.fetchall()
(('M\xc3\xbcller',),)
является ошибкой из MySQLdb 1.2.x с * _bin сопоставлением, см.:
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
В этом конкретном случае (сопоставление utf8_bin - или [все] _bin...) вы должны ожидать "необработанного" значения, здесь utf-8 (да, это отстой, поскольку нет общее исправление).
Ответ 7
и db.set_character_set ('utf8'), следует, что
use_unicode = Правда?
Ответ 8
есть другая ситуация, может быть, немного редка.
если вы сначала создадите схему в mysqlworkbench, вы получите ошибку кодирования и не сможете ее решить, добавив конфигурацию кодировки.
это потому, что mysqlworkbench по умолчанию создает схему по latin1, поэтому сначала нужно установить кодировку!
![введите описание изображения здесь]()