女人夜夜春高潮爽A∨片传媒_国产精品VIDEOS麻豆_在线精品亚洲一区二区三区_亚洲熟妇无码av

編程代碼
新聞詳情

C++與正則表達式入門(二)

發布時間:2020-10-16 16:28:54 最后更新:2020-11-23 14:32:07 瀏覽次數:3629

正(zheng)則表達(da)式編程(cheng) 

 接下來我們會看到更多的示例。同時,也會看到C++正則表達式API的更多功能。 為了便于下文示例的講解,我們以維基百科上對于正則表達式的介紹文本為基礎。

我(wo)們將這段文字保存在(zai)名稱為(wei)content.txt的(de)文本文件中(zhong)。下(xia)面幾個示例會在(zai)這個文本上(shang)操作。

迭代器

在上文中,為了從字符串中查找出所有匹配的字符,我們的做法是遍歷原始字符串的每一個子字符串來進行查找,這樣做很明顯效率很低。更好的做法當然是使用迭代器。


正則表達式迭代器一共有四種,分別對應了是否是寬字符,是否是字符串類型:

在一大段文(wen)本中查找所有(you)匹配的(de)目標,這是(shi)一個非常常見的(de)需(xu)求(qiu)。而迭(die)代器正好滿足這一需(xu)求(qiu),它會依次返回(hui)它從文(wen)本中找到的(de)匹配內容。

  • 示例:統計出文本中一共出現了多個單詞。
思路:組成單詞的字母可以使用[[:alpha:]]字符類來表達,一個單詞至少有一個字母,因此這個正則表達式可以寫成:[[:alpha:]]+。然后借助迭代器便可以統計出總數量。
代碼示例如下:

這段(duan)代(dai)碼的說(shuo)明如下:

  1. 匹配單詞的正則表達式
  2. 通過ifstream讀取文本文件
  3. 依次讀取文本文件中的每一行
  4. 通過正則表達式迭代器從文本行的逐個匹配
  5. 迭代器的末尾
  6. 迭代器遍歷
  7. 每遇到一個匹配進行一次計數
  8. 如果需要,可以輸出匹配的內容

這段代碼輸出如下:

 接(jie)下來的(de)幾個代碼(ma)示例的(de)主體結構和這里會很相似,我(wo)們總是先打開文本文件,然后讀取每一行來進行處理。

正則表達式選項

前面的示例中我們已經看到,通過std::regex并傳遞字符串就可以構造正則表達式對象。實際上,除了std::regex,還有寬字符版本的std::wregex。它們都源自std::basic_regex

在創建正則表達式對象的時候,除了描述規則本身的字符串之外,還可以傳遞一個flag_type類型的參數,該參數的值定義在std::regex_constants::syntax_option_type中。它們中與“文法”相關的已經在上文介紹過了。


剩下的還有幾個說明如下:

這其中,第一個是我們最常用的。

示例:匹配文本中“regular expression”所有的單復數,并且不區分大小寫。

思路(lu):單(dan)詞的(de)首字母(mu)有(you)些會大寫,我(wo)們可以(yi)通(tong)過[Rr]來匹配大寫或者(zhe)小寫的(de)R字母(mu),但實(shi)際上,使用icase無疑會更(geng)方便(bian)。


代碼示例:

這段代碼與前面的結構是一樣的,我們最需要關注的可能就是下面這一行:

通過(guo)std::regex::icase我們指定了這個正(zheng)則(ze)表達式是不區分大小寫的。

另外還(huan)有一個值得(de)注意的(de)就是正則表(biao)達式末尾的(de)...s?,它意味(wei)著單(dan)詞可能是單(dan)數或者復數,因此(ci)結(jie)尾的(de)“s”可以出現0次或者1次。

這段代碼輸出如下:

匹配結果與分組

std::match_results用來存(cun)儲匹(pi)配結(jie)果(guo)。與(yu)迭代(dai)器(qi)類似,匹(pi)配結(jie)果(guo)也有四種類型(xing):

當(dang)我們(men)使用(yong)(yong)正則表達式時,我們(men)的(de)目(mu)標常常不單(dan)單(dan)是判斷或者(zhe)查找完整匹(pi)配的(de)內容。而(er)是需要(yao)(yao)捕獲匹(pi)配結果中的(de)子串。例(li)如:我們(men)不僅要(yao)(yao)匹(pi)配出日(ri)(ri)期,還要(yao)(yao)捕獲日(ri)(ri)期中的(de)年(nian)份,月份等信(xin)息(xi)。這個(ge)時候就要(yao)(yao)使用(yong)(yong)分組功能。

我們在介紹正則表達式特殊字符的時候,提到過圓括號()。它們(men)的作用(yong)(yong)就(jiu)是(shi)分(fen)組(zu)。當你在(zai)正則(ze)表達式中(zhong)(zhong)配對(dui)的使用(yong)(yong)圓括號(hao)(hao)(hao)時,就(jiu)會形(xing)成(cheng)一個分(fen)組(zu),一個正則(ze)表達式中(zhong)(zhong)可(ke)以包含(han)多個分(fen)組(zu)。分(fen)組(zu)通過編號(hao)(hao)(hao)0, 1, 2, …來區分(fen)。編號(hao)(hao)(hao)0的分(fen)組(zu)是(shi)匹(pi)配的整體,其(qi)他編號(hao)(hao)(hao)根據括號(hao)(hao)(hao)的順(shun)序來確定(ding)。

這些(xie)分組(zu)最(zui)終可以在匹(pi)配完成之后,可以通過std::match_results的API來獲取。這些(xie)API如下表所示:

在C++中,分(fen)組叫做子匹配(sub_match)。std::sub_match 這個(ge)類型(xing)只有一個(ge)默認構造函數,通常你不會(hui)主動創建它(ta),而是使用std::match_results的接口(kou)來獲(huo)取它(ta)的對象。

示例:查(cha)找(zhao)出文本中所有的(de)(de)(de)年代,并分離出世(shi)紀的(de)(de)(de)部分和(he)年份的(de)(de)(de)部分。 思路:年代的(de)(de)(de)格式是四(si)位數字(zi)加上“s”作為后綴。我們可以通過分組的(de)(de)(de)形式分離出兩(liang)個部分。圖示如下:

代碼示例:

這(zhe)段代碼說(shuo)明如下:

  1. 這個正則表達式請注意其中的圓括號
  2. 先打印匹配的字符串整體
  3. 所有的分組數量,應該是 2 + 1 = 3
  4. 打印出世紀的部分
  5. 獲取編號2的分組,其類型是sub_match

這段代碼輸出如(ru)下:

稍微深入一點的內容

同一個符號的不同含義

前面的表格中,我們看到了正則表達式的特殊字符。但需要進一步說明的是,這些特殊字符在不同的環境可能有著不同的含義。


例如,特殊字符-只有在字符組[...]內部才是元字符,否則它只能匹配普通的連字符符號。并且,即便在字符組內部,如果連字符是在開頭,它依然是一個普通字符而不是表示一個范圍。


相反的,問號?和點號.不在字符組內部的時候才是特殊字符。因此[?.]中的這兩個符號僅僅代表這兩個字符自身。


還有,字符^出現在字符組中的時候表示的是否定,例如:[a-z]和[^a-z]表示的是正好相反的字符集。但是當字符^不是用在字符組中的時候,它是一個錨點,具體內容下文會說到。

量詞的占有欲

還是以content.txt的內(nei)容為基礎,現在假(jia)設我們(men)的目標是:找(zhao)出(chu)所有雙引號(hao)中的內(nei)容。

根(gen)據之前的知識,你可能很輕松就寫出了下面這(zhe)個(ge)正(zheng)則(ze)表達(da)式(shi):

  • 兩邊的雙引號通過反斜杠轉義
  • 待捕獲的內容通過圓括號形成分組
  • 雙引號中可以是任意內容,因此使用.+

但是當你運行(xing)程序的時候卻發現它可能有點問題。它捕獲的結(jie)果(guo)是:

為什么?其實很簡單,因為雙引號本身也可以與.匹配。上面這個正則表達式的含義是:匹配一個兩端是雙引號,中間是任意文字的內容。


當然,你馬上想到一個改進方法那就是:將正則表達式圓括號中的.+改為[^"]+,它的含義是:一個或多個非雙引號字符。這么做是可以的。但其實我們還有更好的做法。


我們再回頭看一下原先的正則表達式,不考慮分組和轉義,它可以寫成:".+"。其實我們知道下面這三個字符串都是與其匹配的:

而將整個文本交給正則表達式的時候,它找出了最長的那個串。可見,原先的正則表達式太過“貪婪”(greedy)。是的,量詞在默認情況都是貪婪的。即:它們會盡可能多的占有內容。


那我們能不能控制量詞讓其盡可能少的占有內容,只要滿足匹配要求就可以呢?


答案是肯定的,而且做法很簡單:在量詞的后面加上一個?。即,將圓括號中.+修改為.+?即可。量詞的默認形式稱之為“匹配優先量詞”,現在這種寫法稱之為“忽略優先量詞”。


現在它找到的是下面兩個匹配:

小結一下(xia):

錨點

錨點是一類特(te)殊的(de)標(biao)(biao)記,它們不(bu)會匹配任何文(wen)本內(nei)容,而是尋找(zhao)特(te)定的(de)標(biao)(biao)記。你可以簡單理解(jie)為它是原(yuan)先表達式的(de)基礎上(shang)增加了(le)新的(de)匹配條件。如(ru)果條件不(bu)滿足,則無法完成匹配。

錨點主要分為三種:

下面是(shi)代(dai)碼示例:

它的輸(shu)出如下:

環視

現在假設(she)我們有下面兩個(ge)需求:

  1. 匹配出所有sometimes中的前四個字符“some”
  2. 匹配出所有的單詞some,但是要排除掉“some birds”中的“some”

對于(yu)第一個(ge)問題(ti),我們(men)可以分兩步:先找出所有(you)(you)的(de)單詞sometimes,然后取前四個(ge)字符。對于(yu)第二個(ge)問題(ti),我們(men)可以先找出所有(you)(you)的(de)單詞“some”,然后把(ba)后面(mian)是“birds”的(de)丟掉(diao)。

以上的(de)解法都是分兩步(bu)完成。但實(shi)際上,借助(zhu)環(huan)視(lookaround)我們可以一步(bu)就完成任(ren)務。

 環視(shi)是對匹(pi)配位(wei)置的(de)附加條件(jian)(jian),只(zhi)有條件(jian)(jian)滿足(zu)時才能完成匹(pi)配。環視(shi)有:順(shun)序(xu)(向右),逆序(xu)(向左),肯定和否定一共四(si)種(zhong):

環(huan)視說起來有些拗口,但看具體的例(li)子就(jiu)容易(yi)理(li)解了(le):

這段代碼并(bing)不(bu)復雜所以就不(bu)多(duo)做說明,它的輸(shu)出結(jie)果如下:

對于包含環視的正則表達式來說,環視之外的內容是匹配的主體,環視本身只是一個附件條件。(?=sometimes)這個肯定順序環視要求從這個位置開始,接下來的字符串必須是"sometimes"才能完成匹配。(?!some birds)這個否定順序環視要是接下來的字符串一定不能是"some birds"才能完成匹配。


為了進一步幫助你理解,我們以圖示的方式將(?=sometimes)some匹配"something"的過程描述出來。


圖示中,虛線的上面是待匹配的文本,下面是正則表達式。對于環視,我們可以將其環視條件和主體分開來看。我們以一個下標三角箭頭表示當前匹配的搜索位置。


剛開始的時候,搜索的位置是第一個字符的前面:

接下來,搜索位置(zhi)往后走一個(ge)字符:

這個過程可以一直進行,直到匹配完"some"

雖然正則表達式的主體"some"完成(cheng)了匹配,但是(shi)接下來環(huan)視的條件卻無法滿足,于(yu)是(shi)匹配失敗(bai):

但是,如果要匹配內容正好是"sometimes",則條件是滿足的,于是就完(wan)成了匹配。


在(zai)線客(ke)服
客服電話
  • 0755-23712116
  • 13310869691