字符串處理——判斷是否是數字或字母

最近寫的代碼涉及到字符串的判斷,主要是判斷一個字符是否是十進制數字、十六進制數字、大寫字母、小寫字母。學到了一些新知識。

最開始我採用最簡單暴力的方法:

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標誌位,設置了就是數字,否則就不是。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章