调用时 Unicode 字符不在范围内 locale.strxfrm
Unicode character not in range when calling locale.strxfrm
我在使用带有 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 上复制。在评论中听到其他 OS 可能会有所启发。
谁能解释一下幕后情况?
在Python3.x中,函数locale.strxfrm(s)
internally uses the POSIX C function wcsxfrm()是基于当前的LC_COLLATE设置。 POSIX 标准以这种方式定义转换:
The transformation shall be such that if wcscmp()
is applied to two
transformed wide strings, it shall return a value greater than, equal
to, or less than 0, corresponding to the result of wcscoll()
applied
to the same two original wide-character strings.
这个定义可以通过多种方式实现,甚至不需要生成的字符串是可读的。
我创建了一个小的 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 定义是 link 到 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 的 sources 中找到了实际定义。文件 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 字符。
我在使用带有 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 上复制。在评论中听到其他 OS 可能会有所启发。
谁能解释一下幕后情况?
在Python3.x中,函数locale.strxfrm(s)
internally uses the POSIX C function wcsxfrm()是基于当前的LC_COLLATE设置。 POSIX 标准以这种方式定义转换:
The transformation shall be such that if
wcscmp()
is applied to two transformed wide strings, it shall return a value greater than, equal to, or less than 0, corresponding to the result ofwcscoll()
applied to the same two original wide-character strings.
这个定义可以通过多种方式实现,甚至不需要生成的字符串是可读的。
我创建了一个小的 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 定义是 link 到 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 的 sources 中找到了实际定义。文件 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 字符。