開源閱讀替換教程&正則替換五步走(中)


四、限制——限制生效范圍
1.首尾:
? ? ? “^”匹配字符串的開始位置,“$”匹配結(jié)束位置,如^ab$匹配a開頭b結(jié)尾的字符串,ab則不要求a是開頭和b是結(jié)尾,注意^和$本身不匹配內(nèi)容。
2.斷言:
? ? ? ?斷言也是匹配位置而非內(nèi)容,斷言其實(shí)就是判斷前后文是否符合條件,只有符合條件才能進(jìn)行匹配。分別有前向否定(?<!)、前向肯定(?<=)、后向否定(?!)、后向肯定(?=)這四種情況。
? ? ? 例如示例②和③里面的“(?<=[ ? \s])”表示只有前面是空格符才能進(jìn)行匹配,常用于判斷首行縮進(jìn)的段落開頭,不匹配空格符。還有示例⑤中的“(?<=[梅荷])”表示判斷前面必須是“梅”或“荷”,但不匹配“梅”與“荷”;“(?=[朵枝])”表示后面必須是“朵”或“枝”;“(?![a-z])”表示后面不能是小寫字母。
? ? ? ?斷言的使用頻率極高,它是提高匹配精度和減少錯(cuò)誤匹配的最常用方式。追加原文到正則中雖然也能提高匹配精度,但也減小了適用范圍,并且也無法做到像斷言一樣只判斷前后內(nèi)容而不匹配該內(nèi)容。
3.分組:
? ? ? ?早在數(shù)學(xué)中我們就學(xué)過括號(hào)()可以將算式進(jìn)行分組,表示括號(hào)內(nèi)是一個(gè)整體,在正則里面同樣有如此效果。正則里的括號(hào)()不匹配括號(hào)本身,匹配本身需要反斜線\轉(zhuǎn)義。
? ? ? ?在寫正則的過程中,如果前面的正則匹配到了一段原文,后面要想匹配一段相同的原文該怎么辦呢?我們前面所學(xué)正則的匹配思路,無一不是按照原文內(nèi)容的形式和位置來思考的,而現(xiàn)在要求在匹配到一段原文的基礎(chǔ)上再匹配一段與之相同原文,這涉及對(duì)內(nèi)容本身進(jìn)行匹配,顯然超出了之前所學(xué)的范疇。此時(shí)分組就發(fā)揮了作用。
? ? ? 例如前文提到的原文出現(xiàn)多處重復(fù)的情況,我們想匹配重復(fù)的內(nèi)容。
原文:
? ? “斗之力,三段!”
? ? “斗之力,三段!”
? ? 天地不仁,以萬物為芻狗!
? ? 天地不仁,以萬物為芻狗!
替換規(guī)則:([^\n]+)\s*\n\s*\1
? ? ? 規(guī)則中“[^\n]+”匹配了任意一個(gè)自然段的內(nèi)容,“\s*\n\s*”匹配了段前段尾出現(xiàn)的空格和換行,“\1”則是對(duì)第一個(gè)括號(hào)匹配的原文內(nèi)容進(jìn)行引用,即與前面“.*”匹配到的內(nèi)容相同。這樣我們就能匹配到所有重復(fù)的自然段。此外,“[^\n]+”也可寫成“.+”,但“+”不可換成“*”。
? ? ?在匹配到重復(fù)段落之后,我們自然需要去掉多余段落,因?yàn)橛小癨1”這種引用原文的效果,“替換為”的內(nèi)容也就不難寫了。但“替換為”里面用“$1”而非“\1”,這也是引用第一個(gè)括號(hào)匹配到的原文。
替換規(guī)則:([^\n]+)\s*\n\s*\1
替換為:$1
? ? ? 通過上述分析,以上規(guī)則將把所有連續(xù)出現(xiàn)兩次的重復(fù)段落只保留第一個(gè)。另外還有\(zhòng)2、\3、\4……以及$2、$3、$4……引用與之相同的相應(yīng)括號(hào)匹配到的原文。
? ? ? 由此獲得啟發(fā),段落內(nèi)的異常換行也有了解決方案。這種異常換行主要表現(xiàn)為在非結(jié)尾標(biāo)點(diǎn)處換行,也就是在不是句號(hào)、問號(hào)、感嘆號(hào)、反引號(hào)等位置換行,或者說在無標(biāo)點(diǎn)、逗號(hào)、分號(hào)、正引號(hào)、破折號(hào)、頓號(hào)、波浪號(hào)等位置換行。
替換規(guī)則:([^。\.!!"”??\s])\s*\n\s*(.*)
替換為:$1$2
