本文共 3142 字,大约阅读时间需要 10 分钟。
最近写的代码涉及到字符串的判断,主要是判断一个字符是否是十进制数字、十六进制数字、大写字母、小写字母。学到了一些新知识。
最开始我采用最简单暴力的方法:
char ch;if(ch >= '0' && ch <= '9') //十进制数字if(ch >= '0' && ch <= '9' || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) //十六进制if(ch >= 'A' && ch <= 'Z') //大写字母if(ch >= 'a' && ch <= 'z') //小写字母if(ch >= 'A' && ch <= 'Z' || (ch >= 'a' && ch <= 'z')) //字母
后来由于需要性能优化,我就想,能不能更快呢?有没有别的什么办法呢?
于是我查了查ASCII码,搞了一个位操作版:if((ch & 0xf8) == 0x30 || (ch & 0xfe) == 0x38) // 十进制if((ch & 0xd8) == 0x40 && (ch & 0x07) != 0x00 && (ch & 0x07) != 0x07) // 'a'- 'f' or 'A' - 'F'
(咳咳,这里由于字母的ASCII码太难以归纳,还不如直接用最暴力的办法,我就没搞位操作版)
这里解释一下,‘0’ - '9’可以拆成两部分: ‘0’ - ‘7’ : 0011 0000 - 0011 0111 ‘8’ - ‘9’ : 0011 1000 - 0011 1001 前者是前5位为0011 0,后者是前7位为0011 100。至于’a’- ‘f’ 和 ‘A’ - ‘F’,前5位分别为:0110 0 和0100 0。(ch & 0xd8) == 0x40
就是确定这前5位。然后后三位全0和全1的不是a-f,所以再进行两次判断。
写到这里,我本来美汁儿汁儿,直到我看到……别人的代码用了一个函数:isdigit
。
话说回来,这个函数包含在ctype.h头文件中。这个头文件中包含了我最开始说的那些各种判断:
isdigit(int) //十进制数字isxdigit(int) //十六进制数字isalpha(int) //字母isupper(int) //大写字母islower(int) //小写字母
(当然,除了这些还有一些别的功能,感兴趣的可以看一看这个头文件)
那么,C++是怎么实现这些功能的呢?
简单来说就是:空间换时间。#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) */unsigned char _ctype[] = { 0x00, /* EOF */_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 */0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 160-175 */0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 176-191 */0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 192-207 */0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 208-223 */0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 224-239 */0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* 240-255 */}#define isdigit(c) ((_ctype+1)[c]&(_D))
可以看到,C语言做了一个长度为257的数组,将每一个可能的字符都做了对应,然后为其设置标志位,然后只要查询对应字符的标志位,就知道是否是数字、字母、或者其他了。
这里以数字为例,字符’0’-‘9’,就对应48-57(十进制),可以看到,在_ctype数组中,将其全部赋值为_D,这样调用isdigit时,就是查看是否设置了_D标志位,设置了就是数字,否则就不是。转载地址:http://ltxzi.baihongyu.com/