每種語言對正則的支持略有不一樣, 這裏咱們主要說的是Java對正則表達式的支持.html
正則表達式(Regular Expression), 常簡寫爲regex, 是定義搜索模式的一組字符串 (用一組字符描述了一個字符串規則). 一般用於字符串查找和字符串的校驗.java
判斷手機號字符串的正則表達式.
籠統講手機號的特色: 以1開頭, 後面是10位數字. 因此咱們寫一個簡單判斷是否知足這個條件的正則表達式:正則表達式
^1\d{10}$
其中, ^
表明起始位置, $
表明結尾位置. \d
表明是一個數字字符, 後面大括號裏面的數字, 表明它前面的元素會出現10次.express
咱們能夠簡單用一行代碼驗證下:api
System.out.println("13436417560".matches("^1\\d{10}$"));
注, 因 Java 代碼裏面反斜槓起轉義做用, 例如
\t
表明tab
, 因此在 Java 代碼裏, 兩個反斜槓才能表示出一個反斜槓.oracle
正則表達式經過反斜槓來進行轉義. 例如:測試
點字符.
表明任意字符, 而\.
表明真正的小數點.
\d
表明一個數字字符, 而 \\d
表明一個反斜槓和一個字符d
.
\\
表明一個反斜槓字符.fetch
經常使用字符 | 意義 |
---|---|
x | 普通字符 x |
^ | 表示字符串起始位置 |
$ | 表示字符串結尾位置 |
\\ |
反斜槓字符 |
\t | tab字符 |
\n | 換行字符 |
\r | 回車字符 |
. | 表明任意字符(默認不會匹配\r和\n, 須要配置才匹配) |
[abc] | 方括號表示其中的任意字符. 方括號中任意字符(多是a 或 b 或 c) |
[^abc] | 不在方括號中的任意字符 |
[a-zA-Z] | 任意字母, 包括大寫和小寫 |
\d | 任意數字 [0-9] |
\D | 任意非數字 [^0-9] |
\s | 任意空字符 [ \t\n\x0B\f\r] |
\S | 任意非空字符 [^\s] |
\w | 任意組成單詞字符 [a-zA-Z_0-9] |
\W | 任意非組成單子字符 [^\w] |
x? | 表明x字符不存在或者存在1次(最多存在1次) |
x* | 表明x字符不存在或者存在任意次 |
x+ | 表明x字符至少存在1次 |
x{5} | x字符存在5次 |
x{3,5} | x字符存在3到5次 |
正則表達式查找能夠解決普通查找只能根據"特定文本"查找的問題.atom
示例:.net
假設如今有一堆 JSON 日誌, 咱們須要查出 cabin
以 X
開頭的日誌(X後面只可能跟數字): 假設日誌以下:
..."from":"PEK", "cabin":"Y"... ..."from":"SHA", "cabin":"X"... ..."from":"XIY", "cabin":"X2"... ..."from":"XIY", "cabin":"Y2"...
咱們提取的正則表達式是:
cabin":"X\d*"
使用正則表達式能夠校驗文本是否符合必定規範. 例如一開始提到的手機號格式校驗. 還有郵箱格式校驗等. 也能夠對身份證格式進行簡單的校驗.
咱們可使用正則表達式將文本中的一部分提取出來. 主要用到了正則表達式中的 capturing group
概念.
正則表達式中可使用小括號將表達式分紅多個捕獲組. 分組之後, 能夠經過捕獲組號, 取出對應匹配的內容. 經過數左半括號便可得到組號. 例如:
((A)(B(C)))
上面正則表達式對應的捕獲組信息以下: 捕獲組號 | 對應內容 ---|--- 1 | ((A)(B(C))) 2 | (A) 3 | (B(C)) 4 | (C)
第0組老是表明整個正則表達式.
示例代碼以下:
Pattern pattern = Pattern.compile("((A)(B(C)))"); Matcher matcher = pattern.matcher("XXXABCDEF"); if (matcher.find()) { for (int i = 0; i <= matcher.groupCount(); i++) { System.out.println("group " + i + " : " + matcher.group(i)); } }
輸出爲:
group 0 : ABC group 1 : ABC group 2 : A group 3 : BC group 4 : C
其餘應用示例:
從大量日誌中, 獲取天天的車次號, 並計數.
在Java中, Java7之後, 能夠爲 capturing-group 命名. 取的時候能夠根據名字來取. 命名示例以下:
(?<Name>[Pp]attern)
經過在普通 capturing-group 的最前面, 添加 ?<xxx>
來指定名字. 使用示例以下:
public static String fetchOneNamedGroup(String src, String regex, String groupName) { Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); Matcher matcher = pattern.matcher(src); if (matcher.find()) { return matcher.group(groupName); } return null; } public static void main(String[] args) throws Exception { String sourceType = fetchOneNamedGroup("sourceType:PC,name:test", "sourceType:(?<sourceType>\\w+)", "sourceType"); System.out.println(sourceType); } // 結果是"PC"
注意, capturing-group 的名字必須知足條件:
[A-Za-z][A-Za-z0-9]*
咱們要從Hello World
中獲取H和l以及之間的字符 Hel
. 因而, 咱們寫了以下正則表達式:
H.*l
可是結果卻並非咱們想要的:
Hello Worl
仔細分析發現, Hello Worl
這個結果也一樣知足咱們的正則表達式.
如今問題在於咱們能夠認爲 Hel
是咱們要的結果(最小匹配), 也能夠認爲 Hello Worl
是第一個H和最後一個l之間的字符(貪婪匹配).
正則表達式默認狀況下是貪婪匹配模式. 想要最小匹配, 只須要在貪婪匹配模式符後面加一個?
, 便可轉化爲最小匹配.
例如:
咱們使用H.*?l
的結果就是最小匹配:
Pattern pattern = Pattern.compile("H.*?l"); Matcher matcher = pattern.matcher("Hello World"); if (matcher.find()) { System.out.println(matcher.group(0)); } // 結果: Hel
Java裏面經過 java.util.regex.Pattern
實現了對正則的支持.
Java裏面的正則和grep命令裏面的就略有不一樣, 例如在Java裏面, 星號 * 表明0個或多個, 而在 grep 裏面, * 表明任意內容.
默認狀況下, .
在Java不會匹配換行符. 也就是 .*
只能匹配一行. 若是須要經過 .*
匹配多行狀況, 能夠開啓 DOTALL
mode.
示例:
public static String fetchGroup1(String src, String regex) { Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); Matcher matcher = pattern.matcher(src); if (matcher.find()) { return matcher.group(1); } return null; }
String 中有幾個直接正則表達式的方法, 使用方便, 同時能夠用於測試.
boolean matches (String regex) String replaceAll (String regex, String replacement) String replaceFirst (String regex, String replacement) String[] split (String regex) String[] split (String regex, int limit)
注意, replace, replaceFirst 和 replaceAll 的區別.
Java字符串經過反斜槓來標示特殊字符, 而正則表達式一樣經過反斜槓來專業, 致使代碼中的正則表達式可讀性極差.
匹配pattern但不獲取匹配結果, 也就是說這是一個非獲取匹配. 儘管匹配, 將此group匹配結果不保存, 不做爲最終結果返回.
示例:
https://stackoverflow.com/questions/tagged/regex 正則表達式: (https?|ftp)://([^/\r\n]+)(/[^\r\n]*)? 匹配結果: Match "https://stackoverflow.com/questions/tagged/regex" Group 1: "https" Group 2: "stackoverflow.com" Group 3: "/questions/tagged/regex" 若是並不在乎用的什麼協議, 可是 http/https/ftp 協議是使用括號括起來並用了|鏈接符. 若是想把這個group的結果給捨棄了, 則經過費獲取匹配: (?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)? 結果是: Match "https://stackoverflow.com/questions/tagged/regex" Group 1: "stackoverflow.com" Group 2: "/questions/tagged/regex"
參考: https://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group-what-does-do
預查不消耗字符,也就是說,在一個匹配發生後,在最後一次匹配以後當即開始下一次匹配的搜索,而不是從包含預查的字符以後開始。
參考:
bar(?=bar) finds the 1st bar ("bar" which has "bar" after it) bar(?!bar) finds the 2nd bar ("bar" which does not have "bar" after it) (?<=foo)bar finds the 1st bar ("bar" which has "foo" before it) (?<!foo)bar finds the 2nd bar ("bar" which does not have "foo" before it)
https://stackoverflow.com/questions/2973436/regex-lookahead-lookbehind-and-atomic-groups
System.out.println("www".replaceAll("a?", "替換")); // 結果是: 替換w替換w替換w替換
https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html#sum
https://en.wikipedia.org/wiki/Comparison_of_regular_expression_engines