2011-08-03 58 views
8

Google App Engine使用Python 2.5.2,显然在启用UCS4的情况下。但GAE数据存储在内部使用UTF-8。所以,如果你存储U '\ ud834 \ udd0c'(长2)到数据存储,当你找回它,你会得到 '\ U0001d10c'(长度为1)。我试图在存储它之前和之后给出相同结果的方式来计算字符串中的Unicode字符数。所以我想,我接受它,计算它的长度,并把它的数据存储之前,尽快恢复正常的字符串(从u“\ ud834 \ udd0c”到“\ U0001d10c”)。我知道我可以将它编码为UTF-8,然后再解码,但有没有更直接/更高效的方法?如何在Python中获得可靠的unicode字符数?

+4

不要试图存储'u'\ ud834 \ udd0c''。代理不是有效的unicode代码点,所以你不应该依赖它们被保存在字符串中,或​​长度正常工作。 –

+1

澄清:Python中的u'blah''概念上代表了一系列Unicode代码点。所以你不应该把UTF-16的二进制表示放入它们中。 –

+0

如果您从您的问题中删除了关于编码和解码的问题,并将其添加为答案,我可能会赞成它,因为我认为这是获得您要求的最正确的方法。 – SingleNegationElimination

回答

4

我知道我可以只是将其编码成UTF-8,然后再进行解码

是的,这是通常的成语来修复该问题,当你在有“UTF-16替代品UCS-4字符串“输入。但随着机械蜗牛说,该输入畸形,你应该可以固定在任何偏好产生了。

有没有更简单的/有效的方式?

嗯......你可以用正则表达式做手工,如:

re.sub(
    u'([\uD800-\uDBFF])([\uDC00-\uDFFF])', 
    lambda m: unichr((ord(m.group(1))-0xD800<<10)+ord(m.group(2))-0xDC00+0x10000), 
    s 
) 

当然不是更简单...我也有我的怀疑是否它实际上更高效!

+0

谢谢。我的输入来自包含代理代码单元的yaml流,并且修正流中不包含替代项似乎阻止它们在unicode中显示。所以我会考虑修复这个流。 – Travis

+1

什么编码的YAML?如果是UTF-16那些代理*应该被转换为单个Unicode字符......如果它是UTF-8,那么制片人就犯了一个错误。 (UTF-8-with-surrogates被称为'CESU-8',不应该被使用。) – bobince

+0

我很确定这个正则表达式不会更高效。 –

2

不幸的是,早于3.3版本的CPython解释器的行为取决于它是用“窄”还是“宽”Unicode支持构建的。因此,相同的代码(例如致电len)在标准解释器的不同版本中可能会有不同的结果。示例请参见this question。 “窄”解释器在内部存储16位代码单元(UCS-2),而“宽”解释器在内部存储32位代码单元(UCS-4) 。代码 U + 10000及以上(基础多语言平面以外)在“狭义”口译人员上有两个len,因为需要两个UCS-2代码单元来表示它们(使用代理),这就是len措施。上的“宽”构建仅单个UCS-4代码单元需要非BMP代码,所以对于那些构建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 
相关问题