在實際開發中,作一些字符串的匹配時,使用正則表達式來過濾匹配,代碼更加簡潔、匹配更加精準。爲此,想到引入一個問題來總結記錄一下 Linux C 中正則表達式的使用方法。
一、問題描述
要求用戶輸入一串類似 IP地址 的字符串,該程序通過調用C庫提供的正則表達式接口來實現判斷用戶輸入的 IP 是否合法。
二、匹配 IP地址正則表達式
^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}$
注意:此表達式只匹配正確的 IP格式,標準IP爲 [0-255].[0-255].[0-255].[255],不判斷用戶輸入的IP值大小,即用戶輸入 999.999.999.999 時,正則匹配爲正確的 IP格式,實際 ip值的大小可通過在程序中判斷是否爲 【0-255】。
三、正則函數接口
C正則函數聲明頭文件爲: <sys/types.h> 和 <regex.h>
- 首先聲明一個 regex_t preg; 結構體變量,用來存放編譯後的正則表達式,定義在 regex.h
#ifdef __USE_GNU # define __REPB_PREFIX(name) name #else # define __REPB_PREFIX(name) __##name #endif struct re_pattern_buffer { unsigned char *__REPB_PREFIX(buffer); //保存已編譯模式的存儲空間 unsigned long int __REPB_PREFIX(allocated); //*buffer指向空間的字節數 unsigned long int __REPB_PREFIX(used); // 實際buffer使用的字節數 reg_syntax_t __REPB_PREFIX(syntax); // 被編譯正則的語法設置 char *__REPB_PREFIX(fastmap); //指向快速映射的指針 __RE_TRANSLATE_TYPE __REPB_PREFIX(translate); //轉換模式類型 size_t re_nsub; // 用來存儲正則表達式中的子正則表達式的個數 unsigned __REPB_PREFIX(can_be_null) : 1; // 如果此模式與空字符串不匹配,則爲0,否則爲1 /* If REGS_UNALLOCATED, allocate space in the `regs' structure for `max (RE_NREGS, re_nsub + 1)' groups. If REGS_REALLOCATE, reallocate space if necessary. If REGS_FIXED, use what's there. */ #ifdef __USE_GNU # define REGS_UNALLOCATED 0 # define REGS_REALLOCATE 1 # define REGS_FIXED 2 #endif unsigned __REPB_PREFIX(regs_allocated) : 2; /* Set to zero when `regex_compile' compiles a pattern; set to one by `re_compile_fastmap' if it updates the fastmap. */ unsigned __REPB_PREFIX(fastmap_accurate) : 1; /* If set, `re_match_2' does not return information about subexpressions. */ unsigned __REPB_PREFIX(no_sub) : 1; /* If set, a beginning-of-line anchor doesn't match at the beginning of the string. */ unsigned __REPB_PREFIX(not_bol) : 1; /* Similarly for an end-of-line anchor. */ unsigned __REPB_PREFIX(not_eol) : 1; /* If true, an anchor at a newline matches. */ unsigned __REPB_PREFIX(newline_anchor) : 1; }
- regcomp : 編譯生成正則表達式,
參數介紹:int regcomp(regex_t *preg, const char *regex, int cflags)
preg:之前定義的 regex_t 結構體變量地址,用來存儲編譯後的正則表達式
regex:寫好的正則表達式
cflags:表示要編譯的正則類型
REG_EXTENDED 以功能更加強大的擴展正則表達式的方式進行匹配
REG_ICASE 匹配字母時忽略大小寫
REG_NOSUB 不用存儲匹配後的結果
REG_NEWLINE 識別換行符,’^’ 行首和行尾 ‘$’ - regexec:判斷一個字符串是否與之前 regcomp 生成的正則匹配,匹配成功返回 0,失敗返回 REG_NOMATCH
參數介紹:int regexec(const regex_t *preg, const char *string, size_t nmatch, \ regmatch_t pmatch[], int eflags);
string:待匹配的字符串
nmatch:regmatch_t 結構體數組的長度
pmatch[]:regmatch_t 結構體數組,存放匹配字符串的位置信息
eflag:有REG_NOTBOL 和 REG_NOTEOL - regfree:當使用完編譯好的正則表達式後,或者要重新編譯其他正則表達式的時候,用這個函數清空compiled指向的regex_t結構體的內容,如果需要重新編譯,一定要清空regex_t結構體
void regfree(regex_t *preg);
- regerror:當執行regcomp 或者regexec 產生錯誤的時候,就可以調用這個函數而返回一個包含錯誤信息的字符串。
參數介紹:size_t regerror(int errcode, const regex_t *preg, char *errbuf, \ size_t errbuf_size);
errcode:是由regcomp 和 regexec 函數返回的錯誤代號
errbuf:指向存放錯誤信息的字符串的內存空間
errbuf_size:errbuf 的長度
四、C代碼實現
#include <stdio.h>
#include <string.h>
#include <regex.h>
#include <sys/types.h>
#define ERROR_SIZE 256
char errbuf[ERROR_SIZE] = {0};
static void check_Ip_Format(regex_t *ipreg, const char *ip_str)
{
regmatch_t pmatch[1];
const size_t nmatch = 1;
int status = regexec(ipreg, ip_str, nmatch, pmatch, 0);
if(status == 0) {
printf("Match Successful!\n");
}
else if(status == REG_NOMATCH) {
regerror(status, ipreg, errbuf, ERROR_SIZE);
printf("%s\n", errbuf);
memset(errbuf, 0, ERROR_SIZE);
}
return;
}
int main()
{
// 999.999.999.999
char ip_str[20] = {0};
char *ip_format = "^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}$";
// 編譯正則表達式
regex_t ipreg;
int reg = regcomp(&ipreg, ip_format, REG_EXTENDED);
if(reg != 0) {
regerror(reg, &ipreg, errbuf, ERROR_SIZE);
printf("%s\n", errbuf);
memset(errbuf, 0, ERROR_SIZE);
return 0;
}
while(1) {
memset(ip_str, 0, 16);
printf("Please input IP adderss: ");
scanf("%s", ip_str);
if(strstr(ip_str, "end")) {
printf("Stop!!!\n");
break;
}
check_Ip_Format(&ipreg, ip_str);
}
regfree(&ipreg);
return 0;
}
五、執行結果
上面程序能夠匹配出用戶輸入的 ip 是否是數字,並且是否滿足標準IP格式,具體數字大小可以另行判斷