開源正則庫及其使用

來源:互聯網
上載者:User

說起Regex(Regular Expression),也許有的朋友天天都在使用,比如grep、vim、sed、awk,只是可能對這個名詞不大熟悉。Regex一般簡寫為regex或者regexp,甚至是RE。關於Regex的介紹,有很多的文章,用搜尋引擎尋找就可以找到很不錯的使用說明。但是在C/C++語言中如何去使用,相應的介紹比較缺乏。大多數C標準庫內建regex,可以通過/usr/include/regex.h去看,或者man regex看使用說明。perl,php等語言更是提供了功能強大的Regex,最著名的C語言Regex庫為PCRE(Perl Compatible Regular Expression)。本文主要對regex和pcre的使用做一點入門介紹。

1、regex
regex的使用非常簡單,只要看一下範例程式碼1就能明白(範例程式碼是從“GNU C 規則運算式入門”這篇文章裡摘取出來的,是否為原始出處就不得而知了)。

#include <stdio.h><br />#include <string.h><br />#include <regex.h></p><p>#define SUBSLEN 10 /* 匹配子串的數量 */<br />#define EBUFLEN 128 /* 錯誤訊息buffer長度 */<br />#define BUFLEN 1024 /* 匹配到的字串buffer長度 */</p><p>int main()<br />{<br /> size_t len;<br /> regex_t re; /* 儲存編譯好的Regex,Regex在使用之前要經過編譯 */<br /> regmatch_t subs [SUBSLEN]; /* 儲存匹配到的字串位置 */<br /> char matched [BUFLEN]; /* 儲存匹配到的字串 */<br /> char errbuf [EBUFLEN]; /* 儲存錯誤訊息 */<br /> int err, i;</p><p> char src [] = "111 <title>Hello World</title> 222"; /* 源字串 */<br /> char pattern [] = "<title>(.*)</title>"; /* pattern字串 */</p><p> printf("String : %s/n", src);<br /> printf("Pattern: /"%s/"/n", pattern);</p><p> /* 編譯Regex */<br /> err = regcomp(&re, pattern, REG_EXTENDED);</p><p> if (err) {<br /> len = regerror(err, &re, errbuf, sizeof(errbuf));<br /> printf("error: regcomp: %s/n", errbuf);<br /> return 1;<br /> }<br /> printf("Total has subexpression: %d/n", re.re_nsub);<br /> /* 執行模式比對 */<br /> err = regexec(&re, src, (size_t) SUBSLEN, subs, 0);</p><p> if (err == REG_NOMATCH) { /* 沒有匹配成功 */<br /> printf("Sorry, no match .../n");<br /> regfree(&re);<br /> return 0;<br /> } else if (err) { /* 其它錯誤 */<br /> len = regerror(err, &re, errbuf, sizeof(errbuf));<br /> printf("error: regexec: %s/n", errbuf);<br /> return 1;<br /> }</p><p> /* 如果不是REG_NOMATCH並且沒有其它錯誤,則模式比對上 */<br /> printf("/nOK, has matched .../n/n");<br /> for (i = 0; i <= re.re_nsub; i++) {<br /> len = subs[i].rm_eo - subs[i].rm_so;<br /> if (i == 0) {<br /> printf ("begin: %d, len = %d ", subs[i].rm_so, len); /* 注釋1 */<br /> } else {<br /> printf("subexpression %d begin: %d, len = %d ", i, subs[i].rm_so, len);<br /> }<br /> memcpy (matched, src + subs[i].rm_so, len);<br /> matched[len] = '/0';<br /> printf("match: %s/n", matched);<br /> }</p><p> regfree(&re); /* 用完了別忘了釋放 */<br /> return (0);<br />}<br />

 

執行結果是:

String : 111 <title>Hello World</title> 222<br />Pattern: "<title>(.*)</title>"<br />Total has subexpression: 1</p><p>OK, has matched ...</p><p>begin: %, len = 4 match: <title>Hello World</title><br />subexpression 1 begin: 11, len = 11 match: Hello World

 

從樣本程式可以看出,使用之前先用regcomp()編譯一下,然後調用regexec()進行實際匹配。如果只是看有沒有匹配成功,掌握這2個函數的用法即可。有時候我們想要取得匹配後的子運算式,比如樣本中想獲得title是什麼,需要用小括弧 "( )"把子運算式括起來"<title>(.*)</title>",運算式引擎會將小括弧 "( )" 包含的運算式所匹配到的字串記錄下來。在擷取匹配結果的時候,小括弧包含的運算式所匹配到的字串可以單獨擷取,樣本程式就是我用來擷取http網頁的主題(title)的方式。  

regmatch_t subs[SUBSLEN]是用來存放匹配位置的,subs[0]裡存放這個匹配的字串位置,subs[1]裡存放第一個子運算式的匹配位置,也就是例子中的title,通過結構裡的rm_so和rm_eo可以取到,這一點很多人不太注意,應該強調一下。

注釋1:開始調試代碼的時候是在FreeBSD 6.2上進行的,print出來的len總是0,但print出來的字串又沒錯,很是迷惑,把它放到Linux上則完全正常,後來仔細檢查才發現rm_so在Linux上是32位,在FreeBSD上是64位,用%d的話實際取的是rm_so的高32位,而不是實際的len,把print rm_so的地方改為%llu就可以了。

regex雖然簡單易用,但對Regex的支援不夠強大,中文處理也有問題,於是引出了下面要說的PCRE。

2、PCRE  (http://www.pcre.org)
PCRE的名字就說明了是Perl Compatible,熟悉Perl、PHP的人使用起來完全沒有問題。PCRE有非常豐富的使用說明和範例程式碼(看看pcredemo.c就能明白基本的用法),下面的程式只是把上面regex改為pcre。

/* Compile thuswise:<br />* gcc -Wall pcre1.c -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lpcre<br />*<br />*/ </p><p>#include <stdio.h><br />#include <string.h><br />#include <pcre.h> </p><p>#define OVECCOUNT 30 /* should be a multiple of 3 */<br />#define EBUFLEN 128<br />#define BUFLEN 1024 </p><p>int main()<br />{<br /> pcre *re;<br /> const char *error;<br /> int erroffset;<br /> int ovector[OVECCOUNT];<br /> int rc, i;</p><p> char src [] = "111 <title>Hello World</title> 222";<br /> char pattern [] = "<title>(.*)</title>";</p><p> printf("String : %s/n", src);<br /> printf("Pattern: /"%s/"/n", pattern);</p><p> re = pcre_compile(pattern, 0, &error, &erroffset, NULL);<br /> if (re == NULL) {<br /> printf("PCRE compilation failed at offset %d: %s/n", erroffset, error);<br /> return 1;<br /> }</p><p> rc = pcre_exec(re, NULL, src, strlen(src), 0, 0, ovector, OVECCOUNT);<br /> if (rc < 0) {<br /> if (rc == PCRE_ERROR_NOMATCH) printf("Sorry, no match .../n");<br /> else printf("Matching error %d/n", rc);<br /> free(re);<br /> return 1;<br /> }</p><p> printf("/nOK, has matched .../n/n");</p><p> for (i = 0; i < rc; i++) {<br /> char *substring_start = src + ovector[2*i];<br /> int substring_length = ovector[2*i+1] - ovector[2*i];<br /> printf("%2d: %.*s/n", i, substring_length, substring_start);<br /> }</p><p> free(re);<br /> return 0;<br />}<br />

執行結果

String : 111 <title>Hello World</title> 222<br />Pattern: "<title>(.*)</title>"</p><p>OK, has matched ...</p><p>0: <title>Hello World</title><br />1: Hello World<br />

 

比較這2個例子可以看出,在regex用的是regcomp()、regexec(),pcre則使用pcre_compile()、pcre_exec(),用法幾乎完全一致。

pcre_compile()有很多選項,詳細說明參見http://www.pcre.org/pcre.txt。如果是多行文本,可以設定PCRE_DOTALL的選項pcre_complie(re, PCRE_DOTALL,....),表示'.'也匹配斷行符號換行"/r/n"。

3、pcre++
pcre++(http://www.daemon.de/PCRE)對pcre做了c++封裝,使用起來更加方便。

/*<br />* g++ pcre2.cpp -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lpcre++ -lpcre<br />*/<br />#include <string><br />#include <iostream><br />#include <pcre++.h></p><p>using namespace std;<br />using namespace pcrepp;</p><p>int main()<br />{<br /> string src("111 <title>Hello World</title> 222");<br /> string pattern("<title>(.*)</title>");</p><p> cout << "String : " << src << endl;<br /> cout << "Pattern : " << pattern << endl;</p><p> Pcre reg(pattern, PCRE_DOTALL);<br /> if (reg.search(src) == true) { //<br /> cout << "/nOK, has matched .../n/n";<br /> for(int pos = 0; pos < reg.matches(); pos++) {<br /> cout << pos << ": " << reg[pos] << endl;<br /> }<br /> } else {<br /> cout << "Sorry, no match .../n";<br /> return 1;<br /> }</p><p> return 0;<br />}<br />

 

執行結果

String : 111 <title>Hello World</title> 222<br />Pattern : <title>(.*)</title></p><p>OK, has matched ...</p><p>0: Hello World<br />

 

4、oniguruma
還有一個Regex的庫oniguruma(http://www.geocities.jp/kosako3/oniguruma/),對於東亞文字支援比較好,開始是用在ruby上,也可用於C++,是日本的開發人員編寫的。大多數人都不會用到,也就不做介紹了。如果有疑問可以通過email來討論它的用法。

5、DEELX

DEELX (http://www.regexlab.com/zh/deelx/)是一個在 C++ 環境下的與 Perl 相容的Regex引擎。是 RegExLab 開展的一個研究開發項目。所有代碼都在一個.h檔案中。

#include "deelx.h"<br />#include <stdio.h></p><p>int find_remark(const char * string, int & start, int & end)<br />{<br /> // declare<br /> static CRegexpT <char> regexp("///*((?!//*/).)*(//*/)?|//([^//x0A-//x0D////]|////.)*");</p><p> // find and match<br /> MatchResult result = regexp.Match(string);</p><p> // result<br /> if( result.IsMatched() )<br /> {<br /> start = result.GetStart();<br /> end = result.GetEnd ();<br /> return 1;<br /> }<br /> else<br /> {<br /> return 0;<br /> }<br />}</p><p>int main(int argc, char * argv[])<br />{<br /> char * code1 = "int a; /* a */";<br /> char * code2 = "int a;";</p><p> int start, end;</p><p> if( find_remark(code1, start, end) )<br /> printf("In code1, found: %.*s/n", end - start, code1 + start);<br /> else<br /> printf("In code1, not found./n");</p><p> if( find_remark(code2, start, end) )<br /> printf("In code2, found: %.*s/n", end - start, code2 + start);<br /> else<br /> printf("In code2, not found./n");</p><p> return 0;<br />}

執行結果

In code1, found: /* a */<br />In code2, not found.

 

 

 

6、Regular Expression的內部實現
關於Regular Expression的實現,用到了不少自動機理論(Automata Theory)的知識,有興趣的可以找這方面的資料來看,這本書“ Introduction to Automata Theory, Languages, and Computation”寫的很好,編譯原理的書也有這方面的內容。

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.