Linux下C語言處理正則表達式——regex.h
具體函數介紹
編譯正則表達式函數
int regcomp(regex_t *preg, const char *regex, int cflags);
其中preg用於保存編譯后的正則表達式,regex是我們寫的正則表達式。cflags標志位后面再說。
先說說regex_t結構體:
對於這個結構體而言,我們只要記住,它是編譯后的正則表達式,后面的匹配是用編譯后的正則表達式,這樣效率更高,而不是使用我們自己寫的原始的正則表達式。此外,還要知道regex_t有一個成員re_nsub,它表示“子正則表達式的個數”。所謂“子正則表達式”就是圓括號里面的正則表達式。可能還是有點懵。沒關系,慢慢來!我們使用正則表達式的一個主要目的是提取滿足條件的部分。比如有個字符串username=阿星&sex=女,現在我們想提取用戶名,也是就“阿星”,那么我們的正則表達式應該寫成: username=([^&]*)&,也就是將匹配“阿星”的正則表達式放到圓括號中,作為整個表達式的一個子表達式。后面我們執行regexec函數后,就可以得到“阿星”(后面再講)。
再來說說cflags:
cflags 的取值有:REG_EXTENDED、REG_ICASE、REG_NOSUB和REG_NEWLINE。這四個值可以單獨使用,也可以用按位與聯合使用。
其中:
REG_EXTENDED:
意思是,解釋正則表達式時使用擴展的正則表達式語法。POSIX規范將正則表達式的實現方法分為了兩種:基本正則表達式(BRE)和擴展正則表 達式(ERE)。
BRE和ERE到底有什么區別?其實僅僅是元字符的不同!在BRE方式中,只承認^ 、$、 . 、[ 、] 、*這些是元字符,所有其他的字符都被識別為文字字符。而ERE中,則添加了(、 ) 、{ 、} 、?、 + |、等元字符(及其相關功能)。grep命令默認支持BRE,要想支持ERE需要使用-E選項。
REG_ICASE:
如果編譯正則表達式時使用了這個標志,那么在用regexec()函數進行匹配時,就忽略大小寫。
REG_NOSUB:
如果使用了這個選項得到的編譯后的正則表達式,在被后面的regexec()函數使用時,regexec()的nmatch參數和pmatch參數將會被忽略(后面再講)
REG_NEWLINE:
一開始我對這個標志位的理解非常模糊,網上很多人解釋的也不清楚。經過我的反復試驗,終於明白了。
其實REG_NEWLINE的作用就兩個:
1、 使^和$有效。
2、 絕對不匹配換行符。
相信大家也都看過Linux中的man page。對於REG_NEWLINE這個標志位的解釋,在man page中用了四句話。
我們先來看后兩句:
Match-beginning-of-line operator (^) matches the empty string immediately after a newline, regardless of whether eflags, the execution flags of regexec(), contains REG_NOTBOL.
Match-end-of-line operator ($) matches the empty string immediately before a newline, regardless of whether eflags contains REG_NOTEOL.
這兩句的意思其實就是,是^匹配一行的開始位置,$匹配一行的結束位置(如果沒有使用REG_NEWLINE,這兩個字符將被當做普通字符)。並且使REG_NOTBOL和REG_NOTEOL無效。
舉兩個例子:
有字符串:
username=xinger&sex=girl&age=22\r\nschool=BIT&husband=qinger\r\n&like=study&look=pretty\r\n
如果我們沒有使用REG_NEWLINE標志,那么正則表達式^school=([^&]*)將不能匹配,因為這里^被解釋成了普通字符,而不是一行的開始。
如果我們加上REG_NEWLINE標志,那么將匹配成school=BIT,此時^不再是普通字符,而是匹配一行的開始。
再比如正則表達式age=([^$]*),如果沒有使用REG_NEWLINE,將匹配成:
age=22\r\nschool=BIT&husband=qinger\r\n&like=study&look=pretty\r\n
還是因為$被解釋成了普通字符,
比如我們在原字符串中添加一個$,變成
username=xinger&sex=girl&age=22\r\nschool=$BIT&husband=$qinger\r\n&like=study&look=pretty\r\n
那么匹配結果變成了age=22\r\nschool=,原因依然是:把$當成了普通字符。
在來看前兩句:
Match-any-character operators don't match a newline.
A nonmatching list ([^...]) not containing a newline does not match a newline.
這兩句的意思說白了,就是保證不匹配換行符。比如第一句,意思是匹配任意字符的元字符也不匹配新的一行(好亂呀)。什么意思呢?就是比如說點(.)本來匹配所有的字符(注意,在POSIX中點匹配所有字符!!!和我們平時學的不一樣。)但是如果使用了REG_NEWLINE標志,則不匹配換行符\r\n。
還是舉個例子吧:
有字符串:
username=xinger&sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n
有正則表達式:sex=([^@]*),如果沒有REG_NEWLINE標志,匹配結果是:
sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n
因為[^@]匹配所有不是@的字符。但是如果我們加上了REG_NEWLINE,那么匹配結果為:sex=girl&age=22,原因是REG_NEWLINE保證了絕不匹配換行符!!!其實就相當於[^@\r\n]不加REG_NEWLINE。
在比如,有正則表達式sex=(.*),我們前面提到過:點在POSIX中匹配任意字符(’\0’除外),所以點也匹配換行符,所以匹配結果為:
sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n
但是,如果我們使用了REG_NEWLINE,則保證不會匹配換行符,匹配結果就變成了:sex=girl&age=22。
最后說說返回值:
成功返回0,失敗可以用regerror獲取失敗碼。
用編譯后的正則表達式進行匹配
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
regexec函數用上一步中編譯好的正則表達式preg對string內容進行匹配,並將匹配結果以記錄字節偏移量的形式保存在pmatch數組中。
首先看看regmatch_t結構體:
regmatch_t 是一個結構體數據類型,在regex.h中定義:
typedef struct { regoff_t rm_so; regoff_t rm_eo; } regmatch_t;
regexec函數將用匹配的子字符串的起止地址填充pmatch結構體,pmatch[0]對應的是整個正則表達式的匹配結果的起止地址;pmatch[i]則對應存儲了第i個子匹配字符串的起止地址。(rm_so表示起始位置距離首地址的偏移量,rm_eo表示結束位置距離首地址的偏移量+1,如果rm_so為-1則表示該子表達式沒有匹配)。nmatch表示pmatch結構體數組的元素的個數,它至少應該是子表達式的個數加1(因為0下標存儲的是整個表達式的匹配結果)。
再來說說eflags:
首先一點,如果regcomp中使用了REG_NEWLINE變量,這個標志位是無效的!
這個標志位有兩個取值:REG_NOTBOL和REG_NOTEOL,作用就是:如果設置了相應的標志位,那么含有^或$,而且含義是一行開始,或結束(比如^也可以解釋成非),那么該正則表達式將永遠不會匹配!!!。
最后說說返回值:
成功返回0,REG_NOMATCH表示失敗。
將錯誤碼轉換成錯誤信息
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
該函數用於將regcomp或regexec返回的錯誤碼轉換成錯誤字符串信息。
參數errcode表示那兩個函數返回的錯誤碼,preg是regcomp編譯后的正則表達式,errbuf用於存儲錯誤信息字符串,errbuf_size是errbuf的大小。
釋放regex_t結構體
void regfree(regex_t *preg);
regcomp函數會填寫regex_t結構體的元素,這之中需要為某些元素開辟存儲空間,而regfree函數就是釋放這些空間的。
千萬記得最后要調用regfree釋放空間,否則會造成內存泄漏。
最后附上一個小例子:
#include <sys/types.h> #include <regex.h> #include <stdio.h> #include <string.h> #include <stdlib.h> char *pick_regex(const char *string,const char *pattern) { int err; char errbuf[1024]; regex_t compiled; if((err = regcomp(&compiled,pattern,REG_EXTENDED|REG_ICASE|REG_NEWLINE)) != 0) { regerror(err,&compiled,errbuf,sizeof(errbuf)); printf("err:%s\n",errbuf); return NULL; } regmatch_t pmatch[2]; err = regexec(&compiled,string,2,pmatch,REG_NOTBOL); if(err != 0) { printf("未匹配成功!\n"); return NULL; } if(compiled.re_nsub != 1) return NULL; if(pmatch[1].rm_so == -1) return NULL; int len = pmatch[1].rm_eo - pmatch[1].rm_so; char *value = (char *)malloc(len + 1); if(value == NULL) return NULL; memset(value,0,len + 1); memcpy(value,string + pmatch[1].rm_so,len); //free(value); regfree(&compiled);//切記最后要釋放掉,否則會造成內存泄露 return value; } int main() { const char *string = "username=xinger&sex=girl&age=22\r\nschool=BIT&husband=qinger\r\n&like=study&look=pretty\r\n"; const char *pattern = "school=([^&]*)"; char *value = pick_regex(string,pattern); printf("提取的值為:%s\n",value); return 0; }
如果你覺得對你有用,就點個贊吧~~~