写点什么

C++ 学习 ------cctype 头文件的作用与源码学习

作者:桑榆
  • 2022 年 9 月 02 日
    广东
  • 本文字数:4580 字

    阅读完需:约 15 分钟

引言

cctype 是 C++对 ctype.h 头文件的封装,这个文件里面定义了一系列字符识别和转换函数,我们一起来看看它们的作用和具体实现。

cctype 头文件

源码位置

http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/include/ctype.h

36  #include <sys/cdefs.h>37  38  #define __BIONIC_CTYPE_INLINE static __inline39  #include <bits/ctype_inlines.h>
复制代码

跳转到http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/include/bits/ctype_inlines.h

字符识别函数

isalnum---识别字符是否是字母或数字

66  /** Returns true if `ch` is in `[A-Za-z0-9]`. */67  __BIONIC_CTYPE_INLINE int isalnum(int __ch) {68    // `isalnum(c)` is `isalpha(c) || isdigit(c)`, but there's no obvious way69    // to simplify that, and the table lookup is just slightly faster...70    // Note that this is unsafe for inputs less than -1 (EOF) or greater than71    // 0xff. This is true of other C libraries too.72    return (_ctype_[__ch + 1] & (_CTYPE_U|_CTYPE_L|_CTYPE_N));73  }
复制代码

这里使用的是查表的方式进行判断,我们知道 A-Z 是 0x41-0x5A,a-z 是 0x61-0x7A,0-9 是 0x30-0x39。

38  /** Internal implementation detail. Do not use. */39  #define _CTYPE_U 0x0140  /** Internal implementation detail. Do not use. */41  #define _CTYPE_L 0x0242  /** Internal implementation detail. Do not use. */43  #define _CTYPE_D 0x0444  /** Internal implementation detail. Do not use. */45  #define _CTYPE_S 0x0846  /** Internal implementation detail. Do not use. */47  #define _CTYPE_P 0x1048  /** Internal implementation detail. Do not use. */49  #define _CTYPE_C 0x2050  /** Internal implementation detail. Do not use. */51  #define _CTYPE_X 0x4052  /** Internal implementation detail. Do not use. */53  #define _CTYPE_B 0x8054  /** Internal implementation detail. Do not use. */55  #define _CTYPE_R (_CTYPE_P|_CTYPE_U|_CTYPE_L|_CTYPE_D|_CTYPE_B)56  /** Internal implementation detail. Do not use. */57  #define _CTYPE_A (_CTYPE_L|_CTYPE_U)58  /** Internal implementation detail. Do not use. */59  #define _CTYPE_N _CTYPE_D
复制代码

_CTYPE_U = 0x01,_CTYPE_L = 0x02,_CTYPE_N = 0x04,即第 0 位为 1,第一位为 1,第二位为 1。 我们再来看看_ctype_表

http://www.aospxref.com/android-12.0.0_r3/xref/bionic/libc/upstream-openbsd/lib/libc/gen/ctype_.c

39  const char _C_ctype_[1 + CTYPE_NUM_CHARS] = {40  	0,41  	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,42  	_C,	_C|_S,	_C|_S,	_C|_S,	_C|_S,	_C|_S,	_C,	_C,43  	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,44  	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,45     _S|(char)_B,	_P,	_P,	_P,	_P,	_P,	_P,	_P,46  	_P,	_P,	_P,	_P,	_P,	_P,	_P,	_P,//下一行第一个index为49,开始数字,_N标记47  	_N,	_N,	_N,	_N,	_N,	_N,	_N,	_N,48  	_N,	_N,	_P,	_P,	_P,	_P,	_P,	_P,//下一行第二个index为66,开始大写写字母,_U标记49  	_P,	_U|_X,	_U|_X,	_U|_X,	_U|_X,	_U|_X,	_U|_X,	_U,50  	_U,	_U,	_U,	_U,	_U,	_U,	_U,	_U,51  	_U,	_U,	_U,	_U,	_U,	_U,	_U,	_U,52  	_U,	_U,	_U,	_P,	_P,	_P,	_P,	_P,//下一行第二个index为90,开始小写字母,_L标记53  	_P,	_L|_X,	_L|_X,	_L|_X,	_L|_X,	_L|_X,	_L|_X,	_L,54  	_L,	_L,	_L,	_L,	_L,	_L,	_L,	_L,55  	_L,	_L,	_L,	_L,	_L,	_L,	_L,	_L,56  	_L,	_L,	_L,	_P,	_P,	_P,	_P,	_C,57  58  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* 80 */59  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* 88 */60  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* 90 */61  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* 98 */62  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* A0 */63  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* A8 */64  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* B0 */65  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* B8 */66  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* C0 */67  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* C8 */68  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* D0 */69  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* D8 */70  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* E0 */71  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* E8 */72  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0, /* F0 */73  	 0,	 0,	 0,	 0,	 0,	 0,	 0,	 0  /* F8 */74  };75  76  const char *_ctype_ = _C_ctype_;
复制代码

对应字母,数字的位置,进行了标记,由于跳过了第 0 位,所以检测时加一即可。

isalpha---识别字符是否是字母

这里的逻辑很简单,判断输入值是否在 A-Z 或 a-z 之间即可

75  /** Returns true if `ch` is in `[A-Za-z]`. */76  __BIONIC_CTYPE_INLINE int isalpha(int __ch) {77    return (__ch >= 'A' && __ch <= 'Z') || (__ch >= 'a' && __ch <= 'z');78  }
复制代码

isblank---识别字符是否是空白

判断输入值是否是' '或者制表符'\t'即可

80  /** Returns true if `ch` is a space or tab. */81  __BIONIC_CTYPE_INLINE int isblank(int __ch) {82    return __ch == ' ' || __ch == '\t';83  }
复制代码

iscntrl---识别字符是否是控制字符

根据 ASCII 码表,0x00-0x1f,0x7f,是控制字符,0x32 是空格符' ',所以值小于空格符,或者等于 0x7f 都是控制字符。

85  /** Returns true if `ch` is a control character (any character before space, plus DEL). */86  __BIONIC_CTYPE_INLINE int iscntrl(int __ch) {87    return (__BIONIC_CAST(static_cast, unsigned, __ch) < ' ') || __ch == 0x7f;88  }
复制代码


57  #if defined(__cplusplus)58  #define __BIONIC_CAST(_k,_t,_v) (_k<_t>(_v))59  #else60  #define __BIONIC_CAST(_k,_t,_v) ((_t) (_v))61  #endif
复制代码

__BIONIC_CAST的作用是转换为static_cast<unsigned>(__ch),避免int出现越界判断。

isdigit---识别字符是否是数字

判断输入值是否在数字之间即可

90  /** Returns true if `ch` is in `[0-9]`. */91  __BIONIC_CTYPE_INLINE int isdigit(int __ch) {92    return (__ch >= '0' && __ch <= '9');93  }
复制代码

isgraph---识别是否是有图形的

从 0x21 的'!'到最后的 0x7e'~'都是有图形的

95  /** Returns true if `ch` is `[A-Za-z0-9]` or punctuation. */96  __BIONIC_CTYPE_INLINE int isgraph(int __ch) {97    return (__ch >= '!' && __ch <= '~');98  }
复制代码

islower---识别是否是小写字母字符

判断输入值在'a'-'z'之间即可

100  /** Returns true if `ch` is in `[a-z]`. */101  __BIONIC_CTYPE_INLINE int islower(int __ch) {102    return (__ch >= 'a' && __ch <= 'z');103  }
复制代码

isprint---识别是否是可打印的

从 0x20 的' '到最后的 0x7e'~'都是可打印的

105  /** Returns true if `ch` is `[A-Za-z0-9]` or punctuation or space. */106  __BIONIC_CTYPE_INLINE int isprint(int __ch) {107    return (__ch >= ' ' && __ch <= '~');108  }
复制代码

ispunct---识别是否是标点符号

这里的判断方式与 isalnum 相似,通过查表获得

110  /** Returns true if `ch` is punctuation. */111  __BIONIC_CTYPE_INLINE int ispunct(int __ch) {112    // `ispunct(c)` is `isgraph(c) && !isalnum(c)`, but there's no obvious way113    // to simplify that, and the table lookup is just slightly faster...114    // Note that this is unsafe for inputs less than -1 (EOF) or greater than115    // 0xff. This is true of other C libraries too.116    return (_ctype_[__ch + 1] & _CTYPE_P);117  }
复制代码

isspace---识别是否是空格

只有以下字符为空格

  • ' '(0x20)space (SPC)

  • '\t'(0x09)horizontal tab (TAB)

  • '\n'(0x0a)newline (LF)

  • '\v'(0x0b)vertical tab (VT)

  • '\f'(0x0c)feed (FF)

  • '\r'(0x0d)carriage return (CR)

119  /** Returns true if `ch` is in `[ \f\n\r\t\v]`. */120  __BIONIC_CTYPE_INLINE int isspace(int __ch) {121    return __ch == ' ' || (__ch >= '\t' && __ch <= '\r');122  }
复制代码

isupper---识别是否是大写字母

判断输入值在'A'-'Z'之间即可

124  /** Returns true if `ch` is in `[A-Z]`. */125  __BIONIC_CTYPE_INLINE int isupper(int __ch) {126    return (__ch >= 'A' && __ch <= 'Z');127  }
复制代码

isxdigit---识别是否是十六进制数字

判断输入值是否满足[0-9A-Fa-f]即可

129  /** Returns true if `ch` is in `[0-9A-Fa-f]`. */130  __BIONIC_CTYPE_INLINE int isxdigit(int __ch) {131    return (__ch >= '0' && __ch <= '9') || (__ch >= 'a' && __ch <= 'f') || (__ch >= 'A' && __ch <= 'F');132  }
复制代码

增加的 isascii 函数---实际通过判断输入值是否小于 128 来判断是否是 ASCII 码

166  /** Returns true if `ch` is less than 0x80. */167  __BIONIC_CTYPE_INLINE int isascii(int __ch) {168    return __BIONIC_CAST(static_cast, unsigned, __ch) < 0x80;169  }
复制代码

字符转换函数

tolower---将大写字符转换为小写

核心是将输入的大写字符与 0x20 做或运算,实际上就是加上 32,A-Z 是 0x41-0x5A,a-z 是 0x61-0x7A,验证是可以的。

134  /**135   * Returns the corresponding lower-case character if `ch` is upper-case, or undefined otherwise.136   *137   * Prefer tolower() instead.138   */139  __BIONIC_CTYPE_INLINE int _tolower(int __ch) {140    return __ch | 0x20;141  }142  143  /** Returns the corresponding lower-case character if `ch` is upper-case, or `ch` otherwise. */144  __BIONIC_CTYPE_INLINE int tolower(int __ch) {145    if (__ch >= 'A' && __ch <= 'Z') return _tolower(__ch);146    return __ch;147  }
复制代码

toupper---将小写字母转换为大写

核心是用小写字母与 0x20 做异或运算,具体原理可以参看位运算的巧妙使用 -- 字母大小写转换

149  /**150   * Returns the corresponding upper-case character if `ch` is lower-case, or undefined otherwise.151   *152   * Prefer toupper() instead.153   */154  __BIONIC_CTYPE_INLINE int _toupper(int __ch) {155    // Using EOR rather than AND makes no difference on arm, but saves an156    // instruction on arm64.157    return __ch ^ 0x20;158  }159  160  /** Returns the corresponding upper-case character if `ch` is lower-case, or `ch` otherwise. */161  __BIONIC_CTYPE_INLINE int toupper(int __ch) {162    if (__ch >= 'a' && __ch <= 'z') return _toupper(__ch);163    return __ch;164  }
复制代码

增加的 toascii 函数---通过输入值与 0x7f 做与运算,截断低 7 位转换为有效的 ASCII 码

171  /** Returns `ch & 0x7f`. */172  __BIONIC_CTYPE_INLINE int toascii(int __ch) {173    return __ch & 0x7f;174  }
复制代码

以上就是 ctype.h 文件的全部内容。

发布于: 刚刚阅读数: 4
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020.02.29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
C++学习------cctype头文件的作用与源码学习_c++_桑榆_InfoQ写作社区