在C/C++語(yǔ)言中使用正則表達(dá)式
本文所有的內(nèi)容都來(lái)自網(wǎng)絡(luò),加上一些個(gè)人的理解,不保證正確性,歡迎批評(píng)指正
說(shuō)起正則表達(dá)式(Regular Expression),也許有的朋友天天都在使用,比如grep、vim、sed、awk,只是可能對(duì)這個(gè)名詞不大熟悉。正則表達(dá)式一般簡(jiǎn)寫(xiě)為regex或者regexp,甚至是RE。關(guān)于正則表達(dá)式的介紹,有很多的文章,用搜索引擎查找就可以找到很不錯(cuò)的使用說(shuō)明。但是在C/C++語(yǔ)言中如何去使用,相應(yīng)的介紹比較缺乏。大多數(shù)C標(biāo)準(zhǔn)庫(kù)自帶regex,可以通過(guò)/usr/include/regex.h去看,或者man regex看使用說(shuō)明。perl,php等語(yǔ)言更是提供了功能強(qiáng)大的正則表達(dá)式,最著名的C語(yǔ)言正則表達(dá)式庫(kù)為PCRE(Perl Compatible Regular Expression)。本文主要對(duì)regex和pcre的使用做一點(diǎn)入門(mén)介紹。
1、regex
regex的使用非常簡(jiǎn)單,只要看一下示例代碼1就能明白(示例代碼是從“GNU C 規(guī)則表達(dá)式入門(mén)”這篇文章里摘取出來(lái)的,是否為原始出處就
不得而知了)。
CODE:#include <stdio.h>
#include <string.h>
#include <regex.h>
#define SUBSLEN 10 /* 匹配子串的數(shù)量 */
#define EBUFLEN 128 /* 錯(cuò)誤消息buffer長(zhǎng)度 */
#define BUFLEN 1024 /* 匹配到的字符串buffer長(zhǎng)度 */
int main()
{
size_t len;
regex_t re; /* 存儲(chǔ)編譯好的正則表達(dá)式,正則表達(dá)式在使用之前要經(jīng)過(guò)編譯 */
regmatch_t subs [SUBSLEN]; /* 存儲(chǔ)匹配到的字符串位置 */
char matched [BUFLEN]; /* 存儲(chǔ)匹配到的字符串 */
char errbuf [EBUFLEN]; /* 存儲(chǔ)錯(cuò)誤消息 */
int err, i;
char src [] = "111 <title>Hello World</title> 222"; /* 源字符串 */
char pattern [] = "<title>(.*)</title>"; /* pattern字符串 */
printf("String : %s\n", src);
printf("Pattern: \"%s\"\n", pattern);
/* 編譯正則表達(dá)式 */
err = regcomp(&re, pattern, REG_EXTENDED);
if (err) {
len = regerror(err, &re, errbuf, sizeof(errbuf));
printf("error: regcomp: %s\n", errbuf);
return 1;
}
printf("Total has subexpression: %d\n", re.re_nsub);
/* 執(zhí)行模式匹配 */
err = regexec(&re, src, (size_t) SUBSLEN, subs, 0);
if (err == REG_NOMATCH) { /* 沒(méi)有匹配成功 */
printf("Sorry, no match ...\n");
regfree(&re);
return 0;
} else if (err) { /* 其它錯(cuò)誤 */
len = regerror(err, &re, errbuf, sizeof(errbuf));
printf("error: regexec: %s\n", errbuf);
return 1;
}
/* 如果不是REG_NOMATCH并且沒(méi)有其它錯(cuò)誤,則模式匹配上 */
printf("\nOK, has matched ...\n\n");
for (i = 0; i <= re.re_nsub; i++) {
len = subs[i].rm_eo - subs[i].rm_so;
if (i == 0) {
printf ("begin: %d, len = %d ", subs[i].rm_so, len); /* 注釋1 */
} else {
printf("subexpression %d begin: %d, len = %d ", i, subs[i].rm_so, len);
}
memcpy (matched, src + subs[i].rm_so, len);
matched[len] = '\0';
printf("match: %s\n", matched);
}
regfree(&re); /* 用完了別忘了釋放 */
return (0);
}
執(zhí)行結(jié)果是
CODE:String : 111 <title>Hello World</title> 222
Pattern: "<title>(.*)</title>"
Total has subexpression: 1
OK, has matched ...
begin: %, len = 4 match: <title>Hello World</title>
subexpression 1 begin: 11, len = 11 match: Hello World
從示例程序可以看出,使用之前先用regcomp()編譯一下,然后調(diào)用regexec()進(jìn)行實(shí)際匹配。如果只是看有沒(méi)有匹配成功,掌握這2個(gè)函數(shù)的用法即可。有時(shí)候我們想要取得匹配后的子表達(dá)式,比如示例中想獲得title是什么,需要用小括號(hào) "( )"把子表達(dá)式括起來(lái)"<title>(.*)</title>",表達(dá)式引擎會(huì)將小括號(hào) "( )" 包含的表達(dá)式所匹配到的字符串記錄下來(lái)。在獲取匹配結(jié)果的時(shí)候,小括號(hào)包含的表達(dá)式所匹配到
的字符串可以單獨(dú)獲取,示例程序就是我用來(lái)獲取http網(wǎng)頁(yè)的主題(title)的方式。
regmatch_t subs[SUBSLEN]是用來(lái)存放匹配位置的,subs[0]里存放這個(gè)匹配的字符串位置,subs[1]里存放第一個(gè)子表達(dá)式的匹配位置,也就是例子中的title,通過(guò)結(jié)構(gòu)里的rm_so和rm_eo可以取到,這一點(diǎn)很多人不太注意,應(yīng)該強(qiáng)調(diào)一下。
注釋1:開(kāi)始調(diào)試代碼的時(shí)候是在FreeBSD 6.2上進(jìn)行的,print出來(lái)的len總是0,但print出來(lái)的字符串又沒(méi)錯(cuò),很是迷惑,把它放到Linux上則完全正常,后來(lái)仔細(xì)檢查才發(fā)現(xiàn)rm_so在Linux上是32位,在FreeBSD上是64位,用%d的話實(shí)際取的是rm_so的高32位,而不是實(shí)際的len,把print rm_so的地方改為%llu就可以了。
regex雖然簡(jiǎn)單易用,但對(duì)正則表達(dá)式的支持不夠強(qiáng)大,中文處理也有問(wèn)題,于是引出了下面要說(shuō)的PCRE。
2、PCRE (http://www.pcre.org)
PCRE的名字就說(shuō)明了是Perl Compatible,熟悉Perl、PHP的人使用起來(lái)完全沒(méi)有問(wèn)題。PCRE有非常豐富的使用說(shuō)明和示例代碼(看看
pcredemo.c就能明白基本的用法),下面的程序只是把上面regex改為pcre。
CODE:/* Compile thuswise:
* gcc -Wall pcre1.c -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lpcre
*
*/
#include <stdio.h>
#include <string.h>
#include <pcre.h>
#define OVECCOUNT 30 /* should be a multiple of 3 */
#define EBUFLEN 128
#define BUFLEN 1024
int main()
{
pcre *re;
const char *error;
int erroffset;
int ovector[OVECCOUNT];
int rc, i;
char src [] = "111 <title>Hello World</title> 222";
char pattern [] = "<title>(.*)</title>";
printf("String : %s\n", src);
printf("Pattern: \"%s\"\n", pattern);
re = pcre_compile(pattern, 0, &error, &erroffset, NULL);
if (re == NULL) {
printf("PCRE compilation failed at offset %d: %s\n", erroffset, error);
return 1;
}
rc = pcre_exec(re, NULL, src, strlen(src), 0, 0, ovector, OVECCOUNT);
if (rc < 0) {
if (rc == PCRE_ERROR_NOMATCH) printf("Sorry, no match ...\n");
else printf("Matching error %d\n", rc);
free(re);
return 1;
}
printf("\nOK, has matched ...\n\n");
for (i = 0; i < rc; i++) {
char *substring_start = src + ovector[2*i];
int substring_length = ovector[2*i+1] - ovector[2*i];
printf("%2d: %.*s\n", i, substring_length, substring_start);
}
free(re);
return 0;
}
執(zhí)行結(jié)果是:
CODE:String : 111 <title>Hello World</title> 222
Pattern: "<title>(.*)</title>"
OK, has matched ...
0: <title>Hello World</title>
1: Hello World
比較這2個(gè)例子可以看出,在regex用的是regcomp()、regexec(),pcre則使用pcre_compile()、pcre_exec(),用法幾乎完全一致。
pcre_compile()有很多選項(xiàng),詳細(xì)說(shuō)明參見(jiàn)http://www.pcre.org/pcre.txt。如果是多行文本,可以設(shè)置PCRE_DOTALL的選項(xiàng)pcre_complie(re,
PCRE_DOTALL,....),表示'.'也匹配回車(chē)換行"\r\n"。
3、pcre++
pcre++(http://www.daemon.de/PCRE)對(duì)pcre做了c++封裝,使用起來(lái)更加方便。
CODE:/*
* g++ pcre2.cpp -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lpcre++ -lpcre
*/
#include <string>
#include <iostream>
#include <pcre++.h>
using namespace std;
using namespace pcrepp;
int main()
{
string src("111 <title>Hello World</title> 222");
string pattern("<title>(.*)</title>");
cout << "String : " << src << endl;
cout << "Pattern : " << pattern << endl;
Pcre reg(pattern, PCRE_DOTALL);
if (reg.search(src) == true) { //
cout << "\nOK, has matched ...\n\n";
for(int pos = 0; pos < reg.matches(); pos++) {
cout << pos << ": " << reg[pos] << endl;
}
} else {
cout << "Sorry, no match ...\n";
return 1;
}
return 0;
}
執(zhí)行結(jié)果是:
CODE:String : 111 <title>Hello World</title> 222
Pattern : <title>(.*)</title>
OK, has matched ...
0: Hello World
4、oniguruma
還有一個(gè)正則表達(dá)式的庫(kù)oniguruma(http://www.geocities.jp/kosako3/oniguruma/),對(duì)于東亞文字支持比較好,開(kāi)始是用在ruby上,也可用于C++,是日本的開(kāi)發(fā)人員編寫(xiě)的。大多數(shù)人都不會(huì)用到,也就不做介紹了。如果有疑問(wèn)可以通過(guò)email來(lái)討論它的用法。
5、Regular Expression的內(nèi)部實(shí)現(xiàn)
關(guān)于Regular Expression的實(shí)現(xiàn),用到了不少自動(dòng)機(jī)理論(Automata Theory)的知識(shí),有興趣的可以找這方面的資料來(lái)看,這本書(shū)“
Introduction to Automata Theory, Languages, and Computation”寫(xiě)的很好,編譯原理的書(shū)也有這方面的內(nèi)容。
相關(guān)文章

網(wǎng)絡(luò)工程師必備的6款配置文件編輯神器:替代Notepad++!
本期文章介紹了六種網(wǎng)工必備工具,這些工具是網(wǎng)絡(luò)工程師日常工作中必不可少的,也是那些立志成為網(wǎng)絡(luò)工程師的人應(yīng)該熟悉的2026-04-13
首個(gè)版本v1.111已上線! VS Code開(kāi)啟周更狂飆模式
026年3月10日,微軟宣布將Visual Studio Code(VS Code)更新節(jié)奏從月更提速至周更,并發(fā)布首個(gè)周更版本v1.1112026-03-11
AI直接調(diào)試瀏覽器! VS Code重大更新發(fā)布
還在手動(dòng)打開(kāi)瀏覽器、點(diǎn)擊按鈕、檢查控制臺(tái)報(bào)錯(cuò)?太累了!現(xiàn)在,最新的VS Code 1.110中AI可以直接調(diào)試瀏覽器2026-03-10
vc 6.0代碼是正確編譯無(wú)法運(yùn)行怎么解決?
今天帶大家來(lái)共同學(xué)習(xí)VC++6.0無(wú)法正常運(yùn)行怎么辦?下面就是詳細(xì)完整的操作步驟,快跟小編一起來(lái)學(xué)習(xí)一下2026-02-27
vscode調(diào)試c代碼始終找不到exe可執(zhí)行文件的詳細(xì)及解決辦法
在 VS Code 中調(diào)試 C++ 項(xiàng)目時(shí),常見(jiàn)報(bào)錯(cuò):“無(wú)法啟動(dòng)調(diào)試會(huì)話:找不到可執(zhí)行文件”,這個(gè)問(wèn)題問(wèn)題通常是由于環(huán)境配置不當(dāng)、缺少編譯步驟、路徑設(shè)置錯(cuò)誤、或是launch.json2026-02-27
vscode入門(mén)! 小白都能看懂的VS Code保姆級(jí)級(jí)使用教程
相對(duì)于其它前端工具來(lái)說(shuō),VSCode顯得更加的流暢,今天就將我的學(xué)習(xí)經(jīng)歷和大家分享一下,希望可以幫助到有需要的人2026-02-27
2026年真正值得裝、用得上、不雞肋的 VSCode 插件,其實(shí)沒(méi)那么多——挑10個(gè)“神級(jí)”不難,但得是每天打開(kāi)就用、關(guān)掉就難受的那種2026-02-10
VS Code 和 VSCodium怎么選? vscode 和 vscodium之間的區(qū)別
咱們今天就來(lái)聊聊 Visual Studio Code (VS Code) 和 VSCodium的相似與區(qū)別,詳細(xì)請(qǐng)看下文介紹2026-01-10
如何啟用.NET Framework3.5? 一鍵安裝net3.5解決軟件依賴(lài)問(wèn)題
你是否曾在安裝某些軟件時(shí)遇到過(guò)“需要.NET Framework 3.5”的提示?實(shí),只需要幾步簡(jiǎn)單的操作,你就可以輕松解決這個(gè)問(wèn)題2025-10-28
VS Code中Java擴(kuò)展代碼重構(gòu)功能的使用詳解
在軟件開(kāi)發(fā)過(guò)程中,重構(gòu)是一個(gè)非常重要的環(huán)節(jié),重構(gòu)可以提高代碼的可讀性、可維護(hù)性和可擴(kuò)展性,使代碼更加健壯和易于理解,本文將介紹如何使用 VSCode 進(jìn)行 Java 代碼重構(gòu)2025-09-20





