Google App Engine 使用 Python 2.5.2,显然启用了 UCS4。但 GAE 数据存储在内部使用 UTF-8。因此,如果您将 u'\ud834\udd0c'(长度为 2)存储到数据存储区,当您检索它时,您会得到 '\U0001d10c'(长度为 1)。我试图以一种在存储之前和之后给出相同结果的方式来计算字符串中的 unicode 字符数。因此,在我收到字符串后,我会尝试对其进行规范化(从 u'\ud834\udd0c' 到 '\U0001d10c'),然后再计算其长度并将其放入数据存储区。我知道我可以将其编码为 UTF-8,然后再次解码,但有没有更直接/有效的方法?
2 回答
我知道我可以将其编码为 UTF-8,然后再次解码
是的,当您输入“UCS-4 字符串中的 UTF-16 代理”时,这是解决问题的常用习惯用法。但正如机械蜗牛所说,这个输入格式不正确,你应该修复任何产生它的东西。
有没有更直接/有效的方法?
好吧...您可以使用正则表达式手动执行此操作,例如:
re.sub(
u'([\uD800-\uDBFF])([\uDC00-\uDFFF])',
lambda m: unichr((ord(m.group(1))-0xD800<<10)+ord(m.group(2))-0xDC00+0x10000),
s
)
当然不是更直接......我也怀疑它是否真的更有效!
不幸的是,CPython 解释器在 3.3 之前的版本中的行为取决于它是使用“窄”还是“宽”Unicode 支持构建的。因此,相同的代码,例如对 的调用len
,在标准解释器的不同版本中可能会产生不同的结果。有关示例,请参见此问题。
“窄”和“宽”之间的区别在于“窄”解释器在内部存储 16 位代码单元 (UCS-2),而“宽”解释器在内部存储 32 位代码单元 (UCS-4)。代码点U+10000 及以上(基本多语言平面之外)len
在“窄”解释器上具有两个中的一个,因为需要两个 UCS-2 代码单元来表示它们(使用代理),这就是len
衡量标准。在“宽”构建中,非 BMP 代码点只需要一个 UCS-4 代码单元,因此对于此类代码点来说,这些构建是一个。len
我已经确认以下处理所有unicode
字符串,无论它们是否包含代理对,并且适用于 CPython 2.7 窄和宽版本。(可以说,在宽解释器中指定字符串u'\ud83d\udc4d'
反映了将完整代理代码点表示为与部分字符代码单元不同的肯定愿望,因此不会自动纠正错误,但我在这里忽略了这一点。这是一个边缘案例,通常不是理想的用例。)
下面@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