Python 2.7中特定于平台的Unicode语义

Ubuntu 11.10:

$ python Python 2.7.2+ (default, Oct 4 2011, 20:03:08) [GCC 4.6.1] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> x = u'\U0001f44d' >>> len(x) 1 >>> ord(x[0]) 128077 

Windows 7的:

 Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> x = u'\U0001f44d' >>> len(x) 2 >>> ord(x[0]) 55357 

我的Ubuntu的经验是与分配中的默认解释器。 对于Windows 7,我下载并安装了从python.org链接的推荐版本。 我自己也没有编译过。

差异的性质对我来说很清楚。 (在Ubuntu上,string是一系列代码点;在Windows 7上是一系列UTF-16代码单元。)我的问题是:

  • 为什么我观察到这种行为差异? 这是由于解释器是如何构build的,还是依赖系统库的差异?
  • 有没有什么办法可以configurationWindows 7解释器的行为来同意Ubuntu的, 我可以在Eclipse PyDev (我的目标)中做到这一点
  • 如果我必须重build,是否有任何预构build的Windows 7解释器,从可靠的来源performance为Ubuntu上面?
  • 除了手动计数在Windows上的unicodestring中的替代项(blech),是否有任何解决此问题的方法?
  • 这是否certificate一个错误报告? 有没有机会在2.7中解决这个错误报告?

Solutions Collecting From Web of "Python 2.7中特定于平台的Unicode语义"

在Ubuntu上,你有一个“宽”的Python版本 ,其中字符串是UTF-32 / UCS-4。 不幸的是,这还不能用于Windows。

由于对宽字符的请求很少,Windows构建将会缩小一段时间,这些请求主要来自于能够购买他们自己的Python的核心程序员,Windows本身强烈偏向于16位字符。

Python 3.3将具有灵活的字符串表示形式 ,您不需要关心Unicode字符串是使用16位还是32位的代码单元。

在此之前,您可以从UTF-16字符串中获取代码点

 def code_points(text): utf32 = text.encode('UTF-32LE') return struct.unpack('<{}I'.format(len(utf32) // 4), utf32) 

很好的问题! 我最近自己倒了这个兔子洞。

@ dan04的答案启发了我将其扩展为一个unicode子类,它在Python 2和Python 2的构建中都提供了一致的索引,切片和len()

 class WideUnicode(unicode): """String class with consistent indexing, slicing, len() on both narrow and wide Python.""" def __init__(self, *args, **kwargs): super(WideUnicode, self).__init__(*args, **kwargs) # use UTF-32LE to avoid a byte order marker at the beginning of the string self.__utf32le = unicode(self).encode('utf-32le') def __len__(self): return len(self.__utf32le) / 4 def __getitem__(self, key): length = len(self) if isinstance(key, int): if key >= length: raise IndexError() key = slice(key, key + 1) if key.stop is None: key.stop = length assert key.step is None return WideUnicode(self.__utf32le[key.start * 4:key.stop * 4] .decode('utf-32le')) def __getslice__(self, i, j): return self.__getitem__(slice(i, j)) 

这里开源 ,公共领域。 示例用法:

 text = WideUnicode(obj.text) for tag in obj.tags: text = WideUnicode(text[:start] + tag.text + text[end:]) 

( 从这个用法简化。 )

谢谢@ dan04!

我主要需要准确地测试长度。 因此,这个函数正确地返回任何unicode字符串的代码点长度,无论解释器是狭窄的还是宽的。 如果数据在广泛构建的解释器中使用两个代理文字而不是单个\U型代码点,则只要代理“正确”使用,返回的代码点长度就可以解决这个问题,即作为一个狭义的解释器会使用它们。

 invoke = lambda f: f() # trick borrowed from Node.js @invoke def ulen(): testlength = len(u'\U00010000') assert (testlength == 1) or (testlength == 2) if testlength == 1: # "wide" interpreters def closure(data): u'returns the number of Unicode code points in a unicode string' return len(data.encode('UTF-16BE').decode('UTF-16BE')) else: # "narrow" interpreters def filt(c): ordc = ord(c) return (ordc >= 55296) and (ordc < 56320) def closure(data): u'returns the number of Unicode code points in a unicode string' return len(data) - len(filter(filt, data)) return closure # ulen() body is therefore different on narrow vs wide builds 

测试案例,传递狭窄和广泛的构建:

 class TestUlen(TestCase): def test_ulen(self): self.assertEquals(ulen(u'\ud83d\udc4d'), 1) self.assertEquals(ulen(u'\U0001F44D'), 1)