挖了個坑給自己跳,為啥我會這想不開,要在 C 上寫 regexp?
不過最大的坑應該是…我幹麻選 C 阿,都 4、5 年沒碰了。
regex.h 函式庫
用 C 來寫正規表示式第一個碰到的問題是,C 的標準函式庫中並沒有支援正規表示式。
不過還好我的開發環境是 Linux,在現行 Linux 中大多有安裝 POSIX.2,所以可以直接引入 <regex.h>
來使用,但如果是 Windows 就得自食其力了。
看了下它的原始碼還挺簡單的,抽掉細節跟註解,比較常用的也就 4 個函式與一些常數(根據 felix021 的網誌說明,整份文件有 2 個類別、4 個函式和 7 個常數)。
在使用 regex.h 進行開發時,一般會有三個步驟:編譯、 匹配、 釋放,三個步驟分別對應到四個常用函式中的其中三個:regcomp()
、 regexec()
、 regfree()
,剩下一個是錯誤處理 regerror()
。
編譯:regcomp()
這個是把指定的表示式 pattern
編譯成特定的資料格式 regex_t
,在下一個階段會使用編譯後的結果 preg
進行匹配。
1 |
|
這個函式有三的參數,分別是:
- preg: 它的資料格式是 regex_t,用來存放編譯後的結果,所以傳進去的是指標。
- pattern:這邊傳進我們的表示式,也是傳指標。除此之外,它是個常數。
- cflags: 這部分是一些參數的設定,可傳入一個或多個(用|串接)值,可使用的值有:
- REG_EXTENDED:使用 ERE(Extended Regular Expressions,擴展型正規表示式)模式進行匹配。
- REG_ICASE:忽略字母大小寫。
- REG_NOSUB:僅回報匹配成功或失敗。
- REG_NEWLINE: 識別換行符號。
思考了下,我應該會開 ERE,因為我會的流派應該算是 PCRE (Perl Compatible Regular Expressions)一派,跟 BRE(Basic Regular Expression,基本型正規表示式)差異 看起來頗多,相較之下 ERE 還稍微相近一點,不過還是有些差異。
回傳值的部分,編譯成功會回傳 0,否則傳回其他值。來個片段看一下:
1 |
|
是說 POSIX 體系,沒有 \d
跟 \w
可用,只能寫成 [a-z0-9]
。
匹配:regexec()
編譯好後就可以把目標字串放進去匹配了。
1 |
|
先提一下 regmatch_t
這個資料格式,在程式碼中它是長這樣:
1 |
|
其中的 rm_so
是用來記錄匹配結果在字串中的起始位置,rm_eo
是結束位置。一般會將它宣告為陣列,index 為 0 存放 full match,之後存放 group,陣列長度會決定記錄多少的 group。
我們來看下要傳啥參數進去,
- preg:這就是在上一個階段使用
regcomp
編譯完的preg
,直接丟進來就好。 - target:我們的目標字串。
- nmatch 與 matchptr:
matchptr
就是上面所提過regmatch_t
陣列。nmatch
則是matchptr
的長度。 - eflags:有兩個值 REG_NOTBOL、REG_NOTEOL。
繼續我們的 Code:
1 |
|
釋放:regfree()
把空間釋放放掉。
1 |
|
在使用完畢或要重編譯新的表示式前,調用這個清空 preg
指向的 regex_t
的内容。
1 |
|
錯誤處理: regerror()
當執行 regcomp
或 regexec
產生錯誤的時候,就可以調用這個函數返回錯誤訊息的字串。
1 |
|
參數的部分:
- errcode:就是剛剛
regcomp
跟regexec
回傳的狀態碼,直接丟進去就好。 - preg:就是剛剛編譯好的正規表示式。
- errbuf:一個陣列,拿來存放錯誤訊息用的。
- errbuf_size:指
errbuf
的長度。
1 |
|
程式範例
把上面的程式碼,整合起來:
1 |
|
參考資料
- 陈止风 (2017-02-06)。c regex 用法 。檢自 陈止风的博客|CSDN博客 (2020-08-28)。
- felix021 (2012-11-25)。使用Linux/Unix/BSD的regex库 。檢自 Felix021 (2020-08-28)。
- piedog (2016-06-22)。When using regex in C, \d does not work but [0-9] does 。檢自 StackOverflow (2020-08-28)。
- Richard Kettlewell 。Regexp Syntax Summary 。檢自 RJK (2020-08-28)。
- elbort (2012-09-04)。c语言 正则表达式可编译c文件 。檢自 elbort的专栏 | CSDN博客 (2020-08-28)。
更新紀錄
最後更新日期:2020-09-24
- 2021-03-05 錯誤修正
- 2020-09-24 發布
- 2020-09-09 完稿
- 2020-08-28 起稿