【发布时间】:2011-03-14 07:09:26
【问题描述】:
我正在使用 Python 和 Django,但由于 MySQL 的限制,我遇到了问题。根据MySQL 5.1 documentation,他们的utf8 实现不支持4 字节字符。 MySQL 5.5 将支持使用 utf8mb4 的 4 字节字符;并且,在未来的某一天,utf8 也可能会支持它。
但是我的服务器还没有准备好升级到 MySQL 5.5,因此我被限制为占用 3 个字节或更少的 UTF-8 字符。
我的问题是:如何过滤(或替换)占用超过 3 个字节的 unicode 字符?
我想用官方\ufffd(U+FFFD 替换字符)或?替换所有4字节字符。
换句话说,我想要一个与 Python 自己的 str.encode() 方法(传递 'replace' 参数时)非常相似的行为。 编辑:我想要类似于encode() 的行为,但我不想实际对字符串进行编码。我希望过滤后仍然有一个 unicode 字符串。
我不想在存储到 MySQL 之前转义字符,因为这意味着我需要对从数据库中获取的所有字符串进行转义,这非常烦人且不可行。
另见:
- "Incorrect string value" warning when saving some unicode characters to MySQL(在 Django 票务系统中)
- ‘????’ Not a valid unicode character, but in the unicode character set?(在堆栈溢出)
[编辑] 添加了有关建议解决方案的测试
所以到目前为止我得到了很好的答案。谢谢,人们!现在,为了选择其中一个,我做了一个快速测试,以找到最简单和最快的一个。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4 sw=4 et
import cProfile
import random
import re
# How many times to repeat each filtering
repeat_count = 256
# Percentage of "normal" chars, when compared to "large" unicode chars
normal_chars = 90
# Total number of characters in this string
string_size = 8 * 1024
# Generating a random testing string
test_string = u''.join(
unichr(random.randrange(32,
0x10ffff if random.randrange(100) > normal_chars else 0x0fff
)) for i in xrange(string_size) )
# RegEx to find invalid characters
re_pattern = re.compile(u'[^\u0000-\uD7FF\uE000-\uFFFF]', re.UNICODE)
def filter_using_re(unicode_string):
return re_pattern.sub(u'\uFFFD', unicode_string)
def filter_using_python(unicode_string):
return u''.join(
uc if uc < u'\ud800' or u'\ue000' <= uc <= u'\uffff' else u'\ufffd'
for uc in unicode_string
)
def repeat_test(func, unicode_string):
for i in xrange(repeat_count):
tmp = func(unicode_string)
print '='*10 + ' filter_using_re() ' + '='*10
cProfile.run('repeat_test(filter_using_re, test_string)')
print '='*10 + ' filter_using_python() ' + '='*10
cProfile.run('repeat_test(filter_using_python, test_string)')
#print test_string.encode('utf8')
#print filter_using_re(test_string).encode('utf8')
#print filter_using_python(test_string).encode('utf8')
结果:
-
filter_using_re()在 0.139 CPU 秒 内完成了 515 次函数调用(在sub()内置时为 0.138 CPU 秒) -
filter_using_python()在 3.413 CPU 秒 内完成了 2097923 次函数调用(join()调用时 1.511 CPU 秒和评估生成器表达式的 1.900 CPU 秒) - 我没有使用
itertools进行测试,因为……嗯……那个解决方案虽然很有趣,但相当庞大和复杂。
结论
RegEx 解决方案是迄今为止最快的解决方案。
【问题讨论】:
标签: python mysql django unicode