繁体   English   中英

C 大写字母怎么办?

[英]How does C uppercase letters?

glibc-2.33/ctype/ctype.c看到了这段代码:

// [...]

#define __ctype_toupper \
  ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128)

// [...]

int
toupper (int c)
{
  return c >= -128 && c < 256 ? __ctype_toupper[c] : c;
}
libc_hidden_def (toupper)

我知道它正在检查c是否在 -128 和 256(含)范围内,如果字符超出该范围,则按原样返回,但是_NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128)是什么意思,我在哪里实际找到源字母如何大写的代码? 这似乎是在查找当前的语言环境,我只对en_US.UTF-8感兴趣。 另外,一个角色怎么可能是负面的?

我并不特别关心glibc ,我只想知道 C 中的所有 ASCII 字符(从 NUL 到 DEL 中的所有字符)是如何大写的。

“C”不将字符转换为大写。 C 标准仅要求标准库中有一个 function 根据当前语言环境正确执行,并且它在“C”语言环境中以特定方式执行(这是保证存在的唯一语言环境)。

库实现可以按照实现者认为合适的方式自由地完成该任务,并且它们都以不同的方式完成。 甚至完全不同的方式。 一些 C 库不支持具有 ASCII 字符集的“C”语言环境以外的语言环境。 musl 就是这样一个C库的示例,它的实现非常简单:

int toupper(int c)
{
        if (islower(c)) return c & 0x5f;
        return c;
}

如您所见,上面的代码依赖于islower 这里是:

int islower(int c)
{
        return (unsigned)c-'a' < 26;
}

由于对islower的调用, toupper返回小写字符范围之外的任何参数不变,即使 arguments 不在 toupper 的有效范围内。 由于该标准没有定义有效范围之外的 arguments 的toupper行为(基本上可能由fgetc返回的值),因此仅返回无效的 arguments 不变肯定与任何其他行为一样可以接受。 Glibc 的toupper function 经常会在无效的 arguments 上出现段错误,因为它使用参数作为数组的索引(如您在引用的代码中所见)。 根据标准,这种行为也是可以接受的。

Glibc 的实现要复杂得多。 在幕后,它依赖于从语言环境定义文件编译的语言环境数据,这个过程完全在 C 标准之外,并且在某种程度上由 Posix 标准定义(尽管 GNU 实现在某种程度上与 Posix 不同)。

但这里是独家新闻:如果您在 UTF-8 语言环境中使用单字节字符,那么 glibc 的复杂代码都不会产生丝毫差异。 musl 实现完全按照 UTF-8 语言环境的要求工作,因为在单字节 UTF-8 表示中唯一可表示的字母字符是“罗马”字母表中的 52 个字符。 所有其他 Unicode 字符只能以宽字符和多字节序列表示。

此外,使用 UTF-8 以外的单字节编码的环境越来越少。 我们当中肯定有很多人不得不学习这些东西,因为我们的程序运行在使用不同ISO-8859-x 代码页的各种平台上。 或不同的单字节 Windows 代码页。 但最终,Unicode 胜出。 (我们中的许多人都松了一口气。)因此,除了在遗留环境中,大部分设备不再真正需要。

但这并不是说 Unicode 神奇地解决了管理世界上使用的大量字母表所涉及的所有复杂问题。 离得很远。 Unicode 所做的有两个方面:它阐明了复杂性是什么(其中大部分未被 C/Posix 语言环境捕获),它提供了一些实现的基本标准。

并且,作为副作用,UTF-8 将单字节代码标准化为基本符合原始 ASCII 7 位标准。 因此,如果您只处理 7 位字符(如今,这可能不太理想),那么除了 musl 风格的实现之外,您不需要任何东西。 如果您正在处理“世界上所有的字符集”,您将寻找一个实际上符合 Unicode 并且使用char以外的其他东西来表示字符的库。

但遗憾的是,一个并发症将永远存在:C 并未标准化char的签名这一事实。 在签署char的平台上(Unix X86 和 Windows,对于两个主要示例), (char)0xA0是 (a) 未指定和 (b) 可能是 -96,这是单字节 0xA0 在 2 的补码中表示的内容。 因此,如果您编写的代码使用ctype.h中的各种函数并且不处理负char值,然后您尝试将该代码与 UTF-8 编码字符串一起使用,该字符串包含单字节域之外的字符,然后您最终会将负数传递给可能不期望它们的函数。

如果你 go 回到根目录并寻找_NL_CTYPE_TOUPPER你会找到一个提交的地方

[..] (ctype_output):支持备用语言环境格式:nelems 变化的计算。 _NL_CTYPE_TOUPPER32 [...]

所以基本上 _NL_CTYPE_TOUPPER 是 _NL_CTYPE_TOUPPER(8bits) 的宏,例如在法语中你有À作为à的大写版本

在此链接之后,您将找到 header 文件langinfo.h ,该文件的枚举从第 43 行开始, _NL_CTYPE_TOUPPER在第 259 行定义。

LC_CTYPE 类别:字符分类。 256 此信息由 <ctype.h> 中的函数访问。

LC_CTYPE 是为每种语言定义的,例如法语

fr_FR:2000"

请注意,调用此 function 并没有多大意义,因为 ASCII 表中不包含带重音的字符,但由于此 function 是同时处理 utf8 和 ascii 的字符,因此它是这样工作的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM