全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:9149
推到 Plurk!
推到 Facebook!

Regex++簡介(Regular Expression Library for C++)

 
happosai
高階會員


發表:93
回覆:228
積分:109
註冊:2002-09-15

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-07-13 23:50:01 IP:218.166.xxx.xxx 訂閱
http://akk.fengsoft.com/regex .htm 發表人 - happosai 於 2003/07/16 12:25:41
領航天使
站長


發表:12216
回覆:4186
積分:4084
註冊:2001-07-25

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-07-14 19:03:47 IP:210.202.xxx.xxx 未訂閱
Regex++ 簡介 (歡迎轉載,但請註明出處)    作者:八方齋 出處:http://akk.fengsoft.com/regex++.htm    E-mail:make.sense@msa.hinet.net    現職:某遊戲公司程式設計師。    P.S 感謝智峰軟體友情贊助網頁空間。    緣起: C++是一種強大無比的語言,但是有一樣功能卻是從 C 語言時代就一直被人詬病,那就是「字串」。當然,有些朋友認為 sprintf, sscanf 等就已經夠用了,甚至到了 C++ 時代,這些函數仍是程式員的最愛(包括在下),但是不可諱言的,這些函數的確有些跟不上時代(這些東西起碼有2X年的歷史了)。         首先,這些函數都缺乏 type-safe 的特性,因為這些函數的操作幾乎都依靠格式化字串,僅有的除錯資訊就是這些函數 run-time 時的回傳值,但是這些函數的回傳值只有告訴你成功轉換了幾個引數,卻沒有告訴你轉換失敗的是哪幾個,甚至應該轉換失敗的也轉換成功(如 float 不小心轉成 int ),這些也許都還可以歸咎程式員自己寫程式不夠細心,但是還有一個更糟糕的問題是,格式化字串的語法並非放諸四海而皆準。         讓我們來看看問題最嚴重的 sscanf (個人看法),讓我們思考下面這段程式:    char test_str[]=”aaa bbb”;    sscanf(test_str, “%s %s”, p1, p2); //p1, p2 is char*    你會發現引數 p2 永遠不會得到他想要的值, why?很簡單,因為 C 語言的字串是以 0 作為結尾,所以當 sscanf 看到第一個 %s 時,他會開始掃瞄 test_str 直到0為止,此時將字串拷貝給p1,結束執行,而p2什麼也得不到。老練的 C 程式員的解法是改用 strtok 將字串分解,這在C語言中是合理的解法,但是在C++中將 std::string::c_str() 丟給 strtok() 執行將是一場浩劫,原因是 strtok 對字串通常採取破壞的手段(填入0進行分隔)。除此之外,各位不妨思考一下為何C++ Builder、Delphi的String只有 sprintf 而沒有實作 sscanf ?         Regular Expression 讓我們看看另一種語言Perl,Perl眾所皆知對字串處理的能力超強,而程式可與Regular Expression[1]溶為一體,而Regular Expression(後面簡稱regex)更不用說了,熟習Linux操作的人很少沒有人用過它(grep, sed…),根據你所下的expression,可以很方便的對文字進行精確的search/replace;不要以為這樣的功能只存在於script language,事實上regex已經是UN*X標準配備(POSIX),連C語言也可以享用到!         就算在windows環境下,我們也有免費的C regex lib可用,PCRE(http://www.pcre.org/)就是十分知名的一套,大家常用的php, python等都有引用做為核心元件之一,不過PCRE本身並不好操作,參數多是一個原因,重點是PCRE操作的仍是C-style 字串,而非C++程式員熟習的std::string,當然我們可以透過一些包裝的手段來使得PCRE也可用在std::string之上;但是有了Regex++,我們就何苦重新發明輪子?         Regex++ Regex++[2]是一套完全以C++寫成的regex lib,支援VC、BCB、GCC等等C++ compiler,由於使用template的方式設計,於是不但可以用在std::string上,char*、甚至是stlport特有的rope都沒問題,聰明的你一定已經猜到,沒錯,他也是利用iterator的方式對字串操作,如此一來你在STL學到的知識仍然能用在Regex++身上,讓我們看看下面這個簡單的例子[3]:         bool validate_card_format(const std::string& s)    {            static const boost::regex e(“\\d{15,16}”);            return regex_match(s ,e);    }         這個函數的用途是檢查字串長度是不是15or16個字元,並且每個字元都必須是數字(0~9),沒記錯的話這是用來檢查信用卡號碼的格式正不正確,\\d代表必須為數字,{15,16}則限制了長度必須在15~16之間。         現在我們討論一個更複雜的例子:         const boost::regex e(“\\A(\\d{3,4})[- ]?(\\d{4})[- ]?(\\d{4})[- ]?(\\d{4})\\z”);         const std::string machine_format(“\\1\\2\\3\\4”);    const std::string human_format(“\\1-\\2-\\3-\\4”);         std::string    machine_readable_card_number(const std::string& s)    {    std::string result = regex_merge(s, e, machine_format, boost::match_default | boost::format_sed | boost::format_no_copy);         if(result.size()==0)            throw std::runtime_error(“string is not a credit card number”);            return result;    }         std::string    human_readable_card_number(const std::string& s)    {            std::string result = regex_merge(s, e, human_format, boost, boost::match_default | boost::format_sed | boost::format_no_copy);         if(result.size()==0)                    throw std::runtime_error(“string is not a credit card number”);            return result;    }         machine_readable_card_number()的功能是把信用卡號碼轉成適合機器讀取的格式,比方說XXXX-XXXX-XXXX-XXXX是我們一般人習慣的格式,假設我們想把它存進database做為索引,以字串作為欄位格式似乎不是個好主意(效能比較差),所以你可以利用machine_readable_card_number()進行轉換,然後再用我們的老朋友atoi()或是istringstream轉成整數存入資料庫中。同樣的,為了讓user心情愉快,當我們從database中讀出這個整數時,可以利用human_readable_card_number()轉成一般人習慣的格式J         這裡大略解釋一下程式內容(假如你學過Perl,可以跳過這段),regex_merge的用途是「當字串符合pattern時,依據format進行合併」,pattern指的是第二個參數,format是三個參數,至於旗標暫時不用去管他,pattern中的\\A與\\z限制字串必須全部match,而不是片段,第一個小括號(\\d{3,4})對應到format中的\\1(以此類推),[]代表一個集合,[- ]代表”-“與space,加上”?”代表”-“與space只能有一個或零個。         Overloaded Operator >> 如果剛剛舉的例子還不能讓你感受到regex的強大威力,那接下來的範例肯定會讓你耳目一新,大家都知道istringstream的用法如下:    int a, b;    string str=”123 567”;    istringstream iss(str);    iss>>a>>b;    很顯然易見,istringstream是以空白作為區隔讀取字串,假設我們今天不想用空白而想用”,”呢?雖然istringstream也能設定format state讓我們作一些小改變,不過要是逗號旁多了一些空白,istringstream也能輕鬆處理嗎(假設這個字串是從某個設定檔中讀出,這個設定檔是由user自行輸入產生的)?起碼以筆者現在的C++功力,是無法簡單用istringstream完成的。         但是我們現在有了Regex++這個強力的武器,問題就簡單多了,Regex++有個template function - regex_search()可以對string進行搜尋,之前曾經提到可以要求Regex++對字串的比對是「完全吻合」,當然也可以要求Regex++「部分吻合」,這樣我們便可以設計一個pattern來搜尋以逗號作為分隔資料的字串,也許這樣講還是很模糊,我們來看看實際的程式:         typedef pair ConstStdStrPtrPair; template ConstStdStrPtrPair operator>>(ConstStdStrPtrPair lhs, T& rhs) { boost::match_results what; static boost::regex ",* *([^,\\s] ) *,*"); unsigned int flags = boost::match_default; if(boost::regex_search(lhs.first, lhs.second, what, expression, flags) && lhs.first!=lhs.second) { string buf(what[1].first, what[1].second); istringstream ss(buf); if( !(ss>>rhs) ) throw runtime_error("Convert Fail!"); lhs.first = what[0].second; } else throw runtime_error("Format Error or String End"); return lhs; } template ConstStdStrPtrPair operator>>(const std::string& lhs, T& rhs) { boost::match_results what; static boost::regex ",* *([^,\\s] ) *,*"); unsigned int flags = boost::match_default; ConstStdStrPtrPair ptrPair; ptrPair.first = lhs.begin(); ptrPair.second = lhs.end(); if(boost::regex_search(ptrPair.first, ptrPair.second, what, expression, flags) && ptrPair.first!=ptrPair.second) { string buf(what[1].first, what[1].second); istringstream ss(buf); if( !(ss>>rhs) ) throw runtime_error("Convert Fail!"); ptrPair.first = what[0].second; } else throw runtime_error("Format Error or String End"); return ptrPair; } 我先解釋第二個operator>>,第一個參數是string,第二個是欲取得的值,將字串的開頭跟結尾傳入regex_search()進行搜尋,若是符合格式,what會指向符合格式的子字串,what[0].second會指向符合格式的子字串結尾的下一個iterator,可以作為下一次搜尋的起點,所以我把它與字串結尾存入一pair作為回傳值,然後把這個pair寫成第一個operator>>的第一個參數,這樣從字串讀出資料時就如同istringstream一樣簡單: //example int a,b,c; string testString=”123,456,789”; testString>>a>>b>>c; 至於what[1].first與what[1].second可以很明白的看出是指向符合格式的子字串的開頭與結尾,這邊的作法是另外存成字串,透過istringstream轉出,最後檢查有沒有轉換成功。 心得: 如何?Rgex 是不是替你的C 程式設計生涯帶來了曙光?其實剛剛的例子還有改進的空間,最顯而易見的就是pattern是寫死在operator>>裡,有沒有辦法讓user自行決定?或是「另外存成字串,透過istringstream轉出」這樣的動作會不會造成記憶體上額外的開銷?有沒有什麼降低成本的辦法?這些就留給讀者去研究吧(假如你有什麼好點子請聯絡我,謝謝!) 事實上,作者已經利用Regex 完成了不少工作,例如html mail(透過indy發信,regex 則用來搜尋html中的影像檔並修改為outlook可讀取的格式),自訂的設定檔讀取模組(like INI),parse C語言函數參數的型別與參數名稱…就是因為它這麼好用,才有感而發覺得該寫篇文章推廣一下,希望對大家的平日的工作能有所幫助J 參考資料: [1] 學習Regular Expression,國內就有幾個不錯的網站: http://www.cyut.edu.tw/~ckhung/olbook/gnulinux/regexp.shtml 這是朝陽科技大學資管系洪朝貴老師寫的,內容有點亂,不過用Perl配合著練習會好很多。 http://phi.sinica.edu.tw/aspac/reports/94/94019/ 中央研究院計算中心寫的一份簡介 http://main.rtfiber.com.tw/~changyj/ 事實上,國內我還沒看過比龍門少尉寫的更淺顯易懂的(強力推薦) [2] http://ourworld.compuserve.com/homepages/John_Maddock/regexpp.htm [3] 這兩個例子取自Regex 作者在Dr. Dobb’s Journal October 2001發表的文章”Regular Expressions in C ”,個人認為這篇文章是個很好的入門教材,Regex 本身的範例實在有點嚇人。
------
~~~Delphi K.Top討論區站長~~~
shaofu
高階會員


發表:5
回覆:136
積分:103
註冊:2003-01-07

發送簡訊給我
#3 引用回覆 回覆 發表時間:2003-07-15 11:09:38 IP:210.243.xxx.xxx 未訂閱
主題應該就是: 介紹 C++ 的 reg function 吧.. 跟 C++ 字串處理遜不遜關係不大 < > 這篇文章寫的不錯, 值得推薦 < >
happosai
高階會員


發表:93
回覆:228
積分:109
註冊:2002-09-15

發送簡訊給我
#4 引用回覆 回覆 發表時間:2003-07-16 10:13:18 IP:202.168.xxx.xxx 訂閱
引言: 主題應該就是: 介紹 C 的 reg function 吧.. 跟 C 字串處理遜不遜關係不大 < > 這篇文章寫的不錯, 值得推薦 < >
我勒!主題怎麼被改了!?版主對主題有意見也請通知一下吧,這樣隨便改 別人的主題很不禮貌喔...況且改成"介紹 C 的 reg function"我想很多人 還以為是介紹registry...@@
shaofu
高階會員


發表:5
回覆:136
積分:103
註冊:2003-01-07

發送簡訊給我
#5 引用回覆 回覆 發表時間:2003-07-16 10:55:29 IP:210.243.xxx.xxx 未訂閱
呃.. 因為小弟的建議造成 happosai 兄的誤會.. 先在此表達深深的歉意  > 再次致歉...
領航天使
站長


發表:12216
回覆:4186
積分:4084
註冊:2001-07-25

發送簡訊給我
#6 引用回覆 回覆 發表時間:2003-07-16 12:04:07 IP:192.168.xxx.xxx 未訂閱
引言: 我勒!主題怎麼被改了!?版主對主題有意見也請通知一下吧,這樣隨便改 別人的主題很不禮貌喔...況且改成"介紹 C 的 reg function"我想很多人 還以為是介紹registry...@@
很抱歉,是站長修改的,不是版主修改的! 我是覺的"覺得C 字串處理很遜?",這樣的主題會讓人不了解文章的目的, 修改成為"介紹 C 的 reg function",的確不恰當, 也請您再幫忙想個主題,謝謝! 暫時改為:"覺得C 字串處理很遜?請看!(Regex 簡介) " ~~~Delphi K.Top討論區站長~~~
------
~~~Delphi K.Top討論區站長~~~
happosai
高階會員


發表:93
回覆:228
積分:109
註冊:2002-09-15

發送簡訊給我
#7 引用回覆 回覆 發表時間:2003-08-21 00:48:11 IP:218.166.xxx.xxx 訂閱
原來的床不太穩,來個新的... http://home.pchome.com.tw/home/happosai/regex .htm
shinnlu
一般會員


發表:6
回覆:11
積分:3
註冊:2007-09-14

發送簡訊給我
#8 引用回覆 回覆 發表時間:2009-01-22 16:35:42 IP:220.130.xxx.xxx 訂閱
regex++ 好怪,比起php 裡面的 ereg, preg差好多
明明在php 裡面正常的regex,到了 regex 裡面硬是亂抓
happosai
高階會員


發表:93
回覆:228
積分:109
註冊:2002-09-15

發送簡訊給我
#9 引用回覆 回覆 發表時間:2011-03-24 17:45:19 IP:122.124.xxx.xxx 訂閱
遲來的回應,您可以改用 PCRE

http://www.pcre.org/
系統時間:2024-05-08 9:37:02
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!