问题描述
简短版本:当语言环境设置为除 isalpha
以外的其他内容(换句话说,类似于 C
)时,C 函数 en_US.UTF-8
如何工作?
长版:在阅读一堆关于 isalpha
函数的文档时,我不是 100% 清楚它的语言环境相关行为是如何工作的。具体来说,我找到了文档 that say things like
在某些语言环境中,可能会有额外的 isalpha 为真的字符——既不是大写也不是小写的字母。但是在标准的“C”语言环境中,没有这样的附加字符。
此外——如果我用一个小的 C 程序测试这个,我可以确认 isalpha
将返回 true
/1
对于传统 ASCII 文本范围之外的值,当设置了不同的语言环境——对于某些unix。这个程序在我基于 BSD/Darwin 的 mac 上似乎做的很合理——但是当我在 ubuntu 机器上尝试它时它出现了段错误。
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
#include <limits.h>
int main() {
setlocale(LC_ALL,"en_US.UTF-8");
for(int i=0;i<INT_MAX;i++) {
// printf() displays the string inside quotation
if(isalpha(i)) {
printf("is alpha numeric: %i\n",i);
}
}
return 0;
}
我不清楚的是,当语言环境设置为 isalpha
时,en_US.UTF-8
如何知道哪些整数应该返回 true 以及这些整数代表什么。这只是某个范围内的 utf 代码点的硬编码列表吗?或者不那么直接的东西?
我自己尝试过解决这个问题,但我的鸽子-c 无法胜任。
我已经达到了 ctype.c
和 ctype.h
。如果我深入研究 glibc 的源代码,我会发现 isalpha
函数是 actually a macro,它扩展为类似这样的内容
int isalpha (int c) {
return __isctype (c,_ISalpha);
}
__isctype
is also a macro,因此我们将其扩展为类似
int isalpha (int c) {
return ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) _ISalpha) (c,_ISalpha);
}
还有 _ISalpha
enum expands out 到一个小端位掩码,所以现在我们正在研究这样的东西......
int isalpha (int c) {
return ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) ((2) < 8 ? ((1 << (2)) << 8) : ((1 << (2)) >> 8))) (c,((2) < 8 ? ((1 << (2)) << 8) : ((1 << (2)) >> 8)));
}
这就是我挖掘的地方。
除了了解 isalpha
的工作原理外,我没有特别的目标。
解决方法
当语言环境设置为 C 以外的其他内容(换句话说,类似于 en_US.UTF-8)时,C 函数 isalpha 如何工作?
Unicode 的前 128 个字符表示与 ASCII 相同,因此没有任何变化(当 C 语言环境使用 ASCII 时)。
真正改变的是,glibc 不再使用硬编码列表,而是打开并加载语言环境文件。我相信这将来自 /usr/lib/locale/locale-archive
,它应该包含来自 /usr/share/i18n/locales/*
文件的编译语言环境。在我的 /usr/share/i18n/locales/en_US
文件中,我看到 LC_CTYPE copy "en_GB"
,我可以去 en_GB
有 copy "i18n"
,然后到 i18n
有 copy "i18n_ctype"
,最后到 i18n_ctype
文件,其中包含:
% The "alpha" class of the "i18n" FDCC-set is reflecting
% the recommendations in TR 10176 annex A
alpha /
<U0041>..<U005A>;<U0061>..<U007A>;<U00AA>;<U00B5>;<U00BA>;/
<U00C0>..<U00D6>;<U00D8>..<U00F6>;<U00F8>..<U02C1>;<U02C6>..<U02D1>;/
.... many more lines ....
我可以确认 isalpha 将为传统 ASCII 文本范围之外的值返回 true/1
来自C99 7.4p1:
在所有情况下,参数都是 int,其值应表示为无符号字符或应等于宏 EOF 的值。如果参数有任何其他值,则行为未定义。
循环:for(int i=0;i<INT_MAX;i++) { if(isalpha(i)) {
只是任何 i
大于 UCHAR_MAX
的未定义行为。有些程序员甚至会做isalpha((unsigned char)i)
。 (我记得在某些情况下当 is<ctype>(arg)
函数参数不是无符号字符时收到警告)。
这只是某个范围内的 utf 代码点的硬编码列表吗?或者不那么直接的东西?
是的,如上文 /usr/share/i18n/locales/*
文件中所述。
C 语言环境的硬编码列表存储在 locale/C-ctype.c 中,旨在匹配 POSIX。