关于C标准库中的ctype.h(阅读《The Standard C Library》)
nrush@2018/10/18
个人学习笔记,若有错误,欢迎交流指正。
1.ctype.h的目的
ctype.h的主要功能是提供对字符进行分类和两个映射函数。
2.字符集相关知识
字符集的种类很多,ASCII仅仅是一种字符集,对于不同地区和用途,字符集无论是数目还是含义的差异都很大,对于C来说其规定了一套基础的标准字符,即这些字符在任何字符集中都应该存在且有相同的含义和功能。这些标准字符包括以下这些:
字母:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ
数字:0123456789abcdefABCDEF(后面的字母仅在16进制数字中出现)
标点:!#%&|/*±?:’";.^<>=
标准运动控制符:\r(回车CR) \n(换行NL) \t(水平制表HT) \v(垂直制表VT) \f(翻页FF)
控制符:\b(退格BS) \a(报警BEL)
空格:" "
备注:在显示字符中标准运动控制字符也被统一认为是空格符即isspace(标准运动控制符)返回为真。
3.ctype.h的函数及源码分析
3.1 函数
函数名 | 功能 |
---|---|
int isalnum(int c) | 判断是否为数字或字母 |
int isalpha(int c) | 判断是否是字母 |
int iscntrl(int c) | 判断是否为控制字符 |
int isdigit(int c) | 判断是否为数字 |
int isgraph(int c) | 判断是否为打印可显示字符 |
int islower(int c) | 判断是否为小写 |
int isprint(int c) | 判断是否为打印仅占一个显示位的字符 |
int ispunct(int c) | 判断是否位标点 |
int isspace(int c) | 判断是否为空格 |
int isupper(int c) | 判断是否为大写 |
int isxdigit(int c) | 判断是否为16进制数 |
int tolower(int c) | 转化为小写 |
int toupper(int c) | 转化为大写 |
以下是各判断函数的关系图。(摘自《The Standard C Library》)
3.2 实现分析
源码实现方式是使用转换表对判断字符的类型,对于不同的字符集应当加载不同的转换表。
以下为一种ctype.h的实现方式
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CTYPE_H
#define _LINUX_CTYPE_H
/*
* NOTE! This ctype does not handle EOF like the standard C
* library is required to.
*/
#define _U 0x01 /* upper */
#define _L 0x02 /* lower */
#define _D 0x04 /* digit */
#define _C 0x08 /* cntrl */
#define _P 0x10 /* punct */
#define _S 0x20 /* white space (space/lf/tab) */
#define _X 0x40 /* hex digit */
#define _SP 0x80 /* hard space (0x20) */
extern const unsigned char _ctype[];
#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
static inline int isdigit(int c)
{
return '0' <= c && c <= '9';
}
#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
#define islower(c) ((__ismask(c)&(_L)) != 0)
#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
#define ispunct(c) ((__ismask(c)&(_P)) != 0)
/* Note: isspace() must return false for %NUL-terminator */
#define isspace(c) ((__ismask(c)&(_S)) != 0)
#define isupper(c) ((__ismask(c)&(_U)) != 0)
#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)
#define isascii(c) (((unsigned char)(c))<=0x7f)
#define toascii(c) (((unsigned char)(c))&0x7f)
static inline unsigned char __tolower(unsigned char c)
{
if (isupper(c))
c -= 'A'-'a';
return c;
}
static inline unsigned char __toupper(unsigned char c)
{
if (islower(c))
c -= 'a'-'A';
return c;
}
#define tolower(c) __tolower(c)
#define toupper(c) __toupper(c)
/*
* Fast implementation of tolower() for internal usage. Do not use in your
* code.
*/
static inline char _tolower(const char c)
{
return c | 0x20;
}
/* Fast check for octal digit */
static inline int isodigit(const char c)
{
return c >= '0' && c <= '7';
}
#endif
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* linux/lib/ctype.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/ctype.h>
#include <linux/compiler.h>
#include <linux/export.h>
const unsigned char _ctype[] = {
_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
EXPORT_SYMBOL(_ctype);
4.其它
- 对标准库的理解,所谓的标准库实际上是一套实现特定功能并能稳定运行的库,其具体实现原理未必是相同的。
- inline关键字被称为内联函数,使用该关键字的作用是为了提高效率。