c - C如何大写字母?
问题描述
我在以下代码中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
具体,我只想知道所有 ASCII 字符(从 NUL 到 DEL 中的所有字符)在 C 中是如何大写的。
解决方案
“C”不将字符转换为大写。C 标准仅要求标准库中有一个函数根据当前语言环境正确执行,并且它在“C”语言环境中以特定方式执行此操作(这是唯一保证存在的语言环境)。
库实现可以按照实现者认为合适的方式自由地完成该任务,并且它们都以不同的方式完成。甚至完全不同的方式。一些 C 库不支持具有 ASCII 字符集的“C”语言环境以外的语言环境。这种 C 库的一个例子是musl,它的实现非常简单:
int toupper(int c)
{
if (islower(c)) return c & 0x5f;
return c;
}
如您所见,上面的代码依赖于islower
. 这里是:
int islower(int c)
{
return (unsigned)c-'a' < 26;
}
由于对 的调用islower
,toupper
返回小写字符范围之外的任何参数不变,即使参数不在 toupper 的有效范围内。由于标准没有定义toupper
有效范围之外的参数的行为(本质上是可能由返回的值fgetc
),所以只返回无效参数不变肯定与任何其他行为一样可以接受。Glibc 的toupper
函数通常会在无效参数上出现段错误,因为它使用参数作为数组的索引(如您在引用的代码中所见)。根据标准,这种行为也是可以接受的。
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 编码字符串一起使用,那么您将最终将负数传递给可能不期望它们的函数。
推荐阅读
- java - 检测列表项是否以某种语言编写
- c# - dataGridView 不显示列表
- ios - 如何使用 .monospaced 系统字体设计
- java - 我怎么知道将 sourceCompatibility 与 targetCompatibility 设置为什么?
- javascript - TypeError: svg.append(...).attrs 不是使用 D3.js 创建图表时的函数
- git - Github 无法访问... gnutls_handshake() failed:Close notify
- java - Postgres UUID 和 Hibernate → 未找到列
- python - RDF 主题的属性
- android - 我在哪里可以找到 ObjectBox 中的 Data.mdb?
- javascript - 如何调试 Angular 错误 - 错误:未捕获(承诺中):TypeError:无法读取 null 的属性“数据”