Aviso gerado pela inserção de unicode de 4 bytes no mysql

Veja o seguinte:

/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']))

A corda'\xF0\x9F\x91\x8A, na verdade, é um unicode de 4 bytes:u'\U0001f62a'. O conjunto de caracteres do mysql é utf-8, mas inserindo unicode de 4 bytes truncará a string inserida. Eu pesquisei por tal problema e descobri que o mysql em 5.5.3 não suporta unicode de 4 bytes, e infelizmente o meu é 5.5.224. Eu não quero atualizar o servidor mysql, então eu só quero filtrar o unicode de 4 bytes em python, eu tentei usar a expressão regular, mas falhou. Então, alguma ajuda?

 mata29 de mai de 2012 14:01
@MartijnPieters -unicodedata.name("\U0001f62a") diz'SLEEPY FACE' (qual seriab'\xf0\x9f\x98\xaa' em utf-8), então algo não está bem aqui ...
 Kinka29 de mai de 2012 14:08
Na verdade, é um rosto sonolento. Estou raspando páginas desina weibo(twitter na China), e eu raspeiSLEEP FACE.
 Martijn Pieters29 de mai de 2012 14:54
Sim e'\xF0\x9F\x91\x8A'.decode('utf8') éu'\U0001f44a', qual é'FISTED HAND SIGN' :-)
 Martijn Pieters29 de mai de 2012 13:59
Esse é o emoji colorido FISTED HAND SIGN:👊...

questionAnswers(3)

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

você terá que filtrar todos os caracteres unicode sobre o codepoint\U00010000; O UTF-8 codifica pontos de código abaixo desse limite em 3 bytes ou menos.

Você poderia usar uma expressão regular para isso:

>>> 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: '

Alternativamente, você poderia usar o.translate() função com uma tabela de mapeamento que contém apenasNone valores:

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

No entanto, criar a tabela de conversão consumirá muita memória e levará algum tempo para gerar; provavelmente não vale a pena seu esforço, pois a abordagem de expressão regular é mais eficiente.

Isso tudo presume que você está usando um python UCS-4 compilado. Se o seu python foi compilado com suporte a UCS-2, então você só pode usar pontos de código de até'\U0000ffff' em expressões regulares e você nunca vai se deparar com esse problema em primeiro lugar.

Eu observei que, a partir do MySQL 5.5.3, o recém-adicionadoutf8mb4 codec suporta o intervalo Unicode completo.

 Michael Waterfall05 de set de 2012 20:44
Ah tudo bem, obrigado! Fora de interesse, então, por queprint(u'\U0001f3b6') exibir o caractere de emoji correto (que é de 4 bytes)?
 Martijn Pieters05 de set de 2012 21:28
@MichaelWaterfall: Em uma construção UCS-4 você pode compilar a expressão regular, em uma construção UCS-2 você não pode. O Python 3.3 acaba com a distinção, por isso, há esperança para o futuro. :-)
 Martijn Pieters05 de set de 2012 21:30
@MichaelWaterfall: Façaimport sys; print sys.maxunicode. Se você pegar65535 é uma compilação UCS-2,1114111 para uma ampla construção UCS-4.
 Martijn Pieters05 de set de 2012 21:42
@MichaelWaterfall: Em um build do UCS-2, esse personagem é na verdade2 bytes, usando um substituto UTF-16;len(u'\U0001f3b6') == 2 em tal compilação. Em uma construção UCS-4, élen(u'\U0001f3b6') == 1..
 Michael Waterfall07 de set de 2012 16:07
@MartijnPieters Obrigado pela ótima informação. Eu realmente notei quelen(u'\U0001f3b6') == 2 e percebi que isso foi causado pela versão UCS-2 do Python. Hora de atualizar! Obrigado novamente.
 Martijn Pieters05 de set de 2012 21:27
@MichaelWaterfall: Não tenho certeza de como tudo isso é tratado; uma compilação UCS2 pode imprimir uma cadeia unicode de 4 bytes, mas o módulo de expressão regular não pode manipulá-la corretamente porque a representação interna não pode manipulá-lo.
 Martijn Pieters05 de set de 2012 20:38
@MichaelWaterfall: Você tem um UCS2 compilado (unicode de 2 bytes) em Python; ele suportará apenas valores unicode até\uffff.
 Martijn Pieters29 de mai de 2012 15:12
Não, você está bem correto. Eu apenas abaixei o limite inferior também, percebendo que eu interpretei mal a tabela UTF-8.
 Kinka29 de mai de 2012 14:59
Eu tentei o seu código, mas não funciona. Isto é\U(maiúsculasu). No entanto, seu pensamento é realmente esclarecedor, obrigado!
 Martijn Pieters29 de mai de 2012 15:10
Você está certo; corrigido para usar sequências de escape adequadas de 8 bytes. Eu tive alguns problemas no início devido ao uso de um python compilado UCS2 :-P
 Michael Waterfall05 de set de 2012 20:33
Qualquer ideia do motivo pelo qual estou recebendo um erro com:re.compile(u'[\U00010000-\U0010ffff]') "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 244, in _compile raise error, v # invalid expression sre_constants.error: bad character range
 Kinka29 de mai de 2012 15:12
Mas no meu caso, realmente importa se está em minúsculas ou não. O que eu uso éhighpoints = re.compile(u'[\U00010000-\U0001ffff]') e funciona. Parece que no meu computador (é o problema da versão do python? O meu é python 2.7) .Com maiúsculas\U, o unicode suporta um alcance muito maior.

rodar

SET NAMES UTF8MB4

após conexão com DB (ligação, ligação, ligação)

 bobince23 de jul de 2015 13:38
Configurando a conexão para usarutf8mb4 é a melhor abordagem, mas você não deve fazer isso comSET NAMES. Este comando altera a configuração de conexão no servidor sem informar a biblioteca cliente sobre a alteração, o que significa que qualquer coisa na biblioteca cliente usando o Cmysql_real_escape_string API pode obter resultados ruins. Isso pode resultar em falhas de segurança de injeção SQL se uma codificação multibyte do Leste Asiático for um ou ambos os conjuntos de caracteres envolvidos. Conjuntos de caracteres devem ser definidos no momento da conexão; em python-mysql isso seria feito com ocharset argumento paraconnect().

yourAnswerToTheQuestion