使用 POSIX 正則庫 regex.h 實作中國大陸電話號碼(手機、固話、400/800)與中文日期時間的提取與標準化。本文提供完整模組化代碼(頭文件、實作、測試),涵蓋正則模式設計、匹配提取、子串解析、格式化輸出全流程。
// 完整模式(延伸可讀性版)
(
([0-9]{11}) // 11 位手機號
| ( // 或:固話/400/800
(400|800)([0-9\-]{7,10}) // 400/800 開頭
| (([0-9]{3,4})([- ])?)?([0-9]{7,8}) // 區號(3-4碼)+分隔符?+號碼(7-8碼)
(([- ]|转)*([0-9]{1,4}))? // 分機號
)
)
phone_extract
int phone_extract(const char* str, char** phone) {
const char* pattern = "(([0-9]{11})|((400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})(-| )?)?([0-9]{7,8})((-| |转)*([0-9]{1,4}))?))";
regex_t reg; regmatch_t pm[1];
regcomp(®, pattern, REG_EXTENDED);
int z = regexec(®, str, 1, pm, 0);
regfree(®);
if (z != 0) { *phone = NULL; return -1; }
// 提取匹配子串
char* matched = substr(str, pm[0].rm_so, pm[0].rm_eo - pm[0].rm_so);
*phone = strdup(matched);
free(matched);
return 0;
}
// 日期:2011年3月7日 / 2011-03-07 / 2011/03/07
const char* pattern_date = "[0-9]{2,4}(-|/|年)[0-9]{1,2}(-|/|月)[0-9]{1,2}";
// 時間:14:03 / 14:03:59
const char* pattern_time = "[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?";
datetime_extract
int datetime_extract(const char* str, char** datetime) {
char* out = (char*)malloc(20); // "YYYY-MM-DD HH:MM:SS\0"
*datetime = out;
int Y=0, M=0, D=0, h=0, m=0, s=0;
// 1. 提取日期部分
regex_t reg; regmatch_t pm[1];
regcomp(®, pattern_date, REG_EXTENDED);
if (regexec(®, str, 1, pm, 0) == 0) {
char* dstr = substr(str, pm[0].rm_so, pm[0].rm_eo - pm[0].rm_so);
// 解析年月日:尋找分隔符位置
int sep1 = find(dstr, "-"); if (sep1<0) sep1 = find(dstr, "/"); if (sep1<0) sep1 = find(dstr, "年");
int sep2 = rfind(dstr, "-"); if (sep2<0) sep2 = rfind(dstr, "/"); if (sep2<0) sep2 = find(dstr, "月");
int off = (sep1>=0 && strstr(dstr+sep1, "年")) ? 2 : 1; // "年"/"月" 佔 2 字節 (GBK) 或 3 (UTF-8) - 此處假設 GBK
Y = atoi(substr(dstr, 0, sep1));
M = atoi(substr(dstr, sep1+off, sep2-sep1-off));
D = atoi(substr(dstr, sep2+off, strlen(dstr)-sep2-off));
if (Y < 100) Y += 2000;
free(dstr);
}
regfree(®);
// 2. 提取時間部分
regcomp(®, pattern_time, REG_EXTENDED);
if (regexec(®, str, 1, pm, 0) == 0) {
char* tstr = substr(str, pm[0].rm_so, pm[0].rm_eo - pm[0].rm_so);
int c1 = find(tstr, ":");
int c2 = rfind(tstr, ":");
h = atoi(substr(tstr, 0, c1));
if (c1 != c2) {
m = atoi(substr(tstr, c1+1, c2-c1-1));
s = atoi(substr(tstr, c2+1, strlen(tstr)-c2-1));
} else {
m = atoi(substr(tstr, c1+1, strlen(tstr)-c1-1));
s = 0;
}
free(tstr);
}
regfree(®);
sprintf(out, "%04d-%02d-%02d %02d:%02d:%02d", Y, M, D, h, m, s);
return 0;
}
substr(str, pos, len):安全子串提取(分配新記憶體)find(src, sub) / rfind(src, sub):正向/反向查找子串位置
# 編譯
gcc test.c PhoneDateExtract.c -o test -lregex
# 測試輸出
format phone = [800-2142-325]
format datetime = [2011-03-07 14:03:00]
off 需動態計算regcomp 開銷大,高頻調用場景應預編譯快取 regex_t<regex>、PCRE2、RE2 提供更豐富語法與更好效能此模組已在實際項目中穩定運行,可直接整合至日誌分析、爬蟲清洗、表單驗證等管線。