判斷一個字符串是否為數字,看似簡單,但要處理正負號、十六進制、小數點、空白字符等情況,實現起來仍有一定難度。本文提供一個健壯的 is_number 函數實現,能識別整數、十六進制整數與浮點數,並附帶完整測試用例。
函數採用狀態機思路,逐字符解析:
0x/0X 開頭的十六進制#define IS_BLANK(c) ((c) == ' ' || (c) == '\t')
#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
#define IS_HEX_DIGIT(c) (((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
/* 返回值:0=非數字, 1=整數, 2=十六進制整數, 3=浮點數 */
int is_number(char *s) {
int base = 10;
char *ptr;
int type = 0;
if (s == NULL) return 0;
ptr = s;
/* 跳過空白 */
while (IS_BLANK(*ptr)) ptr++;
/* 跳過符號 */
if (*ptr == '-' || *ptr == '+') ptr++;
/* 首字符必須是數字或小數點 */
if (IS_DIGIT(*ptr) || *ptr == '.') {
if (*ptr != '.') {
/* 處理十六進制 */
if (*ptr == '0' && ptr[1] && (ptr[1] == 'x' || ptr[1] == 'X')) {
type = 2;
base = 16;
ptr += 2;
}
/* 跳過前導零 */
while (*ptr == '0') ptr++;
/* 解析數字 */
while (IS_DIGIT(*ptr) || (base == 16 && IS_HEX_DIGIT(*ptr))) ptr++;
}
/* 處理小數點(僅十進制) */
if (base == 10 && *ptr && *ptr == '.') {
type = 3;
ptr++;
while (IS_DIGIT(*ptr)) ptr++;
}
/* 以空字符結尾才算合法 */
if (*ptr == '\0') return (type > 0) ? type : 1;
else type = 0;
}
return type;
}
編寫測試程序覆蓋各種邊界情況:
#include#include int main() { assert(is_number(NULL) == 0); assert(is_number("") == 0); assert(is_number("9a") == 0); assert(is_number("908") == 1); assert(is_number("-908") == 1); assert(is_number("+09") == 1); assert(is_number("-+9") == 0); assert(is_number(" 007") == 1); assert(is_number("0x9a8F") == 2); assert(is_number("-0xAB") == 2); assert(is_number("-9.380") == 3); assert(is_number("-0xFF.3") == 0); // 十六進制不支持小數 printf("test OK\n"); return 0; }
程序輸出 test OK,說明所有斷言通過,函數邏輯正確。
這個實現涵蓋了常見數字格式,代碼結構清晰,易於擴展。實際項目中可根據需求增加科學計數法(e/E)支持。