每种语言对正则的支持略有不一样, 这里咱们主要说的是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