调用 locale.strxfrm 时 Unicode 字符不在范围内
回答问题
在使用带有 unicode 输入的locale
库时,我遇到了一个奇怪的行为。下面是一个最小的工作示例:
>>> x = '\U0010fefd'
>>> ord(x)
1113853
>>> ord('\U0010fefd') == 0X10fefd
True
>>> ord(x) <= 0X10ffff
True
>>> import locale
>>> locale.strxfrm(x)
'\U0010fefd'
>>> locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
'en_US.UTF-8'
>>> locale.strxfrm(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: character U+110000 is not in range [U+0000; U+10ffff]
我在 Python 3.3、3.4 和 3.5 上看到了这一点。我在 Python 2.7 上没有收到错误消息。
据我所见,我的 unicode 输入在适当的 unicode 范围内,因此在使用“en_US.UTF-8”时,strxfrm
内部的某些东西似乎将输入移出范围。
我正在运行 Mac OS X,这种行为可能与http://bugs.python.org/issue23195...但我的印象是这个错误只会表现为不正确的结果,而不是引发的异常.我无法在我的 SLES 11 机器上进行复制,而其他人确认他们无法在 Ubuntu、Centos 或 Windows 上进行复制。在评论中听到其他操作系统可能会很有启发性。
有人可以解释一下这里可能发生的事情吗?
Answers
在 Python 3.x 中,函数locale.strxfrm(s)
在内部使用 POSIX C 函数wcsxfrm(),它基于当前的 LC_COLLATE 设置。 POSIX 标准以这种方式定义转换:
转换应使得如果
wcscmp()
应用于两个转换后的宽字符串,它应返回一个大于、等于或小于 0 的值,对应于将wcscoll()
应用于相同的两个原始宽字符串的结果。
这个定义可以用多种方式实现,甚至不需要结果字符串是可读的。
我创建了一个小 C 代码示例来演示它是如何工作的:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main() {
wchar_t buf[10];
wchar_t *in = L"\x10fefd";
int i;
setlocale(LC_COLLATE, "en_US.UTF-8");
printf("in : ");
for(i=0;i<10 && in[i];i++)
printf(" 0x%x", in[i]);
printf("\n");
i = wcsxfrm(buf, in, 10);
printf("out: ");
for(i=0;i<10 && buf[i];i++)
printf(" 0x%x", buf[i]);
printf("\n");
}
它在转换前后打印字符串。
在 Linux (Debian Jessie) 上运行它,结果如下:
in : 0x10fefd
out: 0x1 0x1 0x1 0x1 0x552
在 OSX (10.11.1) 上运行它时,结果是:
in : 0x10fefd
out: 0x103 0x1 0x110000
您可以看到 OSX 上wcsxfrm()
的输出包含 Python 字符串中不允许的字符 U+110000,因此这是错误的根源。
在 Python 2.7 上,不会引发错误,因为它的locale.strxfrm()
实现基于strxfrm()
C 函数。
更新:
进一步调查,我发现 OSX 上 en_US.UTF-8 的 LC_COLLATE 定义是 la_LN.US-ASCII 定义的链接。
$ ls -l /usr/share/locale/en_US.UTF-8/LC_COLLATE
lrwxr-xr-x 1 root wheel 28 Oct 1 14:24 /usr/share/locale/en_US.UTF-8/LC_COLLATE -> ../la_LN.US-ASCII/LC_COLLATE
我在 Apple 的来源中找到了实际定义。la_LN.US-ASCII.src
文件内容如下:
order \
\x00;...;\xff
第二次更新:
我在 OSX 上进一步测试了wcsxfrm()
功能。使用 la_LN.US-ASCII 排序规则,给定一个宽字符序列C1..Cn
作为输入,输出是一个具有以下形式的字符串:
W1..Wn \x01 U1..Un
在哪里
Wx = 0x103 if Cx > 0xFF else Cx+0x3
Ux = Cx+0x103 if Cx > 0xFF else Cx+0x3
使用这个算法\x10fefd
变成0x103 0x1 0x110000
我已经检查过,并且每个 UTF-8 语言环境在 OSX 上都使用此整理,所以我倾向于说 Apple 系统上对 UTF-8 的整理支持已损坏。得到的排序与正常字节比较得到的排序几乎相同,但能够获得非法 Unicode 字符。
更多推荐
所有评论(0)