2012-05-29 8 views
6

Посмотрите на следующее:Предупреждение поднят путем вставки 4-байтовое Юникода в MySQL

/home/kinka/workspace/py/tutorial/tutorial/pipelines.py:33: Warning: Incorrect string 
value: '\xF0\x9F\x91\x8A\xF0\x9F...' for column 't_content' at row 1 
n = self.cursor.execute(self.sql, (item['topic'], item['url'], item['content'])) 

Строка '\xF0\x9F\x91\x8A, на самом деле представляет собой 4-байтовое Юникода: u'\U0001f62a'. Набор символов mysql - utf-8, но вставляя 4-байтовый юникод, он усекает вставленную строку. Я столкнулся с такой проблемой и нашел, что mysql под 5.5.3 не поддерживает 4-байтовый юникод, и, к сожалению, мой 5.5.224. Я не хочу обновлять mysql-сервер, поэтому просто хочу отфильтровать 4-байтовый unicode в python, я попытался использовать регулярное выражение, но не смог. Итак, любая помощь?

+1

Это FISTED HAND SIGN цвет emoji: '' ... –

+0

@MartijnPieters - 'unicodedata.name (" \ U0001 f62a ")' говорит '' SLEEPY FACE'' (что было бы 'b '\ xf0 \ x9f \ x98 \ xaa'' в utf-8), поэтому некорректно здесь ... – mata

+0

Собственно, это сонливость лицо. Я соскабливаю страницы из 'sina weibo' (твиттер в Китае), и я царапал такой« SLEEP FACE ». – Kinka

ответ

8

Если MySQL не может обрабатывать коды UTF-8 по 4 байта или более, вам придется отфильтровать все символы юникода по кодовому адресу \U00010000; UTF-8 кодирует кодовые точки ниже этого порога в 3 байта или меньше.

Вы можете использовать регулярное выражение для этого:

>>> import re 
>>> highpoints = re.compile(u'[\U00010000-\U0010ffff]') 
>>> example = u'Some example text with a sleepy face: \U0001f62a' 
>>> highpoints.sub(u'', example) 
u'Some example text with a sleepy face: ' 

В качестве альтернативы можно использовать .translate() function с таблицей отображения, которая содержит только None значения:

>>> nohigh = { i: None for i in xrange(0x10000, 0x110000) } 
>>> example.translate(nohigh) 
u'Some example text with a sleepy face: ' 

Однако создание таблицы перевода будет есть много памяти и потребуется некоторое время для генерации; это, вероятно, не стоит ваших усилий, поскольку подход к регулярному выражению более эффективен.

Все это предполагает, что вы используете скомпилированный питон UCS-4. Если ваш python был скомпилирован с поддержкой UCS-2, вы можете использовать только кодовые страницы до '\U0000ffff' в регулярных выражениях, и вы никогда не столкнетесь с этой проблемой в первую очередь.

Отмечу, что с MySQL 5.5.3 недавно добавленный utf8mb4 codec поддерживает полный диапазон Unicode.

+0

Я пробовал ваш код, но это не сработает. Это '\ U' (верхний регистр' u'). Однако, ваша мысль действительно просвещает, спасибо! – Kinka

+0

Вы совершенно правы; исправлено, чтобы использовать правильные 8-байтовые escape-последовательности. Сначала я столкнулся с некоторыми проблемами из-за использования скомпилированного питона UCS2: -P –

+0

Но в моем случае это действительно имеет значение в нижнем регистре или нет. Я использую 'highpoints = re.compile (u '[\ U00010000- \ U0001ffff]')' и работает. Кажется, что на моем компьютере (это проблема версии python? My is python 2.7). С прописными буквами '\ U', unicode поддерживает гораздо больший диапазон. – Kinka

2

Я думаю, вы должны использовать utf8mb4 сверку вместо utf8 и запустить

SET NAMES UTF8MB4 

после соединения с БД (link, link, link)

+0

Настройка соединения с использованием 'utf8mb4' - лучший подход, но вы не должны делать это с помощью' SET NAMES'. Эта команда изменяет настройки соединения на сервере, не позволяя библиотеке клиентов узнать об этом изменении, а это означает, что что-либо в клиентской библиотеке, использующей API-интерфейс C 'mysql_real_escape_string', может привести к плохим результатам. Это может привести к дырам безопасности SQL-инъекций, если восточноазиатское многобайтовое кодирование является одним или обоими кодировками. Наборы символов должны быть установлены во время соединения; в python-mysql это будет сделано с аргументом 'charset' для' connect() '. – bobince

0

простой нормировки строки без регулярных выражений и перевод:

def normalize_unicode(s): 
    return ''.join([ unichr(k) if k < 0x10000 else 0xfffd for k in [ord(c) for c in s]])