首页 > 解决方案 > glibc 的 isalpha 函数和 en_US.UTF-8 语言环境

问题描述

isalpha简短版本:当语言环境设置为非C(换句话说,类似)时,C 函数如何工作en_US.UTF-8

长版:在阅读一堆关于该isalpha函数的文档时,我并不是 100% 清楚它依赖于语言环境的行为是如何工作的。具体来说,我发现文档说诸如

在某些语言环境中,可能存在 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,当语言环境设置为时,如何知道哪些整数应该返回 trueen_US.UTF-8以及这些整数代表什么。这只是某个范围内某个范围内的 utf 代码点的硬编码列表吗?还是不那么直接的东西?

我试着自己追这个,但我的鸽子-c 不能胜任这项任务。

我达到了ctype.cctype.h。如果我深入研究 glibc 的源代码,我会发现该isalpha函数实际上是一个扩展为类似这样的宏

int isalpha (int c) {
    return __isctype (c, _ISalpha);
}

__isctype 也是一个宏,所以我们扩展它我们有类似的东西

int isalpha (int c) {
    return ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) _ISalpha) (c, _ISalpha);
}

并且_ISalpha枚举扩展到一个小端位掩码,所以现在我们正在研究这样的东西......

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除了了解如何工作之外,我没有特别的目标。

标签: clocaleglibcutf

解决方案


当语言环境设置为非 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_GBwhich has copy "i18n",然后转到i18nwhich has 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更大的未定义行为 then UCHAR_MAX。有些程序员甚至会这样做isalpha((unsigned char)i)is<ctype>(arg)(我记得在某些情况下,当函数参数不是无符号字符时会收到警告)。

这只是某个范围内某个范围内的 utf 代码点的硬编码列表吗?还是不那么直接的东西?

是的,正如上面在/usr/share/i18n/locales/*文件中提到的。

C 语言环境的硬编码列表存储在locale/C-ctype.c中,旨在匹配POSIX


推荐阅读