斷言
判斷某個位置左側或者右側是否符合匹配。常見斷言有三種,單詞邊界、行起始/結束位置、環視。閱讀本章前,建議先下載我於CSDN上傳的示例代碼,下載無需分數,下載鏈接。
1.單詞邊界
正則表達式"\b"用於匹配單詞的邊界。實際上他匹配一個這樣的位置,這個位置一邊匹配"\w",一邊不匹配"\w"。例如,輸入字符串"I Love China!",若用正則表達式"\b\w+\b"來匹配,則能捕獲子字符串"I","Love","China"。
2.行起始/結束位置
行起始位置可用"^"匹配,行結束位置可用"$"匹配。這個看起來十分簡單,但是有一點需要十分注意。當他們在單行模式下和在多行模式下能匹配的位置是不同的。起初在我學習正則表達式時,誤以為單行模式是將輸入的字符串看成是單行的,多行模式則看成多行。其實后來通過代碼的測試並且看了一些其他的博客,才理解其實單行模式和多行模式,只是影響某一些正則表達式字符所能匹配的字符串而已。下面做出我對單行模式和多行模式的理解。
首先,通過看資料得知在Windows平台下,每一行的換行符總是"\r\n"這樣的。而其他平台的換行符則是"\n"這樣的。
單行模式下,"^"比較簡單,即匹配文本的最開始的位置,不管你這個輸入文本是單行的還是多行的。多行模式下,"^"除了具有單行模式的能力外,還能匹配"\n"之后的位置。但是不能匹配"\r"后面的位置,當時看了某篇博文誤導了好久。Regex類下的Replace方法,對匹配的字符串替換成你想要的字符串。若匹配的是位置,這實際上是在該位置上添加你想要的字符串。
Regex regex = new Regex("^");//默認所有都為單行模式 string replace = regex.Replace("abc\n", "|");//"|abc\n" 該值通過斷點得出而非控制台 regex = new Regex("^", RegexOptions.Multiline);//多行模式 replace = regex.Replace("abc\n", "|");//"|abc\n|" 該值通過斷點得出而非控制台
單行模式下,"$"匹配文本的最后位置,其實這里說最后位置是不准確的,我無法給出一個准確的詞語描述,只能同過代碼來讓你們注意某些事情。多行模式下,同樣除了具有單行模式的能力外還能匹配"\n"之前的位置,並且也是不能匹配"\r"前面的位置。
Regex regex = new Regex("$");//默認所有都為單行模式 string replace = regex.Replace("abc\nabc\n", "|");//"abc\nabc|\n|" 該值通過斷點得出而非控制台 replace = regex.Replace("abc\nabc\nabc", "|");//"abc\nabc\nabc|" 該值通過斷點得出而非控制台 regex = new Regex("$", RegexOptions.Multiline);//多行模式 replace = regex.Replace("abc\nabc\n", "|");//"abc|\nabc|\n|" 該值通過斷點得出而非控制台
看到以上結果了嗎,單行模式下的"$",如果是以"\n","\r\n"結尾的,除了匹配他們之后的位置,也會匹配他們之前的位置,導致得出第一個replace的結果。如果不以"\n","\r\n"結尾的則沒有爭議的在最后位置上匹配了。多行模式下同樣有這個問題。為了解決這個問題,可以用"\z"來保證匹配字符串的最后一個位置,但是也不再有多行模式下的能力了。
Regex regex = new Regex(@"\z");//默認所有都為單行模式 string replace = regex.Replace("abc\nabc\n", "|");//"abc\nabc\n|" 該值通過斷點得出而非控制台 replace = regex.Replace("abc\nabc\nabc", "|");//"abc\nabc\nabc|" 該值通過斷點得出而非控制台 regex = new Regex(@"\z", RegexOptions.Multiline);//多行模式 replace = regex.Replace("abc\nabc\n", "|");//"abc\nabc\n|" 該值通過斷點得出而非控制台
3.環視
環視的作用是,當匹配到某個位置的時候,我們要確定該位置的左邊或右邊是否匹配我們設定的正則表達式,若是,則該位置匹配,繼續完成后面的匹配,若否,則不匹配,也無需繼續后面的匹配了。環視語法:往右環視,即順序環視"(?=regStr)","(?!regStr)",其中"="表示肯定,"!"表示否定,即要不要匹配的問題。往左環視,即逆序環視"(?<=regStr)","(?<!regStr)"。
例子1:外國人的數字通常喜歡用","去分隔,如有一個數字在中國是這樣的"123456789",在過外就變成這樣了"123,456,789"。我們可以總結出這樣的規律,在這樣一個位置上添加",",該位置的右邊必須是3倍個數的數字,該位置的左邊還必須有數字。這樣,我們寫出了正則表達式"(?<=\d)(?=(?:\d{3})+)"
Regex regex = new Regex(@"(?<=\d)(?=(?:\d{3})+)"); Console.WriteLine(regex.Replace("123456789", ","));//1,2,3,4,5,6,789
嗯,看來我們的思維還不夠縝密,並不能一次編寫正確的正則表達式,不過沒關系,出現bug后我們可以立刻修正bug。導致這個bug的原因在於順序環視時,當我們環視匹配完3倍個數的數字后,還必須保證他的后面再也沒有數字了。找到問題的關鍵,我們來寫出新的正則表達式"(?<=\d)(?=(?:\d{3})+(?!\d))"。在環視內部可以再嵌套一個環視。
Regex regex = new Regex(@"(?<=\d)(?=(?:\d{3})+(?!\d))"); Console.WriteLine(regex.Replace("123456789", ","));//123,456,789 Console.WriteLine(regex.Replace("1234567890", ","));//1,234,567,890
例子2:當我們得到一個這樣無序的,包含數字和字母的字符串"1sf2sdf333sf55ew2156we98552af3sd5fas6654sdfa65assfd56"時,我們可以相當容易的用"\d+"來捕獲其中的數字,但如果我不要求捕獲這些數字,而是要求將數字用括號括起來,並將字符串返回怎么辦。思路,我們要匹配的是一個位置,並在這個位置上加上括號。這個位置可以很簡單的想到,他的一邊是數字,一邊不是數字。當然,我們要分兩步實現這個功能,第一步,先用正則表達式"(?<!\d)(?=\d)"匹配數字開始的位置。第二部,用正則表達式"(?<=\d)(?!\d)"匹配數字結束的位置。是不是十分方便,用這么少的代碼便完成了我們以前不敢想的功能。
Regex regex = new Regex(@"(?<!\d)(?=\d)");//匹配數字開頭 string replace = "1sf2sdf333sf55ew2156we98552af3sd5fas6654sdfa65assfd56"; replace = regex.Replace(replace,"(");//"(1sf(2sdf(333sf(55ew(2156we(98552af(3sd(5fas(6654sdfa(65assfd(56" regex = new Regex(@"(?<=\d)(?!\d)");//匹配數字結尾 replace = regex.Replace(replace, ")");//"(1)sf(2)sdf(333)sf(55)ew(2156)we(98552)af(3)sd(5)fas(6654)sdfa(65)assfd(56)" Console.WriteLine(replace);
斷言暫時介紹這么多,謝謝!