我正在使用Python和Django,但是由于MySQL的限制,我遇到了问题。根据MySQL 5.1文档,他们的
但我的服务器还没有准备好升级到MySQL 5.5,因此我只能使用占用3个或更少字节的UTF-8字符。
我的问题是:如何过滤(或替换)需要超过3个字节的Unicode字符? 我想将所有4字节字符都替换为官方的
换句话说,我希望行为与Python自己的
另请参见:
- 在Django票务系统上,“保存某些Unicode字符时出现不正确的字符串值”警告 - ‘’虽然在Unicode字符集中,但不是有效的Unicode字符吗?(在Stack Overflow上)
结果:
utf8
实现不支持4字节字符。MySQL 5.5将使用utf8mb4
支持4字节字符;未来,utf8
也可能支持它。但我的服务器还没有准备好升级到MySQL 5.5,因此我只能使用占用3个或更少字节的UTF-8字符。
我的问题是:如何过滤(或替换)需要超过3个字节的Unicode字符? 我想将所有4字节字符都替换为官方的
\ufffd
(U+FFFD替换字符),或者用?
替换。换句话说,我希望行为与Python自己的
str.encode()
方法非常相似(当传递'replace'
参数时)。编辑:我希望行为类似于encode()
,但我不想实际编码字符串。我仍然希望在过滤后保留一个Unicode字符串。
我不希望在存储到MySQL之前转义字符,因为这意味着我需要对从数据库获取的所有字符串进行取消转义,这非常麻烦和不可行。另请参见:
- 在Django票务系统上,“保存某些Unicode字符时出现不正确的字符串值”警告 - ‘’虽然在Unicode字符集中,但不是有效的Unicode字符吗?(在Stack Overflow上)
【编辑】添加了有关所提出的解决方案的测试
所以到目前为止,我得到了很好的答案。谢谢大家!现在,为了选择其中一个,我进行了快速测试,以找到最简单和最快的方法。#!/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
进行测试,因为......嗯...虽然有趣,但那个解决方案相当大和复杂。
结论
正则表达式解决方案是迄今为止最快的。
"[^\u0000-\uFFFF]"
等不是原始字符串,也就是说,字符串文字没有前缀r
! - Flimmu'[^\u0000-\uD7FF\uE000-\uFFFF]'
中的第一个范围结束符从'\uD7FF'
更改为'\u07FF'
,因为仍然有一些字符没有被清除。 - Rolando Urquiza