【发布时间】:2021-04-07 10:12:27
【问题描述】:
简短版:当语言环境设置为 C 以外的其他值(换句话说,类似于 en_US.UTF-8)时,C 函数 isalpha 如何工作?
长版:在阅读大量关于 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 代码点的硬编码列表吗?还是不那么直接的东西?
我试着自己追这个,但我的 pigeon-c 不能胜任这项任务。
我到了ctype.c 和ctype.h。如果我深入研究 glibc 的源代码,我会看到 isalpha 函数是 actually a macro 扩展为类似这样的东西
int isalpha (int c) {
return __isctype (c, _ISalpha);
}
__isctypeis 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 的工作原理之外,我没有特别的目标。
【问题讨论】:
-
isalpha必须在unsigned char值上调用。这只是未定义的行为。 -
实际上,是的,它是一个硬编码列表 - 或者更确切地说是一个硬编码查找表。该字符用作特定于该语言环境的查找表的索引,并且相应条目的一位用于指示该字符是否为
alpha。库使用内部__ctype_b_loc()函数来获取指向当前语言环境的正确表的指针。 -
但请注意,将语言环境设置为
en_US.UTF-8不会神奇地使事物变成 UTF-8。isalpha()继续对单字节字符进行操作。为了处理多字节字符,有一整套单独的“宽字符”函数和类型,以及相应的iswalpha()函数。iswalpha()可能有一个更复杂的实现,因为查找表会非常大,但在某种程度上仍然有一个硬编码列表。 -
因此,正如 Kamil 所说,通过在超出
unsigned char范围的 int 上调用isalpha()(加上EOF),您可能会导致它在外部读取该 257 条目 (*) 查找表的边界,可能会崩溃或至少读取垃圾。 -
谢谢@NateEldredge -- 你是否知道这些查找表最终来自哪里(在 glibc 中?在其他地方?)?