C 编程/ctype.h
在 C 编程语言的 ANSI C 标准库中,头文件 ctype.h 包含字符分类函数的声明。
早期在 Unix 下使用 C 语言的工具匠们开始以惊人的速度开发习语来将字符分类为不同的类型。例如,在 ASCII 字符集中,以下测试可以识别字母
if ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z')
但是,此习语不一定适用于其他字符集,如 EBCDIC。
很快,程序中就充斥着类似上述测试,甚至更糟,几乎与上述测试相同的测试。程序员可以以多种不同的方式编写相同的习语,这会减慢理解速度并增加出错的机会。
不久后,这些习语就被中的函数取代了<ctype.h>.
与上面的示例不同,字符分类例程不是以比较测试的方式编写的。在大多数 C 库中,它们是以静态表查找的方式编写的,而不是宏或函数。
例如,可以创建一个包含 256 个 8 位整数的数组,并将其排列为位域,其中每个位对应于字符的特定属性,例如 isdigit、isalpha。如果整数的最低有效位对应于 isdigit 属性,则代码可以这样编写
#define isdigit(x) (TABLE[x] & 1)
早期版本的 Linux 使用了类似第一个代码示例的潜在错误方法
#define isdigit(x) ((x) >= '0' && (x) <= '9')
如果以下情况,这可能会导致问题x有副作用——例如,如果调用isdigit(x++)或isdigit(run_some_program()). 不会立即显而易见的是,对isdigit的参数进行了两次求值。出于这个原因,通常使用基于表的方。
<ctype.h> 包含十二个字符分类函数的原型。除了 isdigit
和 isxdigit
之外,所有这些函数都是特定于语言环境的;它们的运行行为可能会随着语言环境的更改而更改。
测试 | 以 int isfunc(int); 的形式对 true 返回非零数,对 false 返回零。 |
---|---|
isalnum |
测试字母数字字符 |
isalpha |
测试字母字符 |
isblank |
测试空白字符(C99 中新增) |
iscntrl |
测试控制字符 |
isdigit |
测试数字。不特定于语言环境。 |
isgraph |
测试图形字符,不包括空格字符。 |
islower |
测试小写字母字符 |
isprint |
测试可打印字符,包括空格字符。 |
ispunct |
测试标点符号字符 |
isspace |
测试任何空白字符 |
isupper |
测试大写字母字符 |
isxdigit |
测试十六进制数字。不特定于语言环境。 |
字符转换 | 以 int tofunc(int); 的形式返回转换后的字符,除非它不是字母。 |
tolower |
将字符转换为小写字母 |
toupper |
将字符转换为大写字母 |
单一 Unix 规范版本 3 添加了类似于上述的函数
isascii |
测试参数是否在 0 到 127 之间 |
toascii |
将字符转换为 ASCII |
标准规定(§7.4-1)
- 在所有情况下,参数都是一个 int,其值应可表示为一个 unsigned char 或等于宏 EOF 的值。如果参数具有任何其他值,则行为未定义。
不幸的是,许多程序员忘记了 char 类型可能是带符号的或无符号的,具体取决于实现。如果 char 类型是有符号的,则从 char 到 int 的隐式转换可能会生成负值,从而导致未定义的行为。这通常意味着如果参数用作查找表的索引,它将访问正确表之外的区域,甚至可能导致程序崩溃。
使用 char 参数的正确方法是在检查 EOF 条件后先将其转换为 unsigned char。
getchar
、getc
和 fgetc
返回的 int
类型值保证在 unsigned char(或 EOF)的范围内,因此在这些情况下不需要转换。