Regex++簡介(Regular Expression Library for C++) |
|
happosai
高階會員 發表:93 回覆:228 積分:109 註冊:2002-09-15 發送簡訊給我 |
|
領航天使
站長 發表:12216 回覆:4186 積分:4084 註冊:2001-07-25 發送簡訊給我 |
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
------
~~~Delphi K.Top討論區站長~~~ |
shaofu
高階會員 發表:5 回覆:136 積分:103 註冊:2003-01-07 發送簡訊給我 |
|
happosai
高階會員 發表:93 回覆:228 積分:109 註冊:2002-09-15 發送簡訊給我 |
|
shaofu
高階會員 發表:5 回覆:136 積分:103 註冊:2003-01-07 發送簡訊給我 |
|
領航天使
站長 發表:12216 回覆:4186 積分:4084 註冊:2001-07-25 發送簡訊給我 |
引言: 我勒!主題怎麼被改了!?版主對主題有意見也請通知一下吧,這樣隨便改 別人的主題很不禮貌喔...況且改成"介紹 C 的 reg function"我想很多人 還以為是介紹registry...@@很抱歉,是站長修改的,不是版主修改的! 我是覺的"覺得C 字串處理很遜?",這樣的主題會讓人不了解文章的目的, 修改成為"介紹 C 的 reg function",的確不恰當, 也請您再幫忙想個主題,謝謝! 暫時改為:"覺得C 字串處理很遜?請看!(Regex 簡介) " ~~~Delphi K.Top討論區站長~~~
------
~~~Delphi K.Top討論區站長~~~ |
happosai
高階會員 發表:93 回覆:228 積分:109 註冊:2002-09-15 發送簡訊給我 |
|
shinnlu
一般會員 發表:6 回覆:11 積分:3 註冊:2007-09-14 發送簡訊給我 |
|
happosai
高階會員 發表:93 回覆:228 積分:109 註冊:2002-09-15 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |