首页 > 解决方案 > 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 中是如何大写的。

标签: cglibc

解决方案


“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;
}

由于对 的调用islowertoupper返回小写字符范围之外的任何参数不变,即使参数不在 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 编码字符串一起使用,那么您将最终将负数传递给可能不期望它们的函数。


推荐阅读