C标准库 ctype.h (一) _Ctype转换表原理

简述

P.J.Plauger版本 C标准库 Ctype 中判断字符是否属于某个类型,主要是通过转换表来实现的。

以判断是否为小写字母为例

ctype.h 头文件

/* ctype.h */

#ifndef _CTYPE
#define _CTYPE

/* _Ctype 转换位 */
#define _XA	0x200	/* extra alphabetic */
#define _XS	0x100	/* extra space */
#define _BB	0x80	/* BEL, BS, etc. */
#define _CN	0x40	/* CR, FF, HT, NL, VT */
#define _DI	0x20	/* '0' - '9' */
#define _LO	0x10	/* 'a' - 'z' */
#define _PU	0x08	/* punctuation */
#define _SP	0x04	/* space */
#define _UP	0x02	/* 'A' - 'Z' */
#define _XD	0x01	/* '0' - '9', 'A' - 'F', 'a' - 'f' */

/* 声明外部的 _Ctype 转换表 */
extern const short *_Ctype;

/* 判断是否为小写字母的带参数宏 islower */
#define islower(c) (_Ctype[(int)(c)] & _LO)

// 其余省略 ...

#endif

_Ctype 转换表

/* xctype.c _Ctype 转换表 -- ASCII 版 */
#include <limits.h>
#include <stdio.h>
#include "ctype.h"

#if EOF != -1 || UCHAR_MAX != 255
#error WRONG CTYPE table
#endif

/* 组合位 */ 
#define XDI (_DI|_XD)
#define XLO (_LO|_XD)
#define XUP (_UP|_XD)

/* 转换表 */
static const short ctype_tab[257] = { 0, /* EOF */
	_BB, _BB, _BB, _BB, _BB, _BB, _BB, _BB,
	_BB, _CN, _CN, _CN, _CN, _CN, _BB, _BB,
	_BB, _BB, _BB, _BB, _BB, _BB, _BB, _BB,
	_BB, _BB, _BB, _BB, _BB, _BB, _BB, _BB,
	_SP, _PU, _PU, _PU, _PU, _PU, _PU, _PU,
	_PU, _PU, _PU, _PU, _PU, _PU, _PU, _PU,
	XDI, XDI, XDI, XDI, XDI, XDI, XDI, XDI,
	XDI, XDI, _PU, _PU, _PU, _PU, _PU, _PU,
	_PU, XUP, XUP, XUP, XUP, XUP, XUP, _UP,
	_UP, _UP, _UP, _UP, _UP, _UP, _UP, _UP,
	_UP, _UP, _UP, _UP, _UP, _UP, _UP, _UP,
	_UP, _UP, _UP, _PU, _PU, _PU, _PU, _PU,
	_PU, XLO, XLO, XLO, XLO, XLO, XLO, _LO,
	_LO, _LO, _LO, _LO, _LO, _LO, _LO, _LO,
	_LO, _LO, _LO, _LO, _LO, _LO, _LO, _LO,
	_LO, _LO, _LO, _PU, _PU, _PU, _PU, _BB,
};

const short *_Ctype = &ctype_tab[1];

调用过程

#include <stdio.h>
#include "ctype.h"

void test(char c)
{
	if( islower(c) != 0 )
		printf("%c 是小写字母\n", c);
	else
		printf("%c 不是小写字母\n", c);
}

int main()
{
	int i;
	// 遍历ASCII码表128个字符判断是否为小写字母
	for(i=0; i<128; i++)
	{
		test(i);
	}
	return 0;
}

中间判断的过程其实很简单,首先islower 是个带参数宏

预处理之后,假设当前的c是'a'那么变成了:

(_Ctype[(int)('a')] & _LO)

字符'a'的值为97所以接下来便是:

(_Ctype[97] & _LO)

_Ctype[97]的转换宏是 _LO 大家可以对找下方的 _Ctype 转换表, _LO 的值又是 0x10,所以最后是:

(_LO & _LO)   ---->    0x10 & 0x10    ---->    1    当前字符为小写字母

& 运算就不说了,详细请百度“按位与”

结果就不放上来了,大家自行实验。

_Ctype 转换位

转表宏 二进制 十进制 十六进制 对应字符
_BB 1000 0000 128 0x80 BEL, BS, etc.
_CN 0100 0000 64 0x40 CR, FF, HT, NL, VT
_DI 0010 0000 32 0x20 '0' - '9'
_LO 0001 0000 16 0x10 'a' - 'z'
_PU 0000 1000 8 0x08 标点符号
_SP 0000 0100 4 0x04 空格
_UP 0000 0010 2 0x02 'A' - 'Z'
_XD 0000 0001 1 0x01 '0' - '9', 'A' - 'F', 'a' - 'f'

通过按位与上对应的宏,就可以检查该字符在转换表中对应的转表宏,二者按位与:
如与上 _LO 其值为 0001 0000,计算的时候,0与上任何数均为0,对应位全部被清零
仅保留第5位(1与上1为1,1与上0为0),所以如果只有两种结果,一个是第5位为真(16)或者为假即为0

_Ctype 转换表

二进制 十进制 十六进制 转表宏 二进制 十进制 十六进制 ASCII值
0000 0000 0 0x00 _BB 1000 0000 128 0x80 空字符(Null)
0000 0001 1 0x01 _BB 1000 0000 128 0x80 标题开始
0000 0010 2 0x02 _BB 1000 0000 128 0x80 本文开始
0000 0011 3 0x03 _BB 1000 0000 128 0x80 本文结束
0000 0100 4 0x04 _BB 1000 0000 128 0x80 传输结束
0000 0101 5 0x05 _BB 1000 0000 128 0x80 请求
0000 0110 6 0x06 _BB 1000 0000 128 0x80 确认回应
0000 0111 7 0x07 _BB 1000 0000 128 0x80 响铃
0000 1000 8 0x08 _BB 1000 0000 128 0x80 退格
0000 1001 9 0x09 _CN 0100 0000 64 0x40 水平定位符号
0000 1010 10 0x0A _CN 0100 0000 64 0x40 换行键
0000 1011 11 0x0B _CN 0100 0000 64 0x40 垂直定位符号
0000 1100 12 0x0C _CN 0100 0000 64 0x40 换页键
0000 1101 13 0x0D _CN 0100 0000 64 0x40 Enter键
0000 1110 14 0x0E _BB 1000 0000 128 0x80 取消变换(Shift out)
0000 1111 15 0x0F _BB 1000 0000 128 0x80 启用变换(Shift in)
0001 0000 16 0x10 _BB 1000 0000 128 0x80 跳出数据通讯
0001 0001 17 0x11 _BB 1000 0000 128 0x80 设备控制一(XON 激活软件速度控制)
0001 0010 18 0x12 _BB 1000 0000 128 0x80 设备控制二
0001 0011 19 0x13 _BB 1000 0000 128 0x80 设备控制三(XOFF 停用软件速度控制)
0001 0100 20 0x14 _BB 1000 0000 128 0x80 设备控制四
0001 0101 21 0x15 _BB 1000 0000 128 0x80 确认失败回应
0001 0110 22 0x16 _BB 1000 0000 128 0x80 同步用暂停
0001 0111 23 0x17 _BB 1000 0000 128 0x80 区块传输结束
0001 1000 24 0x18 _BB 1000 0000 128 0x80 取消
0001 1001 25 0x19 _BB 1000 0000 128 0x80 连接介质中断
0001 1010 26 0x1A _BB 1000 0000 128 0x80 替换
0001 1011 27 0x1B _BB 1000 0000 128 0x80 退出键
0001 1100 28 0x1C _BB 1000 0000 128 0x80 文件分区符
0001 1101 29 0x1D _BB 1000 0000 128 0x80 组群分隔符
0001 1110 30 0x1E _BB 1000 0000 128 0x80 记录分隔符
0001 1111 31 0x1F _BB 1000 0000 128 0x80 单元分隔符
0010 0000 32 0x20 _SP 0000 0100 4 0x04 (空格,␠)
0010 0001 33 0x21 _PU 0000 1000 8 0x08 !
0010 0010 34 0x22 _PU 0000 1000 8 0x08 "
0010 0011 35 0x23 _PU 0000 1000 8 0x08 #
0010 0100 36 0x24 _PU 0000 1000 8 0x08 $
0010 0101 37 0x25 _PU 0000 1000 8 0x08 %
0010 0110 38 0x26 _PU 0000 1000 8 0x08 &
0010 0111 39 0x27 _PU 0000 1000 8 0x08 '
0010 1000 40 0x28 _PU 0000 1000 8 0x08 (
0010 1001 41 0x29 _PU 0000 1000 8 0x08 )
0010 1010 42 0x2A _PU 0000 1000 8 0x08 *
0010 1011 43 0x2B _PU 0000 1000 8 0x08 +
0010 1100 44 0x2C _PU 0000 1000 8 0x08 ,
0010 1101 45 0x2D _PU 0000 1000 8 0x08 -
0010 1110 46 0x2E _PU 0000 1000 8 0x08 .
0010 1111 47 0x2F _PU 0000 1000 8 0x08 /
0011 0000 48 0x30 XDI 0010 0001 33 0x21 0
0011 0001 49 0x31 XDI 0010 0001 33 0x21 1
0011 0010 50 0x32 XDI 0010 0001 33 0x21 2
0011 0011 51 0x33 XDI 0010 0001 33 0x21 3
0011 0100 52 0x34 XDI 0010 0001 33 0x21 4
0011 0101 53 0x35 XDI 0010 0001 33 0x21 5
0011 0110 54 0x36 XDI 0010 0001 33 0x21 6
0011 0111 55 0x37 XDI 0010 0001 33 0x21 7
0011 1000 56 0x38 XDI 0010 0001 33 0x21 8
0011 1001 57 0x39 XDI 0010 0001 33 0x21 9
0011 1010 58 0x3A _PU 0000 1000 8 0x08 :
0011 1011 59 0x3B _PU 0000 1000 8 0x08 ;
0011 1100 60 0x3C _PU 0000 1000 8 0x08 <
0011 1101 61 0x3D _PU 0000 1000 8 0x08 =
0011 1110 62 0x3E _PU 0000 1000 8 0x08 >
0011 1111 63 0x3F _PU 0000 1000 8 0x08 ?
0100 0000 64 0x40 _PU 0000 1000 8 0x08 @
0100 0001 65 0x41 XUP 0000 0011 3 0x03 A
0100 0010 66 0x42 XUP 0000 0011 3 0x03 B
0100 0011 67 0x43 XUP 0000 0011 3 0x03 C
0100 0100 68 0x44 XUP 0000 0011 3 0x03 D
0100 0101 69 0x45 XUP 0000 0011 3 0x03 E
0100 0110 70 0x46 XUP 0000 0011 3 0x03 F
0100 0111 71 0x47 _UP 0000 0010 2 0x02 G
0100 1000 72 0x48 _UP 0000 0010 2 0x02 H
0100 1001 73 0x49 _UP 0000 0010 2 0x02 I
0100 1010 74 0x4A _UP 0000 0010 2 0x02 J
0100 1011 75 0x4B _UP 0000 0010 2 0x02 K
0100 1100 76 0x4C _UP 0000 0010 2 0x02 L
0100 1101 77 0x4D _UP 0000 0010 2 0x02 M
0100 1110 78 0x4E _UP 0000 0010 2 0x02 N
0100 1111 79 0x4F _UP 0000 0010 2 0x02 O
0101 0000 80 0x50 _UP 0000 0010 2 0x02 P
0101 0001 81 0x51 _UP 0000 0010 2 0x02 Q
0101 0010 82 0x52 _UP 0000 0010 2 0x02 R
0101 0011 83 0x53 _UP 0000 0010 2 0x02 S
0101 0100 84 0x54 _UP 0000 0010 2 0x02 T
0101 0101 85 0x55 _UP 0000 0010 2 0x02 U
0101 0110 86 0x56 _UP 0000 0010 2 0x02 V
0101 0111 87 0x57 _UP 0000 0010 2 0x02 W
0101 1000 88 0x58 _UP 0000 0010 2 0x02 X
0101 1001 89 0x59 _UP 0000 0010 2 0x02 Y
0101 1010 90 0x5A _UP 0000 0010 2 0x02 Z
0101 1011 91 0x5B _PU 0000 1000 8 0x08 [
0101 1100 92 0x5C _PU 0000 1000 8 0x08 \
0101 1101 93 0x5D _PU 0000 1000 8 0x08 ]
0101 1110 94 0x5E _PU 0000 1000 8 0x08 ^
0101 1111 95 0x5F _PU 0000 1000 8 0x08 _
0110 0000 96 0x60 _PU 0000 1000 8 0x08 `
0110 0001 97 0x61 XLO 0001 0001 17 0x11 a
0110 0010 98 0x62 XLO 0001 0001 17 0x11 b
0110 0011 99 0x63 XLO 0001 0001 17 0x11 c
0110 0100 100 0x64 XLO 0001 0001 17 0x11 d
0110 0101 101 0x65 XLO 0001 0001 17 0x11 e
0110 0110 102 0x66 XLO 0001 0001 17 0x11 f
0110 0111 103 0x67 _LO 0001 0000 16 0x10 g
0110 1000 104 0x68 _LO 0001 0000 16 0x10 h
0110 1001 105 0x69 _LO 0001 0000 16 0x10 i
0110 1010 106 0x6A _LO 0001 0000 16 0x10 j
0110 1011 107 0x6B _LO 0001 0000 16 0x10 k
0110 1100 108 0x6C _LO 0001 0000 16 0x10 l
0110 1101 109 0x6D _LO 0001 0000 16 0x10 m
0110 1110 110 0x6E _LO 0001 0000 16 0x10 n
0110 1111 111 0x6F _LO 0001 0000 16 0x10 o
0111 0000 112 0x70 _LO 0001 0000 16 0x10 p
0111 0001 113 0x71 _LO 0001 0000 16 0x10 q
0111 0010 114 0x72 _LO 0001 0000 16 0x10 r
0111 0011 115 0x73 _LO 0001 0000 16 0x10 s
0111 0100 116 0x74 _LO 0001 0000 16 0x10 t
0111 0101 117 0x75 _LO 0001 0000 16 0x10 u
0111 0110 118 0x76 _LO 0001 0000 16 0x10 v
0111 0111 119 0x77 _LO 0001 0000 16 0x10 w
0111 1000 120 0x78 _LO 0001 0000 16 0x10 x
0111 1001 121 0x79 _LO 0001 0000 16 0x10 y
0111 1010 122 0x7A _LO 0001 0000 16 0x10 z
0111 1011 123 0x7B _PU 0000 1000 8 0x08 {
0111 1100 124 0x7C _PU 0000 1000 8 0x08 |
0111 1101 125 0x7D _PU 0000 1000 8 0x08 }
0111 1110 126 0x7E _PU 0000 1000 8 0x08 ~
0111 1111 127 0x7F _BB 1000 0000 128 0x80 删除

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>