精品国产色欧洲激情_中文字幕在线欧美日韩制服在线_欢迎观看网站影片国产在线观看伊_日本高清一本视频_ww亚洲无码免费在线观看_午夜片无码区观看_欧美性爱福利资源_丰满少妇肥唇翘臀ⅩXX_欧美日韩另类久久_国内揄拍国内精品对白86

編程代碼
新聞詳情

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

發(fā)布時(shí)間:2020-10-16 16:28:54 最后更新:2020-11-23 14:32:07 瀏覽次數(shù):3000

正則表達(dá)式編程 

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

我們將這段文字保存在名稱為content.txt的文本文件中。下面幾個(gè)示例會(huì)在這個(gè)文本上操作。

迭代器

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


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

在一大段文本中查找所有匹配的目標(biāo),這是一個(gè)非常常見的需求。而迭代器正好滿足這一需求,它會(huì)依次返回它從文本中找到的匹配內(nèi)容。

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

這段代碼的說明如下:

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

這段代碼輸出如下:

接下來的幾個(gè)代碼示例的主體結(jié)構(gòu)和這里會(huì)很相似,我們總是先打開文本文件,然后讀取每一行來進(jìn)行處理。

正則表達(dá)式選項(xiàng)

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

在創(chuàng)建正則表達(dá)式對象的時(shí)候,除了描述規(guī)則本身的字符串之外,還可以傳遞一個(gè)flag_type類型的參數(shù),該參數(shù)的值定義在std::regex_constants::syntax_option_type中。它們中與“文法”相關(guān)的已經(jīng)在上文介紹過了。


剩下的還有幾個(gè)說明如下:

這其中,第一個(gè)是我們最常用的。

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

思路:單詞的首字母有些會(huì)大寫,我們可以通過[Rr]來匹配大寫或者小寫的R字母,但實(shí)際上,使用icase無疑會(huì)更方便。


代碼示例:

這段代碼與前面的結(jié)構(gòu)是一樣的,我們最需要關(guān)注的可能就是下面這一行:

通過std::regex::icase我們指定了這個(gè)正則表達(dá)式是不區(qū)分大小寫的。

另外還有一個(gè)值得注意的就是正則表達(dá)式末尾的...s?,它意味著單詞可能是單數(shù)或者復(fù)數(shù),因此結(jié)尾的“s”可以出現(xiàn)0次或者1次。

這段代碼輸出如下:

匹配結(jié)果與分組

std::match_results用來存儲(chǔ)匹配結(jié)果。與迭代器類似,匹配結(jié)果也有四種類型:

當(dāng)我們使用正則表達(dá)式時(shí),我們的目標(biāo)常常不單單是判斷或者查找完整匹配的內(nèi)容。而是需要捕獲匹配結(jié)果中的子串。例如:我們不僅要匹配出日期,還要捕獲日期中的年份,月份等信息。這個(gè)時(shí)候就要使用分組功能。

我們在介紹正則表達(dá)式特殊字符的時(shí)候,提到過圓括號()。它們的作用就是分組。當(dāng)你在正則表達(dá)式中配對的使用圓括號時(shí),就會(huì)形成一個(gè)分組,一個(gè)正則表達(dá)式中可以包含多個(gè)分組。分組通過編號0, 1, 2, …來區(qū)分。編號0的分組是匹配的整體,其他編號根據(jù)括號的順序來確定。

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

在C++中,分組叫做子匹配(sub_match)。std::sub_match 這個(gè)類型只有一個(gè)默認(rèn)構(gòu)造函數(shù),通常你不會(huì)主動(dòng)創(chuàng)建它,而是使用std::match_results的接口來獲取它的對象。

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

代碼示例:

這段代碼說明如下:

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

這段代碼輸出如下:

稍微深入一點(diǎn)的內(nèi)容

同一個(gè)符號的不同含義

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


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


相反的,問號?和點(diǎn)號.不在字符組內(nèi)部的時(shí)候才是特殊字符。因此[?.]中的這兩個(gè)符號僅僅代表這兩個(gè)字符自身。


還有,字符^出現(xiàn)在字符組中的時(shí)候表示的是否定,例如:[a-z]和[^a-z]表示的是正好相反的字符集。但是當(dāng)字符^不是用在字符組中的時(shí)候,它是一個(gè)錨點(diǎn),具體內(nèi)容下文會(huì)說到。

量詞的占有欲

還是以content.txt的內(nèi)容為基礎(chǔ),現(xiàn)在假設(shè)我們的目標(biāo)是:找出所有雙引號中的內(nèi)容。

根據(jù)之前的知識(shí),你可能很輕松就寫出了下面這個(gè)正則表達(dá)式:

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

但是當(dāng)你運(yùn)行程序的時(shí)候卻發(fā)現(xiàn)它可能有點(diǎn)問題。它捕獲的結(jié)果是:

為什么?其實(shí)很簡單,因?yàn)殡p引號本身也可以與.匹配。上面這個(gè)正則表達(dá)式的含義是:匹配一個(gè)兩端是雙引號,中間是任意文字的內(nèi)容。


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


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

而將整個(gè)文本交給正則表達(dá)式的時(shí)候,它找出了最長的那個(gè)串??梢姡鹊恼齽t表達(dá)式太過“貪婪”(greedy)。是的,量詞在默認(rèn)情況都是貪婪的。即:它們會(huì)盡可能多的占有內(nèi)容。


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


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


現(xiàn)在它找到的是下面兩個(gè)匹配:

小結(jié)一下:

錨點(diǎn)

錨點(diǎn)是一類特殊的標(biāo)記,它們不會(huì)匹配任何文本內(nèi)容,而是尋找特定的標(biāo)記。你可以簡單理解為它是原先表達(dá)式的基礎(chǔ)上增加了新的匹配條件。如果條件不滿足,則無法完成匹配。

錨點(diǎn)主要分為三種:

下面是代碼示例:

它的輸出如下:

環(huán)視

現(xiàn)在假設(shè)我們有下面兩個(gè)需求:

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

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

以上的解法都是分兩步完成。但實(shí)際上,借助環(huán)視(lookaround)我們可以一步就完成任務(wù)。

環(huán)視是對匹配位置的附加條件,只有條件滿足時(shí)才能完成匹配。環(huán)視有:順序(向右),逆序(向左),肯定和否定一共四種:

環(huán)視說起來有些拗口,但看具體的例子就容易理解了:

這段代碼并不復(fù)雜所以就不多做說明,它的輸出結(jié)果如下:

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


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


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


剛開始的時(shí)候,搜索的位置是第一個(gè)字符的前面:

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

img

這個(gè)過程可以一直進(jìn)行,直到匹配完"some"

img

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

img

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

img


在線客服 雙翌客服
客服電話
  • 0755-23712116
  • 13822267203