Google App Engine使用Python 2.5.2,显然启用了UCS4。但GAE数据存储区在内部使用UTF-8。因此,如果将u'\ ud834 \ udd0c'(长度为2)存储到数据存储区,则在检索它时,会得到'\ U0001d10c'(长度为1)。我试图计算字符串中unicode字符的数量,以便在存储它之前和之后给出相同的结果。因此,在收到字符串之前,我会尝试将字符串规范化(从u'\ ud834 \ udd0c'到'\ U0001d10c'),然后再计算其长度并将其放入数据存储区。我知道我可以将其编码为UTF-8然后再次解码,但是有更简单/有效的方法吗?
答案 0 :(得分:4)
我知道我可以将其编码为UTF-8然后再解码
是的,当你有“UCS-4字符串中的UTF-16代理”输入时,这是解决问题的常用习惯用法。但是正如Mechanical蜗牛所说,这个输入 格式不正确,你应该修理任何产生它的东西。
有更简单/有效的方法吗?
嗯......你可以手动使用正则表达式,例如:
re.sub(
u'([\uD800-\uDBFF])([\uDC00-\uDFFF])',
lambda m: unichr((ord(m.group(1))-0xD800<<10)+ord(m.group(2))-0xDC00+0x10000),
s
)
当然不是更直截了当......我也怀疑它是否真的更有效率!
答案 1 :(得分:2)
不幸的是,CPython解释器在3.3之前版本中的行为取决于它是用“窄”还是“宽”Unicode支持构建的。因此,相同的代码(例如对len
的调用)可能在标准解释器的不同构建中具有不同的结果。有关示例,请参阅this question。
“narrow”和“wide”之间的区别在于“narrow”解释器在内部存储16位代码单元(UCS-2),而“wide”解释器在内部存储32位代码单元(UCS-4)。代码点 U + 10000及以上(基本多语言平面之外)在“窄”解释器上有两个len
因为两个UCS-2代码单位需要代表他们(使用代理人),这就是len
衡量的内容。在“宽”构建中,非BMP代码点只需要一个UCS-4代码单元,因此对于那些构建len
是一个代码点。
我已经确认以下处理所有unicode
字符串,无论它们是否包含代理项对,并且在CPython 2.7中都适用于窄版本和宽版本。 (可以说,在广泛的解释器中指定类似u'\ud83d\udc4d'
的字符串反映了表示完整的代理代码 point 与部分字符代码单元不同的肯定愿望。因此不会自动纠正错误,但我忽略了这一点。这是一个边缘情况,通常不是一个理想的用例。)
下面使用的@invoke
技巧是一种避免重复计算而不向模块的__dict__
添加任何内容的方法。
invoke = lambda f: f() # trick taken from AJAX frameworks
@invoke
def codepoint_count():
testlength = len(u'\U00010000') # pre-compute once
assert (testlength == 1) or (testlength == 2)
if testlength == 1:
def closure(data): # count function for "wide" interpreter
u'returns the number of Unicode code points in a unicode string'
return len(data.encode('UTF-16BE').decode('UTF-16BE'))
else:
def is_surrogate(c):
ordc = ord(c)
return (ordc >= 55296) and (ordc < 56320)
def closure(data): # count function for "narrow" interpreter
u'returns the number of Unicode code points in a unicode string'
return len(data) - len(filter(is_surrogate, data))
return closure
assert codepoint_count(u'hello \U0001f44d') == 7
assert codepoint_count(u'hello \ud83d\udc4d') == 7