正規表示式其實還是蠻簡單的,因為它沒有很強的邏輯在裡頭,我們只需要按照它的規則從頭開始依次去讀就好了
話雖這麼說,我相信很多人一開始接觸正則,當看到那一連串「亂七八糟」的字元時,都會一臉懵逼,這是啥玩意?然後就自然而然的認為這東西很複雜,接著打心底就牴觸使用正則,然後開始在網上搜看是否有現成的表示式可用,或者直接寫**來實現字元匹配。
我自己就是這樣
最近終於花時間把這塊東西看了下,下面是學習總結,便於後續在淡忘時回頭溫故
字串匹配,無非就是基於如下規則:
1:從被匹配字串中確認起始匹配位置
2:基於該位置,對後續字元依次逐個進行匹配,如果全部相等,則匹配成功,否則失敗
正規表示式也是基於如上規則來進行匹配的
先來看乙個簡單的例子:
string str = "d123te:testadad46587:asd-test111test";
pattern pt = pattern.compile("test");
matcher match = pt.matcher(str);
while (match.find())
輸出結果:
test
test
test
可以把整個表示式作為乙個分組,那麼這個分組會在目標字串中從頭開始匹配,這個例子很明顯,test匹配三次
對於分組內部表示式的匹配,則是對表示式所描述的字串與目標索引下的字元做完整的匹配,如果全部匹配,則ok
拿這個例子來說,一開始d跟t就不符,那肯定pass,然後匹配索引+1,繼續往前走,發現te匹配,但是s跟:不匹配,繼續往前走,直到碰到跟test完整匹配的,然後儲存匹配結果,然後繼續匹配,直到結束
正則的基礎匹配規則介紹完了
通過上面的描述,我們可以得出表示式所能定義的:
1:大分組匹配的起始位置
2:目標字串的描述,包括字元型別,字元長度等等
^ 行的開頭
$ 行的結尾
\b 單詞邊界, 就是不是單詞字元的地方。
分組斷言
邊界匹配符只能出現在表示式的開始或者結束位置,比如:
^aa$
\baaa
aaaa\b
分組斷言也可以指定要匹配的開始位置,這個後面再介紹
起始位置確定了,接下去基於該位置開始逐個比對,這就需要在表示式裡逐個描述要匹配的字元
描述方式有三種:
1:字元
就是顯示的描述要匹配的字元,比如a,b,c,-,@等等
2:字元類
描述要匹配的某一類字元
[abc] a、b 或 c(簡單類)
[^abc] 任何字元,除了 a、b 或 c(否定)
[a-za-z] a到 z 或 a到 z,兩頭的字母包括在內(範圍)
[0-9] 0到9的字元都包括
. 任何字元。我的就是.字元本身,怎麼表示呢? \.
\d 數字:[0-9]
\d 非數字:[^\d]/[^0-9]
\w 單詞字元:[a-za-z_0-9]
\w 非字元[^\w]
[abc]其實等同於[a|b|c], 同樣的,[a-z0-9]也等同於[a-z|0-9],都是或的關係
單個描述某一字元,肯定不夠,還需要量詞來描述要匹配的多個字元
量詞預設都是取重複的上限
"*"
重複零次或更多 例如
"aaaaaaaa"
匹配字串中所有的
a 正則:
"a*"
會出到所有的字元
"a"
"+"重複一次或更多次 例如
"aaaaaaaa"
匹配字串中所有的
a 正則:
"a+"
會取到字元中所有的
a字元,
"a+"
與"a*"
不同在於
"+"至少是一次而
"*"可以是0次,
"?"重複零次或一次 例如
"aaaaaaaa"
匹配字串中的a正則
:"a?"
只會匹配一次,也就是結果只是單個字元a
""重複n
次 例如從"aaaaaaaa"
匹配字串的
a並重複3次
正則:"a"
結果就是取到3個
a字元"aaa";
""重複n
到m次
例如正則
"a"將
a重複匹配
3次或者4次
所以供匹配的字元可以是三個
"aaa"
也可以是四個
"aaaa"
正則都可以匹配到
""重複
n次或更多次,無上限
上面的量詞預設都是指定重複上限的,那就需要乙個限定符,可以指定重複下限,意思是,盡量少重複或者不重複
懶惰限定符就是在上面的量詞符號後面加個 ? 號
比如:*?, +?, ??, ?, ?, ?
加了懶惰限定符後,預設都是取重複的下限
上面說過整個表示式是乙個大的分組,其實在表示式內部,還可以指定分組,這些分組在表示式被解析的時候,會從0開始依次分配組號
在正則中,用()來表示乙個分組
分組的作用我覺得有兩個:
1:將某些特定的子表示式分成一組,便於後續表示式復用和以組為單位進行邏輯操作
2:根據組號,對特定組的表示式匹配結果進行後向引用
看個例子:
string str = "15916898088";
pattern pt = pattern.compile("^(15([0-3]|[5-9]))");
matcher match = pt.matcher(str);
while (match.find())
這個例子表示式有三個分組,分別是:
0:^(15([0-3]|[5-9]))
1:(15([0-3]|[5-9]))
2:([0-3]|[5-9])
第乙個分組就是整個表示式,對應索引為0,內部分組的索引,則按照從左到右,從外到內依次排列
上面表示式的執行結果為:159,9
接著看通過分組索引後向引用的例子:
string str = "15916898088";
pattern pt = pattern.compile("^(15([0-3]|[5-9])).*\\2");
matcher match = pt.matcher(str);
while (match.find())
表示式中的\2表明要引用分組2的匹配結果,這裡很明顯為9
所以這個表示式的執行結果為:1591689,9
如果我們只想匹配結果,不希望捕獲匹配的結果和對分組分配組號,只需要在分組表示式前面加上?: 就可以了,比如:
^(15(?:[0-3]|[5-9])).*
上面在匹配起始位置中提過,可以通過分組斷言來指定起始位置,其實說白了就是:
先通過乙個分組表示式找到匹配的字串,然後根據該匹配字串的位置,來決定後續表示式的起始位置
舉個例子:
「 iloveyouforever」
現在如果有個分組表示式(you), 我們通過這個表示式的結果,至少可以確定兩個位置,乙個是you前面的position,緊接著e,還有就是you後面的position,緊接著f
所以,斷言語法就是用來確定上面兩個位置的
(?=you)
匹配
you前面的
position
,比如:
表示式
"(?=you).*"
的匹配結果為:
youforever
表示式
".*(?=you)"
的匹配結果為:
ilove
(?<=you)
匹配
you後面的
position
表示式
"(?<=you).*"
的匹配結果為:
forever
表示式
".*(?<=you)"
的匹配結果為:
iloveyou
另外(?!you)和(?
則是表示式取反,
position
的確定則跟上面一致
over
深入淺出之正規表示式
1.什麼是正規表示式 基本說來,正規表示式是一種用來描述一定數量文字的模式。regex代表regular express。本文將用 來表示一段具體的正規表示式。一段文字就是最基本的模式,簡單的匹配相同的文字。2.不同的正規表示式引擎 正規表示式引擎是一種可以處理正規表示式的軟體。通常,引擎是更大的應...
正規表示式深入淺出 深入淺出資料分析
618買的 深入淺出資料分析 前段時間終於看完了,發現閒魚上的市場還不錯,就擺在上面 了,在被賣出之前,我寫了一篇總結,沒想到的是,文章儲存失誤了,現在書已經賣出去了,想重寫只能靠回憶了。深入淺出資料分析 這本書給我最深的感受是excel上 資料分析 和 規劃求解 兩個選項的使用,在這本書之前我知道...
深入淺出之正規表示式 2
5.字符集字符集是由一對方括號 括起來的字元集合。使用字符集,你可以告訴正規表示式引擎僅僅匹配多個字元中的乙個。如果你想匹配乙個 a 或乙個 e 使用 ae 你可以使用 匹配gray或grey。這在你不確定你要搜尋的字元是採用美國英語還是英國英語時特別有用。相反,將不會匹配graay或graey。字...