2020年8月28日 星期五

C 語言 - 正規表示法實作 ( regex.h )

regex.h (Linux 原生, Windows 再說)

    Regex.h 實作主要分 3 階段,regcom, regexec, regfree。
    // 要被批配的 buffer 跟一些參數
    int status, len, i;
    char buf[1024], data[1024];
    getdata(data);

    // 正規表示式的會要先 compile (regcomp())並儲存在 regex_t 此資料結構
    regex_t preg;

    // 設定批配模式的 flag. 
    // REG_EXTENDED ERE (Extended Regular Expression, 不用就是 BRE)
    // REG_ICASE    忽略大小寫
    // REG_NOSUB    不儲存批配後的結果
    // REG_NEWLINE  識別換行符(^$ 這類符號會成每一行的開頭跟結尾), REG_NEWLINE 效力 > eflags
    int cflags = REG_EXTENDED | REG_NEWLINE;

    // 正規表示式的批配樣板
    const char * regex = "Name: ([A-Z a-z]+)\nYear of Birth: ([^\n]*)\n([^:]*: [^\n]*)";

    // pmatch 為 struct 陣列去儲存批配後的結果
    // pmatch.rm_so 批配到的子字串在 target string 的起始 index
    // pmatch.rm_eo 批配到的子字串在 target string 的終止 index
    // nmatch 為宣告 pmatch 的陣列大小
    const size_t nmatch = 10;
    regmatch_t pmatch[nmatch];
    
    // eflags 也會對批配做改動
    // REG_NOTBOL   開頭批配符號(^)永遠批配不到
    // REG_NOTEOL   結尾批配符號($)永遠批配不到
    // REG_STARTEND 是直接使用 pmatch[0] 的 rm_so 跟 rm_eo
    //   作為字串頭尾的 index 然後批配,是為了避免 target string 
    //   中間有終止符(\0) 或 字串太長 strlen() 會 fail.
    int eflags = 0;

    // compile regex 
    if( regcomp(&preg, regex, cflags) != 0 ) {
        puts("regex compile error!\n");
        return;
    }

    // 進行批配 status = 0 代表成功
    status = regexec(&preg, data, nmatch, pmatch, eflags);
    if (status == REG_NOMATCH) {
        printf("No Match\n");
    } else if (status == 0) {
        // pmatch[0] 所指的子字串會是整串
        // pmatch[1], pmatch[2]... 會是括弧裡 sub regex,通常拿來取真正想要的值
        for (i = 0; i < nmatch && pmatch[i].rm_so >= 0; ++i) {
            len = pmatch[i].rm_eo - pmatch[i].rm_so;
            strncpy(buf, data + pmatch[i].rm_so, len);
            buf[len] = '\0';
            printf("match %d :\n%s\n\n", i+1, buf);
        }
    }

    regfree(&preg);
    return;

測試資料 跟 結果

    測試資料
Name: JunYe
Year of Birth: 1993
Gender: Male
    結果
match 1 :
Name: JunYe
Year of Birth: 1993
Gender: Male

match 2 :
JunYe

match 3 :
1993

match 4 :
Gender: Male

Basic (BRE) and extended (ERE) regular expression

    在一些特殊字元處理不一樣, 請參考 GNU的說明
參考資料 :

0 意見:

張貼留言

Popular Posts