C 编程/ctype.h
在 C 编程语言的 ANSI C 标准库中,标题 ctype.h 包含字符分类函数的声明。
早期的 C 语言工具开发者在 Unix 下开始快速开发习语来将字符分类成不同的类型。例如,在 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); 对于真返回非零数,对于假返回零。 |
---|---|
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)的范围内,因此在这些情况下不需要强制转换。