正则化表达式
简述
在 Java 中正则表达式为 String 类型,被验证的内容同样为 String 类型。通过String类中的matches方法实现内容的匹配校验。如:“被验证内容”.matches(“正则表达式”)。
正则表达式语法规则
[内容限定]{长度限定}
内容限定
在定义限定内容规则时,如果没有指定长度限定,那么默认长度为 1。
单个字符限定
[a]:表示当前内容必须是字母 a。
范围字符限定
[a-z0-9]:表示内容可以是 a-z 之间的任意字母或者 0-9之间的任意数字,不分先后。
取反限定
[^abc]表示内容不能是 a 或 b 或 c。
长度限定
在正则表达式中通过{ }来限定内容长度。
固定长度:{固定长度值};
范围长度:{最小长度值,最大长度值};
[a-z]{5}:表示内容范围为小写字母 a 到 z 且长度必须为 5;
[a-z]{2,8}:表示内容范围为小写字母 a 到 z 且长度在 2 到 8 之间,包含 2 与 8;
[a-z]{2,}:表示内容范围为小写字母 a 到 z 且最小长度为 2,最大长度无限制;
[a-z]{0,2}:表示内容范围为小写字母 a 到 z 且最小长度为 0,最大长度为 2。
长度限定符号
长度限定符号是指通过预定义符号来完成长度限定。
?:零次或一次。等同于{0,1};
+:一次或多次。等同于{1,};
*:零次或多次。等同于{0,}。
元字符
元字符又可以分为:限定符、预定义字符(字符匹配符)、选择匹配符、定位符、特殊字符以及分组组合和反向引用符。
限定符(字符长度限定符)
项目 | Value | Value | Value |
---|---|---|---|
* | 指定字符重复0次到n次(无要求)零到多 | (abc)* | 说明包含任意个abc的字符串 |
+ | 指定字符重复1次到n次(至少一次)一到多 | m+(abc)* | 至少以1个m字母开头的后面跟着任意个abc的字符串 |
? | 指定字符重复0次或者1次(最多一次)零到一 | m+(abc)? | 至少以一个m开头,后接0个或者1个abc的字符串 |
{n} | 只能输入n个字符 | [abc]{3} | 由abc这三个字母任意组合成长度为3的字符串 |
{n,} | 指定至少n个匹配 | [abcd]{3,} | 由abcd这四个字母任意组合成的至少长度为3的字符串 |
{n,m} | 指定至少n个但不多于m个匹配 | [abcd]{3,5} | 由abcd任意组合成的长度不小于3,不大于5的字符串 |
预定义字符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
[ ] | 可接收的字符列表 | [efgh] | e、f、g、h其中的任意一个字符 |
[^] | 不可接收的字符列表 | [^abc] | 除a、b、c以外的任意一个字符 |
. | 匹配除\n以外的任意一个字符 | a…b | 以a为开头,以b为结尾,中间包括两个任意字符 |
- | 连字符 | A-Z | 任意单个大写字母 |
(?i) | 匹配字母,并且字母忽略大小写 | (?i)abc | 匹配abc,ABC,aBc… |
\d | 匹配一个数字字符,等价于[0-9] | ----- | ----- |
\D | 匹配一个非数字字符,等价于[^0-9] | ----- | ----- |
\n | 匹配一个换行符 | ----- | ----- |
\r | 匹配一个回车符 | ----- | ----- |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等,注意斜杠的转义 | ----- | ----- |
\S | 匹配任何非空白字符,注意斜杠的转义 | ----- | ----- |
\t | 匹配一个制表符 | ----- | ----- |
\w | 匹配包括下划线的任何单词字符,等价于“[A-Za-z0-9_]” | ----- | ----- |
\W | 匹配任何非单词字符,等价于“[^A-Za-z0-9_]” | ----- | ----- |
注意:正则表达式中定义的带有需要转义字符的符号需要特别注意,例如\d,在定义正则表达字符串时需要写为:\d,其他等同。
选择匹配符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
| | 匹配 | 前面 或者后面的内容 | ab| cd | 匹配ab 或者 cd |
定位符
项目 | Value | Value | Value |
---|---|---|---|
\b | 匹配目标字符的边界 | han\b | 这里说的有边界是指子字符串有空格,或者目标字符串的结束位置 |
\B | 匹配目标字符的非边界 | han\B | 和\b的含义相反 |
^ | 指定起始字符 | ^ [0-9]+[a-z]* | 以至少一个数字字符开头,后面接任意个小写字母的字符串 |
$ | 指定结尾字符 | ^ [0-9]\-[a-z]+$ | 以至少一个数字字符开头,后面连接 - 字符,并且至少以一个小写字母结尾的字符串 |
特殊字符
. * + \ / ( ) $ ? [ ] ^ { } ,这些特殊字符需要用到转义字符""进行转义
分组组合和反向引用
分组组合
正则表达式可以通过”()”来进行分组,更专业的表达就是捕获组,每个完整的”()”可以分为一组,同时,”()”中还可以嵌套”()”,即组之间还可以存在更小的组,以此类推。而编号为0的组,则是正则表达式匹配到的整体,这个规则只要支持正则表达式中捕获组的语言基本上都适用。
捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。当然,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部。
比如:
在表达式((A)(B(C))),有四个这样的组:
((A)(B(C)))
(A)
(B(C))
(C)
分组就是按照括号从左至右计算得来的,可以通过调用 matcher 对象的 groupCount 方法来查看表达式有多少个分组。groupCount 方法返回一个 int 值,表示matcher对象当前有多个捕获组。
还有一个特殊的组(group(0)),它总是代表整个表达式。该组不包括在 groupCount 的返回值中。
分组作用:
将某些规律看成是一组,然后进行组级别的重复,可以得到意想不到的效果。
分组之后,可以通过后向引用简化表达式(\1 或者$1)。
示例:
作用1
对于IP地址的匹配,简单的可以写为如下形式
String pattern = “(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})”;
从这个 我们可以看出,这里有五个group,group(0)、group(1)、group(2)、group(3)、group(4),并且这几个分组的正则表达式都是一样的。我们可以对上述正则表达式进行简化。结果如下:
String pattern = “(\d{1,3})(\.(\d{1,3})){3}”;
这个表达式的意思就是把(\.(\d{1,3}))重复三次。
反向引用
Java正则表达式中的反向引用是Java提供的另一个重要功能。反向引用用于查找重复字符组。 此外可使用反向引用来重新排列输入字符串中各个元素的顺序和位置,以重新设置输入字符串的格式。
可以从正则表达式和替换字符串中引用子表达式。 每个子表达式都由一个编号来标识,并称作反向引用。
要了解反向引用,我们首先需要了解群组。正则表达式中的分组意味着将多个字符视为一个单元。通过将要分组的字符放在一组括号“()”中来创建它们。每组括号对应一个组。
反向引用很方便,因为它使我们无需重复编写就可以重复一个模式。我们可以使用\#(#是组号)来引用先前定义的组。阅读以下两个示例后,这将更有意义。
捕获分组:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegTheory01 {
public static void main(String[] args) {
String content="1996年4月23日,1999年6月28日";
String reg="(?<zidu1>\\d\\d)(?<zidu2>\\d\\d)";
Pattern pattern=Pattern.compile(reg);
Matcher matcher= pattern.matcher(content);
while (matcher.find())
{
System.out.println(matcher.group(0));
System.out.println("第一组"+matcher.group(1));
System.out.println("第一组【通过组名来取】"+matcher.group("zidu1"));
System.out.println("第二组"+matcher.group(2));
System.out.println("第二组【通过组名来取】"+matcher.group("zidu2"));
}
}
}
1996
第一组19
第一组【通过组名来取】19
第二组96
第二组【通过组名来取】96
1999
第一组19
第一组【通过组名来取】19
第二组99
第二组【通过组名来取】99
非捕获分组:
?: 这种写法就是非捕获分组,也就是看起来是分组,但实际上不是分组,不能使用matcher.group(1)来获取.匹配的内容是自渡一号,自渡二号,自渡三号这三个子字符串
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegTheory01 {
public static void main(String[] args) {
String content="自渡一号自渡二号自渡三号";
// String reg="自渡一号|自渡二号|自渡三号";
// 下面这种写法就是非捕获分组,也就是看起来是分组,但实际上不是分组,不能使用matcher.group(1)来获取
String reg="自渡(?:一号|二号|三号)";
Pattern pattern=Pattern.compile(reg);
Matcher matcher= pattern.matcher(content);
while (matcher.find())
{
System.out.println(matcher.group(0));
}
}
}
自渡一号
自渡二号
自渡三号
?=表示匹配的内容是在后面这些字符串中有的关键字的子字符串,比如,我只想找有关于自渡一号和自渡二号中的自渡关键字,如果不是这两个子字符串中的“自渡”,我是不要的。
这里只会输出自渡一号和自渡二号中的自渡,即只有两个结果!
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegTheory01 {
public static void main(String[] args) {
String content="自渡一号自渡二号自渡三号";
// String reg="自渡一号|自渡二号|自渡三号";
String reg="自渡(?=一号|二号)";
Pattern pattern=Pattern.compile(reg);
Matcher matcher= pattern.matcher(content);
while (matcher.find())
{
System.out.println(matcher.group(0));
}
}
}
自渡
自渡
?!,寻找的内容不在后面所跟着内容的字符串的子字符串里面。找到不在自渡一号,自渡二号中的“自渡关键字”
这里只会输出自渡三号中的自渡,即只有一个结果,作用跟上面的相反!
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegTheory01 {
public static void main(String[] args) {
String content="自渡一号自渡二号自渡三号";
// String reg="自渡一号|自渡二号|自渡三号";
String reg="自渡(?!一号|二号)";
Pattern pattern=Pattern.compile(reg);
Matcher matcher= pattern.matcher(content);
while (matcher.find())
{
System.out.println(matcher.group(0));
}
}
}
自渡
示例:
String regex = "\\b(\\d{3})(\\d{3})(\\d{4})\\b";
Pattern p = Pattern.compile(regex);
String source = "1234567890, 12345, and 9876543210";
Matcher m = p.matcher(source);
while (m.find()) {
System.out.println("Phone: " + m.group() + ", Formatted Phone: ("
+ m.group(1) + ") " + m.group(2) + "-" + m.group(3));
}
注:\b表示只匹配在单词边界的10个数字。
运行结果:
Phone:1234567890,Formatted Phone:(123)456-7890
Phone:9876543210,Formatted Phone:(977)654-3210
也可以通过正则替换完成:
String regex = "\\b(\\d{3})(\\d{3})(\\d{4})\\b";
String replacementText = "($1) $2-$3";
String source = "1234567890, 12345, and 9876543210";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(source);
String formattedSource = m.replaceAll(replacementText);
反向引用非常方便,因为它允许重复一个模式(pattern),无需再重写一遍。我们可以使用#(#是组号)来引用前面已定义的组。比如一个文本以abc开始,接着为xyz,紧跟着abc,对应的正则表达式可以为“abcxyzabc”,也可以使用反向引用重写正则表达式,““(abc)xyz\1””,\1表示第一组(abc)。\2表示第二组,\3表示第三组,以此类推。
使用反向引用匹配2-5个字母的回文(palindromes)。
例子
转义字符
\ 转义符号:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义字符,否则会报错。在java中,需要用\代表其他语言中的\。
在java中,需要用到转义字符的有如下:
. * + \ / ( ) $ ? [ ] ^ { }
常用的正则表达式案例
校验数字的表达式
数字:^[0-9]*$
n位的数字:^\d{n}$
至少n位的数字:^\d{n,}$
m-n位的数字:^\d{m,n}$
零和非零开头的数字:^(0|[1-9][0-9]*)$
非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
非负整数:^\d+$ 或 ^[1-9]\d*|0$
非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
校验字符的表达式
汉字:^[\u4e00-\u9fa5]{0,}$
英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
长度为3-20的所有字符:^.{3,20}$
由26个英文字母组成的字符串:^[A-Za-z]+$
由26个大写英文字母组成的字符串:^[A-Z]+$
由26个小写英文字母组成的字符串:^[a-z]+$
由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}
中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
禁止输入含有~的字符[^~\x22]+
================================================= 特殊使用========================================
.*匹配除 \n 以外的任何字符。
/[\u4E00-\u9FA5]/ 汉字
/[\uFF00-\uFFFF]/ 全角符号
/[\u0000-\u00FF]/ 半角符号
特殊需求表达式
Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
身份证号(15位、18位数字):^\d{15}|\d{18}$
短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
日期格式:^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
钱的输入格式
1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
9.xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
10.中文字符的正则表达式:[\u4e00-\u9fa5]
11.双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
12.空白行的正则表达式:\n\s*\r (可以用来删除空白行)
13.HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
14.首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
15.腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
16.中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
17.IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
18.IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
19.IP-v4地址:\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b (提取IP地址时有用)
20.校验IP-v6地址:(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))
21.子网掩码:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
22.校验日期:^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$(“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。)
23.抽取注释:<!--(.*?)-->
24.查找CSS属性:^\\s*[a-zA-Z\\-]+\\s*[:]{1}\\s[a-zA-Z0-9\\s.#]+[;]{1}
25.提取页面超链接:(<a\\s*(?!.*\\brel=)[^>]*)(href="https?:\\/\\/)((?!(?:(?:www\\.)?'.implode('|(?:www\\.)?', $follow_list).'))[^" rel="external nofollow" ]+)"((?!.*\\brel=)[^>]*)(?:[^>]*)>
26.提取网页图片:\\< *[img][^\\\\>]*[src] *= *[\\"\\']{0,1}([^\\"\\'\\ >]*)
27.提取网页颜色代码:^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$
28.文件扩展名效验:^([a-zA-Z]\\:|\\\\)\\\\([^\\\\]+\\\\)*[^\\/:*?"<>|]+\\.txt(l)?$
29.判断IE版本:^.*MSIE [5-8](?:\\.[0-9]+)?(?!.*Trident\\/[5-9]\\.0).*$