c/c++ regex正則使用

工作需要用到C++中的正則表達式,所以就研究了以上三種正則。

一、三種正則的比較

1. C regex

/* write by xingming * time:2012年10月19日15:51:53 * for: test regex * */

#include <regex.h>
#include <iostream>
#include <sys/types.h>
#include <stdio.h>
#include <cstring>
#include <sys/time.h>

using namespace std;
const int times = 1000000;

int main(int argc,char** argv)
{
    char pattern[512]="finance\.sina\.cn|stock1\.sina\.cn|3g\.sina\.com\.cn.*(channel=finance|_finance$|ch=stock|/stock/)|dp.sina.cn/.*ch=9&";
    const size_t nmatch = 10;
    regmatch_t pm[10];
    int z ;
    regex_t reg;
    char lbuf[256]="set",rbuf[256];
    char buf[3][256] = {"finance.sina.cn/google.com/baidu.com.google.sina.cndddddddddddddddddddddda.sdfasdfeoasdfnahsfonadsdf",
                    "3g.com.sina.cn.google.com.dddddddddddddddddddddddddddddddddddddddddddddddddddddbaidu.com.sina.egooooooooo",
                    "http://3g.sina.com.cn/google.baiduchannel=financegogo.sjdfaposif;lasdjf.asdofjas;dfjaiel.sdfaosidfj"};
    printf("input strings:\n");
    timeval end,start;
    gettimeofday(&start,NULL);
    regcomp(&reg,pattern,REG_EXTENDED|REG_NOSUB);
    for(int i = 0 ; i < times; ++i)
    {
        for(int j = 0 ; j < 3; ++j)
        {
            z = regexec(&reg,buf[j],nmatch,pm,REG_NOTBOL);
/* if(z==REG_NOMATCH) printf("no match\n"); else printf("ok\n"); */
        }
    }
    gettimeofday(&end,NULL);
    uint time = (end.tv_sec-start.tv_sec)*1000000 + end.tv_usec - start.tv_usec;
    cout<<time/1000000<<" s and "<<time%1000000<<" us."<<endl;
    return 0 ;
}

使用正則表達式可簡單的分成幾步:
1. 編譯正則表達式
2. 執行匹配
3. 釋放內存

首先,編譯正則表達式

int regcomp(regex_t *preg, const char *regex, int cflags);

reqcomp()函數用於把正則表達式編譯成某種格式,可以使後面的匹配更有效。

@param preg: regex_t結構體用於存放編譯後的正則表達式;
@param regex: 指向正則表達式指針;
@param cflags:編譯模式

共有如下四種編譯模式:
· REG_EXTENDED:使用功能更強大的擴展正則表達式
· REG_ICASE:忽略大小寫
· REG_NOSUB:不用存儲匹配後的結果
· REG_NEWLINE:識別換行符,這樣‘$’就可以從行尾開始匹配,‘^’就可以從行的開頭開始匹配。否則忽略換行符,把整個文本串當做一個字符串處理。

其次,執行匹配

int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);

@param preg: 已編譯的正則表達式指針;
@param string:目標字符串;
@param nmatch:pmatch數組的長度;
@param pmatch:結構體數組,存放匹配文本串的位置信息;

regmatch_t 是一個結構體數據類型,在regex.h中定義:

typedef struct
{
   regoff_t rm_so;
   regoff_t rm_eo;
} regmatch_t;

成員rm_so 存放匹配文本串在目標串中的開始位置,rm_eo 存放結束位置。通常我們以數組的形式定義一組這樣的結構。因爲往往我們的正則表達式中還包含子正則表達式。數組0單元存放主正則表達式位置,後邊的單元依次存放子正則表達式位置

@param eflags:匹配模式

共兩種匹配模式:

REG_NOTBOL:The match-beginning-of-line operator always fails to match (but see the compilation flag REG_NEWLINE above). This flag may be used when different portions of a string are passed to regexec and the beginning of the string should not be interpreted as the beginning of the line.

REG_NOTEOL:The match-end-of-line operator always fails to match (but see the compilation flag REG_NEWLINE above)

最後,釋放內存

void regfree(regex_t *preg);

當使用完編譯好的正則表達式後,或者需要重新編譯其他正則表達式時,一定要使用這個函數清空該變量。

其他,處理錯誤

size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);

當執行regcomp 或者regexec 產生錯誤的時候,就可以調用這個函數而返回一個包含錯誤信息的字符串。
@parm errcode: 由regcomp 和 regexec 函數返回的錯誤代號。
@parm preg: 已經用regcomp函數編譯好的正則表達式,這個值可以爲NULL。
@parm errbuf: 指向用來存放錯誤信息的字符串的內存空間。
@parm errbuf_size: 指明buffer的長度,如果這個錯誤信息的長度大於這個值,則regerror 函數會自動截斷超出的字符串,但他仍然會返回完整的字符串的長度。所以我們可以用如下的方法先得到錯誤字符串的長度。

2. C++ regex

/* write by xingming * time:2012年10月19日15:51:53 * for: test regex * */

#include <regex>
#include <iostream>
#include <stdio.h>
#include <string>

using namespace std;

int main(int argc,char** argv)
{
    regex pattern("[[:digit:]]",regex_constants::extended);
    printf("input strings:\n");
    string buf;

    while(cin>>buf)
    {
        printf("*******\n%s\n********\n",buf.c_str());

        if(buf == "quit")
        {
            printf("quit just now!\n");
            break;
        }

        match_results<string::const_iterator> result;
        printf("run compare now! '%s'\n", buf.c_str());
        bool valid = regex_match(buf,result,pattern);
        printf("compare over now! '%s'\n", buf.c_str());

        if(!valid)
            printf("no match!\n");
        else
            printf("ok\n");
    }

    return 0 ;
}

C++這個真心不想多說它,測試過程中發現 字符匹配的時候 ‘a’ 是可以匹配的,a+也是可以的,[[:w:]]也可以匹配任意字符,但[[:w:]]+就只能匹配一個字符,+號貌似不起作用了。所以後來就乾脆放棄了這偉大的C++正則,如果有大牛知道這裏面我錯在哪裏了,真心感謝你告訴我一下,謝謝。

3. boost regex

/* write by xingming
 * for:test boost regex
 * time:2012102311:35:33
 * */
#include <iostream>
#include <string>
#include <sys/time.h>
#include "boost/regex.hpp"

using namespace std;
using namespace boost;
const int times = 10000000;

int main()
{
    regex  pattern("finance\\.sina\\.cn|stock1\\.sina\\.cn|3g\\.sina\\.com\\.cn.*(channel=finance|_finance$|ch=stock|/stock/)|dp\\.s ina\\.cn/.*ch=9&");
    cout<<"input strings:"<<endl;
    timeval start,end;
    gettimeofday(&start,NULL);
    string input[] = {"finance.sina.cn/google.com/baidu.com.google.sina.cn",
                      "3g.com.sina.cn.google.com.baidu.com.sina.egooooooooo",
                      "http://3g.sina.com.cn/google.baiduchannel=financegogo"};
    for(int i = 0 ;i < times; ++ i)
    {
        for(int j = 0 ; j < 3;++j)
        {
            //if(input=="quit")
            //  break;
            //cout<<"string:'"<<input<<'\''<<endl;
            cmatch what;
            if(regex_search(input[j].c_str(),what,pattern)) ;
            //  cout<<"OK!"<<endl;
            else ;
            //  cout<<"error!"<<endl;
        }
    }
    gettimeofday(&end,NULL);
    uint time = (end.tv_sec-start.tv_sec)*1000000 + end.tv_usec - start.tv_usec;
    cout<<time/1000000<<" s and "<<time%1000000<<" us."<<endl;
    return 0 ;
}

boost正則不用多說了,要是出去問,C++正則怎麼用啊?那90%的人會推薦你用boost正則,他實現起來方便,正則庫也很強大,資料可以找到很多,所以我也不在闡述了。

4. 對比情況

二、總結

C regex的速度讓我吃驚啊,相比boost的速度,C regex的速度幾乎要快上3倍,看來正則引擎的選取上應該有着落了!

上面的表格中我用到的正則和字符串是一樣的(在代碼中C regex的被我加長了),速度相差幾乎有3倍,C的速度大約在30+w/s , 而boost的速度基本在15-w/s ,所以對比就出來了!

結果很顯然,當然會選擇C regex了。

三、補

c regex使用時發現對於待匹配的字符串中如果出現’\x00’字符時會無法匹配!例如,pattern”[0-9]{6,10}”, 字符串”aaaa,,,,\x00,,,123456。。。”,這種情況下無法匹配到”123456”,發生錯誤,如何解決還需要看下源碼或者測試下boost regex吧,頭疼!:(
最後問題解決了, 使用了HyperScan intel的一個好用的庫!23333

四、引用

[1] C語言正則表達式詳解 regcomp() regexec() regfree()詳解 http://blog.csdn.net/yangbingzhou/article/details/51352648

[2] C++中三種正則表達式比較(C regex,C ++regex,boost regex) https://www.cnblogs.com/pmars/archive/2012/10/24/2736831.html

[3] 在MinGW使用正則表達式regex庫 http://blog.creke.net/766.html

点赞