正则表达式从入门到实战
該文章來自?https://mp.weixin.qq.com/s?__biz=MzIwNjEwNTQ4Mw==&mid=2651579105&idx=3&sn=781da0d036bcc1ab464c599d1ab24b6b&chksm=8cd9fc05bbae75130be2926f29c2f0f42ad9b6e668bd1831cb36bcc060248e6963c4fce830e2&scene=21#wechat_redirect
在開發的過程中,字符串處理往往很頻繁。比如我們經常會對用戶輸入做校驗:手機號,身份證號,郵箱,密碼,域名,IP 地址,URL 或者其他與字符串相關校驗的業務場景。
正則表達式就是一種強大而靈活的文本處理工具,正則可以很好的解決這類字符串校驗問題。掌握正則表達式,就能大大提高開發過程的效率。
正則表達式(Regular Expression)在代碼中常常簡寫為regex。正則表達式通常被用來檢索、替換那些符合某個規則的文本,它是一種強大而靈活的文本處理工具。
本場Chat將從2個方面入手:
-
學習正則表達式的語法規則,并介紹開發過程中使用正則表達式的流程,提供一款正則工具。
-
通過實現 5 個小功能練習使用正則,然后解決 2 個實際開發中遇到的問題。
通過本場 Chat 的學習,從零開始輕松掌握正則表達式,并且具備解決實際項目問題的能力。
一款好用的正則工具
給大家推薦一個正則工具“RegexBuddy”,(?http://pan.baidu.com/s/1jHRshpW?"正則工具RegexBuddy"?),密碼:c509
說明:
為了便于理解,文章所有示例的正則表達式用“regex=正則”表示,“=”號后面就是正則表達式,為了閱讀效果,我會把工具GegexBuddy里匹配到的字符串截圖展示。
比如:regex=study regex,其中 study regex 就是一個正則,它匹配字符串“study regex”,如下:
正則表達式的語法規則
學習正則表達式語法,主要就是學習元字符以及它們在正則表達式上下文中的行為。
元字符包括:普通字符、標準字符、特殊字符、限定字符(又叫量詞)、定位字符(也叫邊界字符)。下面分別介紹不同元字符的用法。
普通字符
字母[a-zA-Z]、數字[0-9]、下劃線[-]、漢字,標點符號:
-
匹配字母a可以 regex=a
-
匹配字母b可以 regex=b
-
匹配字母a或者b可以 regex=a|b,這個正則引入一個特殊字符“|”,專業名稱為“或”,你也可以叫它“豎線”,它表示“或”的意思。
-
匹配字母a或者b或者c可以 regex=a|b|c
-
匹配字母a或者b或者c或者d可以 regex=a|b|c|d
-
如果匹配所有26個字母,這種寫法明顯很二了。
這里引入兩個特殊字符方括號“[ ]”和中劃線“-” “[ ]”,專業名稱為“字符集合”,你也可以叫它“方括號”。
“-” ,表示“范圍”,你也可以叫它“到”,regex=[A-Z] 匹配從A到Z26個字母中的任意一個。
那么匹配字母a或者b或者c或者d可以 regex=[abcd]。
匹配數字1到8的任意數字可以 regex=[1-8],這樣就不會匹配到0與9這2個數字了,如下:
標準字符集合
標準字符集合是能夠與“多種普通字符”匹配的簡單表達式,比如:\d、\w、\s。
匹配數字0到9的任意數字可以 regex=[0-9] 也可以 regex=\d。標準字符集要注意區分大小寫,大寫是相反的意思。
regex=\D,則匹配非數字字符,即不能匹配數字0到9,如下:
常用的標準字符說明 標黃的要熟記。
特殊字符
特殊字符在正則表達式中表示特殊的含義,比如:*,+,?,\,等等。
-
“\”是轉義字符,用于匹配特殊字符
-
匹配反斜杠“\”可以 regex=\\,因為“\”是特殊字符,所以需要在它前邊再加一個“\”進行轉義
-
匹配星號“*”,可以 regex=\,因為“\”是特殊字符,所以需要在它前邊再加一個“\”進行轉義
常用的特殊字符說明 標黃的要熟記。
限定字符
限定字符又叫量詞,是用于表示匹配的字符數量的。
-
匹配任意1位數字可以 regex=\d
-
匹配任意2位數字可以 regex=\d\d
-
匹配任意3位數字可以 regex=\d\d\d
匹配任意8位數字,再這么寫就有點二了。這里引入用于表示數量限定字符“{n}”。“{n}”,n是一個非負整數,匹配確定的n次。
注意:regex=\d\d{3} 匹配任意4個數字不是6個,量詞只對它前面的字符負責, regex=\d\d{3} 匹配的內容如下:
-
匹配任意8位數字可以 regex=\d{8}
-
匹配任意8位以上的數字可以 regex=\d{8,}
-
匹配任意1到8位以上的數字可以 regex=\d{1,8}
從上圖,我們可以看到 regex=\d{1,8},可以匹配到任意1-8個數字,超過8位數字后,從新開始匹配。
匹配次數中的“貪婪模式”與“非貪婪模式”:
正則的匹配默認是貪婪模式,即匹配的字符越多越好,而非貪婪模式是匹配的字符越少越好,在修飾匹配字數的量詞后再加上一個問號“?”即可。
那么同樣是上面的字符串,regex=\d{1,8}?匹配到什么呢?
因為在{1,8}這個量詞后面加上了問號“?”,表示非貪婪模式,所以只能匹配到1個數字,即匹配的字符越少越好。
常用的限定字符說明 標黃的要熟記。
-
匹配0個或多個字母A可以 regex=A* 或者 regex=A{0,}
-
匹配至少一個字母A可以 regex=A+ 或者 regex=A{1,}
-
匹配0個或1字母A可以 regex=A?或者 regex=A{0,1}
匹配至少一個 Hello可以 regex=(Hello)+,匹配的效果如下:
定位字符
定位字符也叫字符邊界,標記匹配的不是字符而是符合某種條件的位置,所以定位字符是“零寬的”。
常用的定位字符:
匹配以 Hello 開頭的字符串可以 regex=^Hello
匹配以 Hello 結尾的字符串可以 regex=Hello$,如下:
匹配以H開頭以o結尾的任意長度字符串可以regex=^H.*o$,如下:
\b匹配這樣一個位置:前面的字符和后面的字符不全是\w。如果在“hello,hello1 helloregex,hello regexhello.”這個字符串里匹配regex=hello\b,匹配到的結果如下:
分析一下:為什么 hello1 匹配不了“hello\b”這個正則?
首先\b是一個定位字符,它是零寬的,標識一個位置,這個位置的前面和這個位置的后面不能全是\w,即不能全是字母數字和下劃線 [A-Za-z0-9_],而hello1的o與1之間的位置前面是o后面是1,前后全是\w,不符合\b匹配的含義,因此hello1不能匹配正則表達式 “hello\b”。
但是?bhello?可以匹配 “hello\b” 這個正則,因為 hello 的結尾的位置,前面是o,后面是空白,所以符合\b匹配的含義,因此 bhello 可以匹配 “hello\b” 這個正則。
自定義字符集合
方括號[ ]表示字符集合,即[ ]表示自定義集合,用[ ]可以匹配方括號里的任意一個字符。
regex=[aeiou] 匹配“a”,“e”,“i”,“o”,“u”任意一個字符,也就是可以匹配集合 [aeiou] 的任意一個字符。
但是,特殊字符(除了小尖角“^”和中劃線“-”外)被包含到方括號中,就會失去特殊意義,只代表其字符本身。
regex=[abc+?] 匹配“a”,“b”,“c”任意一個字符或者**“+”,“\”,“?”,即包含在自定義集合中的特殊字符“+”,“*”,“?”**失去了特殊含義,只表示其字符本身的意思。
特殊字符小尖角“^”,原本含義是匹配字符串的開始位置,如果包含在自定義集合[ ]中,則表示取反的意思。
比如:regex=[^aeiou] 匹配“a”,“e”,“i”,“o”,“u”之外的任意一個字符。
中劃線“-”,在自定義集合[ ]中,表示“范圍”,而不是字符“-”本身,regex=[a-z],匹配從a到z中26個字母中的任意一個。
除小數點“.”外,標準字符集合包含在方括號中,仍然表示集合范圍。regex=[\d.+] 匹配0-9的任意一個數字或者小數點“.”或者加號“+”
也就是說\d在自定義集合中仍然表示數字,但是小數點在字符集合中只表示小數點本身,而不是除“\r\n”之外的任何單個字符。
選擇符和分組
regex=x|y,匹配字符x或y。( )表示捕獲組,( )的作用如下:
括號中的表達式可以作為整體被修飾,用來表示匹配括號中表達式的次數,regex=(abc){2,3},可以匹配連續的2個或3個abc,如下:
括號中的表達式匹配到的內容會存儲起來,并可以獲取到括號中表達式匹配到的內容
每一對括號會分配一個編號,使用( )的捕獲根據左括號的順序從1開始自動編號,編號為0的捕獲是整個正則表達式匹配到的文本。
捕獲組( )可以把匹配的內容存儲起來,那么如何獲取( )捕獲到的內容呢,下面介紹反向引用。
反向引用 “\number”
每一對括號會分配一個編號,使用( )的捕獲根據左括號的順序從1開始自動編號。
通過反向引用,可以對分組已捕獲的字符串進行引用。“\number” 中的 number 就是組號
regex=(abc)d\1 可以匹配字符串 abcdabc,即\1表示把獲取到的第一組再匹配一次,如下:
(?:pattern)?表示非捕獲組,匹配括號中表達式匹配到的內容,但是不進行存儲匹配到的內容。
這在使用 “或” 字符?(|)?來組合一個正則的各個部分是很有用的。
例如:匹配字符 “story” 或者 “stories”,regex=stor(?:y|ies) 就是一個比 regex=story|stories 更簡略的表達式。
預搜索
預搜索,又叫零寬斷言,又叫環視,它是對位置的匹配,與定位字符(邊界字符)類似。
regex=love (?=story)匹配的結果如下(匹配 “love?” 后面是 story):
regex=love (?!story)匹配的結果如下(匹配 “love” 后面不能是 story):
運算符的優先級
正則表達式從左到右進行計算,并遵循優先級順序,這與算術表達式非常類似。下表的優先級從高到低排序。
說明:“|” 或操作是優先級最低的,它比普通字符的優先級低。因此,regex=r|loom 匹配 “r” 或 “loom”,如下:
如果想匹配 “room” 或 “loom”,請用括號創建子表達式,regex=(r|l)oom,如下:
開發過程中使用正則表達式的流程
分析所要匹配的數據特點,模擬各種測試數據;
利用正則工具,寫正則表達式與測試數據進行匹配,從而驗證你寫的正則;
在程序里調用在正則工具中驗證通過的正則表達式。
練習使用正則實現 5 個小功能
電話號碼的正則
電話號碼由數字和“-”組成
如果包含區號,那么區號為三位或四位,首位是0
區號用“-”和其他數字分割
除了區號,電話號碼為7到8位
手機號碼為11位
11位手機號碼的前2位為“13”,“14”,“15”,“17”,“18”
分析
電話號碼分為固話和手機號,首先匹配固話,然后匹配手機號。
-
固話的正則:regex=0\d{2,3}-\d{7,8}
-
手機號的正則:regex=1[34578]\d{9}
所以電話號碼的正則:
regex=(0\d{2,3}-\d{7,8})|(1[34578]\d{9})
電話號碼匹配結果如下:
身份證號碼的正則
長度:15位或者18位
如果是15位,則都是數字
如果是18位,最后一位可能為數字或字母X
分析
-
15位數字:regex=\d{15}
-
18位數字:regex=\d{18}
-
17位數字+X:regex=\d{17}X|x
所以省份證號碼的正則:
regex=(^\d{15}$)|(^\d{18}$)|(^\d{17}X|x$)
身份證號碼匹配的結果如下:
電子郵箱的正則
郵箱格式:用戶名@網址.域名
用戶名:字母、數字、下劃線組成
網址:字母、數字
域名:2-4位字母組成,1-2個域名
不區分大小寫
分析
-
用戶名:regex=\w+。
-
網址:regex=[a-zA-Z0-9]+。
所以電子郵箱的正則 regex=\w+@[a-zA-Z0-9]+(.[a-zA-Z]{2,4}){1,2}。
電子郵箱匹配的結果如下:
IP地址的正則
IP地址的格式:(1~255).(0~255).(0~255).(0~255)
分析
-
1-255的正則 regex=^([1-9]|[1-9]\d|1\d\d|2[0-5][0-5])。
-
0-255的正則 regex=^(\d|[1-9]\d|1\d\d|2[0-5][0-5]) 。
所以 IP 地址的正則 regex=^([1-9]|[1-9]\d|1\d\d|2[0-5][0-5]).((\d|[1-9]\d|1\d\d|2[0-5][0-5]).){2}(\d|[1-9]\d|1\d\d|2[0-5][0-5])$
IP地址匹配的結果如下:
日期格式的正則
日期格式:yyyy-mm-dd
分析
-
4位的年,第一位只能是1或2,regex=^([12]\d{3})
-
一年的12個月(01~09和1~12),regex=^(0?[1-9]|1[0-2])
-
一個月的31天(01~09和1~31), regex=^((0?[1-9])|((1|2)[0-9])|30|31)
所以 格式為 yyyy-mm-dd 的日期正則 regex=^([12]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|((1|2)[0-9])|30|31)$
yyyy-mm-dd 的日期匹配的結果如下:
在 Java 代碼中如何使用正則
Java 中正則相關類位于 java.util.regex 包下,主要使用2個類,如下:
Pattern 類:
-
Pattern 是正則表達式 regex 的編譯表示形式
-
代碼:Pattern pattern = Pattern.compile(regex);
Matcher 類:
-
通過解釋 Pattern 對輸入的字符串 input 執行匹配操作的引擎
-
代碼:Matcher matcher = pattern.matcher(input);
注意:在 Java 代碼中轉義字符“\”要寫成“\”才表示一個“\”。比如regex=\d,在 Java 代碼中應該寫成“\d”。
Java示例代碼:??
package regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class TestRegex { ? ?public static void main(String[] args) { ? ? ? ?String input = "Hello regex 666!"; ? ? ? ?// java中要想表示\需要通過轉義字符\進行轉義 ? ? ? ?String regex = "\\w+"; ? ? ? ?Pattern pattern = Pattern.compile(regex); ? ? ? ?Matcher matcher = pattern.matcher(input); ? ? ? ?// matches()方法,將輸入的整個字符串與給定的正則匹配 ? ? ? ?System.out.println(matcher.matches()); ? ? ? ?// 結果為:false,因為"Hello regex 666!"不全是\w ? ? ? ?String regex1 = "\\d+"; ? ? ? ?Pattern pattern1 = Pattern.compile(regex1); ? ? ? ?Matcher matcher1 = pattern1.matcher(input); ? ? ? ?// find()方法,從輸入的字符串里找出與給定的正則匹配的子串 ? ? ? ?while (matcher1.find()) { ? ? ? ? ? ?// 只要找到,則就能通過group()方法獲取到符合條件的子串 ? ? ? ? ? ?System.out.println(matcher1.group()); ? ? ? ? ? ?// 結果為:666,通過find()找到了\d,通過group()方法獲取匹配到的值 ? ? ? ?} ? ?} }
利用正則解決 2 個實際開發中遇到的問題
問題1:一鍵獲取短信驗證碼
短信驗證碼在目前的互聯網應用的非常廣泛,在一些重要操作中都需要輸入短信驗證碼來驗證身份信息。
列舉3條不同的驗證碼短信內容如下:
【京東】尊敬的用戶,634561是您本次的省份驗證碼,30分鐘內有效,請完成驗證。
【滴滴】您的驗證碼是6678,請在頁面中提交驗證碼完成驗證。
【百度】376687(動態驗證碼),請在30分鐘內填寫。
那么如何通過一個正則表達式來獲取到3個不同類型的短信內容里的數字驗證碼呢?
首先分析以上3條短信內容,找出共同點:
驗證碼都是數字,可以是4位數字,也可以是6位數字
每條短信都包含“驗證碼”3個漢字
“驗證碼”3個字與數字的順序關系,“驗證碼”3個字可以在數字前,也可以在數字后
按照以上的分析,我們就可以寫在正則工具里寫正則表達式進行驗證了。
4位數字或者6位數字,可以用 “\d{4}|\d{6}” 來匹配,我們使用捕獲組( )來獲取數字部分,即 regex=(\d{4}|\d{6})
驗證碼3個字就用“驗證碼”來匹配,regex=驗證碼
“驗證碼”3個字在數字前,可以 regex=驗證碼 \D(\d{4}|\d{6}),“驗證碼”3個字在數字后,可以 regex=(\d{4}|\d{6})\D 驗證碼,這2個表達式是或的關系,需要用到括號來組織這2個表達式,然后再用或“|”來進行選擇,即 regex=(驗證碼\D(\d{4}|\d{6}))|((\d{4}|\d{6})\D驗證碼)
由于要通過捕獲組( )來獲取數字內容,又要用括號來組織關系,因此需要把或 “|” 兩邊的表達式部分用非捕獲組 (?:) 來標記,因為我們只需要獲取數字部分的括號( )匹配到的數字。
即 regex=(?:驗證碼\D(\d{4}|\d{6}))|(?:(\d{4}|\d{6})\D驗證碼)
最后我們把分析到的表達式代入到 Java 代碼完成功能。注意在 Java 中,反斜杠需要轉義,即一杠變二杠。
Java代碼:
package regex; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class TestRegex { ? ?public static void main(String[] args) { ? ? ? ?List<String> inputList = new ArrayList<String>(16); ? ? ? ?inputList.add("【京東】尊敬的用戶,634561是您本次的省份驗證碼,30分鐘內有效,請完成驗證。"); ? ? ? ?inputList.add("滴滴】您的驗證碼是6678,請在頁面中提交驗證碼完成驗證。"); ? ? ? ?inputList.add("【百度】376687(動態驗證碼),請在30分鐘內填寫。"); ? ? ? ?String regex = "(?:驗證碼\\D*(\\d{4}|\\d{6}))|(?:(\\d{4}|\\d{6})\\D*驗證碼)"; ? ? ? ?Pattern pattern = Pattern.compile(regex); ? ? ? ?System.out.println("一鍵獲取到的驗證碼如下:"); ? ? ? ?for (String input : inputList) { ? ? ? ? ? ?Matcher matcher = pattern.matcher(input); ? ? ? ? ? ?if (matcher.find()) { ? ? ? ? ? ? ? ?for (int i = 1; i <= matcher.groupCount(); i++) { ? ? ? ? ? ? ? ? ? ?if (matcher.group(i) != null) { ? ? ? ? ? ? ? ? ? ? ? ?System.out.println(matcher.group(i)); ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ?} ? ?} }
運行效果:
問題2:判斷用戶密碼是否為強密碼
用戶設置的密碼弱,會導致信息安全問題,一般的系統都要求設置強密碼。下面是京東注冊頁面的截圖:
以京東注冊為例,京東建議使用字母、數字和符號兩種及以上的組合,6-20個字符。
下面我們通過正則表達式來完成用戶輸入的密碼是否符合密碼規則的校驗。首先分析密碼要求,如下:
密碼包括字母、數字和符號3種字符
必須包含2種及以上的字符
密碼長度6-20位
字母包括:A-Za-z,數字包括:0-9,
符號包括32個:`-=][‘;/.,~!@#$%^&()_+|}{“:?><
需要注意的是如果使用32個符號,特殊字符“\”、“[”、“]”是需要進行轉義的,為了簡單直觀,我們假設符號只有@#$3個。
進一步分析,密碼只有字母,數字,符號3種類型的字符,要求必須包含2種及以上,那么密碼組合的種類有4種(3個里面選2個+3個全選=4),即:字母+數字,字母+符號,數字+符號,字母+數字+符號。
如果從正面去考慮這個問題,那么正則會很難寫,所有我們從反向考慮:“必須包含2種及以上”的反向就是“只包含1種”,也就是說密碼要求“不能只包含1種字符”。
密碼長度6-20位,需要用到開始標記“^”和結束標記“$”,量詞{6,20}
最終分析密碼要求是:密碼從開始到結束必須 6-20 位而且不能全部是1種單一的字符
因此正則可以這么寫:
解釋:
-
^(?![A-Za-z]+$)表示從頭到位不能全是字母
-
^(?![0-9]+$)表示從頭到位不能全是數字
-
^(?![@#$]+$)表示從頭到位不能全是符號@#$
-
^[A-Za-z0-9@#$]{6,20}$表示從頭到位只能是字母數字符號@#$的集合
需要注意的是,開始符“^”和預搜索“(?!)”都是零寬的,表示位置,所以開始符“^”只需要在整個正則表達式的開始處寫一個即可。
最后我們把分析到的表達式代入到 Java 代碼完成功能。
Java 代碼片段:
密碼校驗效果:
轉載于:https://www.cnblogs.com/duguxiaobiao/p/9128822.html
總結
以上是生活随笔為你收集整理的正则表达式从入门到实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GeneratedKeyHolder的作
- 下一篇: fiddler设置https抓包