1. 程式人生 > >在linux下,如何在C語言中使用正則表示式(整理)

在linux下,如何在C語言中使用正則表示式(整理)

  一個正則表示式的教程可以參看(裡面有個測試正則表示式的工具)

     正則表達是用來匹配字串的好東東。
  
     如果使用者熟悉Linux下的sed、awk、grep或vi,那麼對正則表示式這一概念肯定不會陌生。由於它可以極大地簡化處理字串時的複雜度,因此現 在已經在許多Linux實用工具中得到了應用。千萬不要以為正則表示式只是Perl、Python、Bash等指令碼語言的專利,作為C語言程式設計師,使用者同 樣可以在自己的程式中運用正則表示式。
   
    標準的C和C++都不支援正則表示式,但有一些函式庫可以輔助C/C++程式設計師完成這一功能,其中最著名的當數Philip Hazel的Perl-Compatible Regular Expression庫,許多Linux發行版本都帶有這個函式庫。
   
    本人使用的是fedora 8系統,該版本中就帶有這個函式庫,下面介紹一下如何使用。(實際上只要使用man命令查詢就可瞭解很多有關的細節了)
   
    很多在網上都有介紹,但多有些小的錯誤,於是我更改了並整理了一下。

    要使用正則表示式的函式庫在你的程式前面包含
    #include <sys/types.h>
    #include <regex.h>
    
    下面介紹下如何使用:
   首先,編譯正則表示式。


    為了提高效率,在將一個字串與正則表示式進行比較之前,首先要用regcomp()函式對它進行編譯,將其轉化為regex_t結構:
   
    int regcomp(regex_t *preg, const char *regex,int cflags);
   
    引數regex是一個字串,它代表將要被編譯的正則表示式;引數preg指向一個宣告為regex_t的資料結構,用來儲存編譯結果;引數cflags決定了正則表示式該如何被處理的細節。 (此處可以用man regcomp 命令檢視詳細的解釋)
    如果函式regcomp()執行成功,並且編譯結果被正確填充到preg中後,函式將返回0,任何其它的返回結果都代表有某種錯誤產生。
   
   接下來匹配正則表示式。

    一旦用regcomp()成功地編譯了正則表示式,接下來就可以呼叫regexec()完成模式匹配:

    int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t pmatch[], int eflags);
    typedef struct
    {
        regoff_t rm_so;
        regoff_t rm_eo;
    } regmatch_t;   

    引數preg指向編譯後的正則表示式,引數string是將要進行匹配的字串,而引數nmatch和pmatch則用於把匹配結果返回給呼叫程式,最後一個引數eflags決定了匹配的細節。

    在呼叫regexec()進行模式匹配的過程中,可能在字串string中會有多處與給定的正則表示式相匹配,引數pmatch就是用來儲存這些匹配位置的,而引數nmatch則告訴regexec()最多可以把多少個匹配結果填充到pmatch陣列中。當regexec()成 功返回時,從string+pmatch[0].rm_so到string+pmatch[0].rm_eo是第一個匹配的字串,而從string+ pmatch[1].rm_so到string+pmatch[1].rm_eo,則是第二個匹配的字串,依此類推。

   最後釋放正則表示式。

    無論什麼時候,當不再需要已經編譯過的正則表示式時,都應該呼叫regfree()將其釋放,以免產生記憶體洩漏。
    void regfree(regex_t *preg);

   報告錯誤資訊
  
如果呼叫regcomp()或regexec()得到的是一個非0的返回值,則表明在對正則表示式的處理過程中出現了某種錯誤,此時可以通過呼叫regerror()得到詳細的錯誤資訊。

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

    引數errcode是來自regcomp()或regexec()的錯誤程式碼,而引數preg則是由regcomp()得到的編譯結果,其目的是把格式化訊息所必須的上下文提供給regerror()。在執行regerror()時,將按照引數errbuf_size指明的最大位元組數,在errbuf緩衝區中填入格式化後的錯誤資訊,同時返回錯誤資訊的長度。



   給出一個應用正則表示式的例子
    (這個例子網上容易找到,但多半有些小錯誤,不能編譯成功,我將其修改寫在了下面)

例子如下:

#include <stdio.h>
#include <sys/types.h>
#include <regex.h>


/* 取子串的函式 */

static char* substr(const char*str,unsigned start, unsigned end)
{
    unsigned n = end - start;

    static char stbuf[256];

    strncpy(stbuf, str + start, n);

    stbuf[n] = 0;

    return stbuf;

}

int main(int argc, char** argv)
{
    char * pattern;
    int x, z, lno = 0, cflags = 0;
    char ebuf[128], lbuf[256];
    regex_t reg;
    regmatch_t pm[10];
    const size_t nmatch = 10;

    /* 編譯正則表示式*/

    pattern = argv[1];
    z = regcomp(&reg, pattern, cflags);

    if (z != 0)
    {   
        regerror(z, &reg, ebuf, sizeof(ebuf));
        fprintf(stderr, "%s: pattern '%s' /n",ebuf, pattern);
        return 1;
    }

    /* 逐行處理輸入的資料 */

    while(fgets(lbuf, sizeof(lbuf), stdin))
    {
        ++lno;
        if ((z = strlen(lbuf)) > 0 && lbuf[z-1]== '/n') lbuf[z - 1] = 0;    

        /* 對每一行應用正則表示式進行匹配 */
        z = regexec(&reg, lbuf, nmatch, pm, 0);
        if (z == REG_NOMATCH) continue;
        else if (z != 0)
        {
            regerror(z, &reg, ebuf, sizeof(ebuf));
            fprintf(stderr, "%s: regcom('%s')/n",
            ebuf, lbuf);
            return 2;
        }

        /* 輸出處理結果 */

        for (x = 0; x < nmatch && pm[x].rm_so != -1; ++ x)
        {
            if (!x) printf("%04d: %s/n", lno, lbuf);
            printf(" $%d='%s'/n", x, substr(lbuf, pm[x].rm_so,pm[x].rm_eo));
        }

    }

    /* 釋放正則表示式 */

    regfree(&reg);    
    return 0;
}

上述程式負責從命令列獲取正則表示式,然後將其運用於從標準輸入得到的每行資料,並打印出匹配結果。執行下面的命令可以編譯並執行該程式:
    # gcc regexp.c -o regexp (編譯是會有兩個警告,沒有事)
    # ./regexp 'regex[a-z]*' < regexp.c
   
    0003: #include <regex.h>
    $0='regex'
    0027: regex_t reg;
    $0='regex'
    0054: z = regexec(?, lbuf, nmatch, pm, 0);
    $0='regexec'

小結
    在C語言中使用正則表示式並不複雜。
    正則表示式用來匹配複雜的字串非常好用。