字符串处理——判断是否是数字或字母

最近写的代码涉及到字符串的判断,主要是判断一个字符是否是十进制数字、十六进制数字、大写字母、小写字母。学到了一些新知识。

最开始我采用最简单暴力的方法:

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
嗯?介系嘛?一查,人家C++早就想好了、做好了这个功能……orz

话说回来,这个函数包含在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标志位,设置了就是数字,否则就不是。

原创文章 34 获赞 41 访问量 5944

猜你喜欢

转载自blog.csdn.net/qq_44844115/article/details/105700027