最近寫的代碼涉及到字符串的判斷,主要是判斷一個字符是否是十進制數字、十六進制數字、大寫字母、小寫字母。學到了一些新知識。
最開始我採用最簡單暴力的方法:
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標誌位,設置了就是數字,否則就不是。