Java正则表达式
一 正则表达式的入门
/**
* 记住正则表达式的入门:
* 记住结论:
* 1,Java中的正则表达式都是通过模式和匹配结合的方式
* 模式:Pattern 主要用来封装正则表达式 通过Pattern中的静态方法compile方法来得到对象
* 匹配:Matcher 主要用来使用正则表示来进行字符产的匹配,封装匹配的结果
*
* 2,使用Pattern的matcher方法对字符串进行匹配,得到结果的包装。Matcher模式匹配对象。
* Matcher对象是对字符串进行匹配和结果的封装
*
* 3,正则表示底层中操作字符串的记录放在group数组中
* 每匹配到一个放入到group数组中的0号角标的位置
*
* 4,就是正则表达式的操作了。对正则表达式的理解
*
* 关于Matcher中group组进行匹配的大致方式为:
* 1,初始化一个group组,默认长度是20;初始化一个locals数组,默认长度为分组的个数
* 2,group中0角标和1角标用来记录的是匹配到的正则表达式的一个串的的开始角标和结束角标
* 3,locals是用来记录每一组匹配到的开始角标和结束几标的位置。最终将数据是放在group数组中
* 4,最终匹配到的结果都在group数组中,记录的是group数组中的角标位置
*
*
*
*/
public class RegexStart {
public static void main(String[] args) {
String content = "2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发布。\" +\n" +
" \"2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。在这些新特性当中,还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。\" +\n" +
" \"2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0),\" +\n" +
" \"代号为“Tiger”,Tiger包含了从1996年发布1.0版本以来的最重大的更新,其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、\" +\n" +
" \"格式化I/O及可变参数。";
// 第一步:得到Pattern模式对象
Pattern pattern = Pattern.compile("(\\d\\d)(\\d)(\\d)");
// 第二步:得到Matcher匹配对象
Matcher matcher = pattern.matcher(content);
// 第三步:进行模式匹配
while (matcher.find()) {
// 这个就是取的group数组中的0号角标的元素
System.out.println(matcher.group());
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
System.out.println("------");
}
}
}
1 正则表达式的尝试
public class Demo {
public static void main(String[] args) {
String content = "1995年,互联网的蓬勃发展给了Oak机会。业界为了使死板、单调的静态网页能够“灵活”起来,急需一种软件技术来开发一种程序," +
"这种程序可以通过网络传播并且能够跨平台运行。于是,世界各大IT企业为此纷纷投入了大量的人力、物力和财力。这个时候," +
"Sun公司想起了那个被搁置起来很久的Oak,并且重新审视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编写的,所以非常小," +
"特别适用于网络上的传输系统,而Oak也是一种精简的语言,程序非常小,适合在网络上传输。Sun公司首先推出了可以嵌入网页并且可以随同网页在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术),并将Oak更名为Java(在申请注册商标时,发现Oak已经被人使用了,再想了一系列名字之后,最终,使用了提议者在喝一杯Java咖啡时无意提到的Java词语)。5月23日,Sun公司在Sun world会议上正式发布Java和HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Java平台。";
// 匹配文中所有单词,输出:找到:Oak 找到:IT 找到:Sun
// Pattern compile = Pattern.compile("[a-zA-Z]+");
// 匹配文中所有数字,输出:找到:1995
// Pattern compile = Pattern.compile("[0-9]+");
// 匹配数字或者单词
Pattern compile = Pattern.compile("([0-9]+)|([a-zA-Z]+)");
// 2.创建一个匹配器对象
Matcher matcher = compile.matcher(content);
// 3. 可以循环匹配
while (matcher.find()) {
// 匹配内容,文本,放到 m.group(0)
System.out.println("找到:" + matcher.group(0));
}
}
}
2 原理剖析
/**
* 匹配4个数字,实现原理的理解
* @param args
*/
public static void main(String[] args) {
String content = "2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发布。" +
"2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。在这些新特性当中,还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。" +
"2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0)," +
"代号为“Tiger”,Tiger包含了从1996年发布1.0版本以来的最重大的更新,其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、" +
"格式化I/O及可变参数。";
// Pattern pattern = Pattern.compile("\\d{4}");
Pattern pattern = Pattern.compile("(\\d\\d)(\\d\\d)");
Matcher matcher = pattern.matcher(content); // 对字符串进行匹配
/**
* 对于未分组的匹配维护而言:
* 对find方法的维护:一次匹配一次取值的方式维护在groups数组中的
* groups[0]记录类匹配开始的字符角标
* groups[1]记录类匹配字符结束的角标 加1
*
* matcher.group(0)方法调用的是substring方法:getSubSequence(groups[group * 2], groups[group * 2 + 1])
* 传入的是0得到的值正好是 开始角标和结束角标记录的位置:group[0] - group[1] (substring解决字符串是前闭后开)
*
* 追踪原码可知:匹配的方式是一个字符一个字符的匹配,使用递归的方式,进行递增匹配,
* 每匹配到一个结果,就会维护到groups[]数组中,记录开始和结束+1的角标
*
*
* 对于分组的匹配模式:
* 维护在group中的 0角标为匹配到的开始位置,1角标维护的是结束字符串角标+1的位置
* 维护在group中的 2角标为第一组匹配开始的位置,3角标的值是第一组的匹配结束字符串的索引位置+1 (第一组的信息)
* ...
*
*/
while (matcher.find()) {
System.out.println("匹配到:"+matcher.group(0));
System.out.println("组一为:"+matcher.group(1));
System.out.println("组二为:"+matcher.group(2));
}
}
二 正则表达式的基本语法
一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列匹配某个句法规则的字符串。例如:Handel、Händel和Haendel这三个字符串,都可以由H(a|ä|ae)ndel
这个模式来描述。大部分正则表达式的形式都有如下的结构:
1 基础语法
选择[编辑]
数量限定[编辑]
某个字符后的数量限定符用来限定前面这个字符允许出现的个数。最常见的数量限定符包括+
、?
和*
(不加数量限定则代表出现一次且仅出现一次):
- 加号
+
代表前面的字符必须至少出现一次。(1次或多次)。例如,goo+gle
可以匹配google、gooogle、goooogle等; - 问号
?
代表前面的字符最多只可以出现一次。(0次或1次)。例如,colou?r
可以匹配color或者colour; - 星号
*
代表前面的字符可以不出现,也可以出现一次或者多次。(0次、1次或多次)。例如,0*42
可以匹配42、042、0042、00042等。
匹配[编辑]
- 圆括号
()
可以用来定义操作符的范围和优先度。例如,gr(a|e)y
等价于gray|grey
,(grand)?father
匹配father和grandfather。
上述这些构造子都可以自由组合,因此H(ae?|ä)ndel
和H(a|ae|ä)ndel
是相同的,表示{“Handel”, “Haendel”, “Händel”}。
精确的语法可能因不同的工具或程序而异。
2 PCRE表达式全集
字符 | 描述 |
---|---|
\ | 将下一个字符标记为一个特殊字符(File Format Escape,清单见本表)、或一个原义字符(Identity Escape,有^$()*+?.[{|共计12个)、或一个向后引用(backreferences)、或一个八进制转义符。例如,“n ”匹配字符“n ”。“\n ”匹配一个换行符。序列“\\ ”匹配“\ ”而“\( ”则匹配“( ”。 |
^ | 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n ”或“\r ”之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n ”或“\r ”之前的位置。 |
* | 匹配前面的子表达式零次或多次。例如,zo*能匹配“z ”、“zo ”以及“zoo ”。*等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,“zo+ ”能匹配“zo ”以及“zoo ”,但不能匹配“z ”。+等价于{1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)? ”可以匹配“does ”中的“do ”和“does ”。?等价于{0,1}。 |
{n} | n是一个非负整数。匹配确定的n次。例如,“o{2} ”不能匹配“Bob ”中的“o ”,但是能匹配“food ”中的两个o。 |
{n,} | n是一个非负整数。至少匹配n次。例如,“o{2,} ”不能匹配“Bob ”中的“o ”,但能匹配“foooood ”中的所有o。“o{1,} ”等价于“o+ ”。“o{0,} ”则等价于“o* ”。 |
{n,m} | m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3} ”将匹配“fooooood ”中的前三个o。“o{0,1} ”等价于“o? ”。请注意在逗号和两个数之间不能有空格。 |
? | 非贪心量化(Non-greedy quantifiers):当该字符紧跟在任何一个其他重复修饰符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo ”,“o+? ”将匹配单个“o ”,而“o+ ”将匹配所有“o ”。 |
. | 匹配除“\r ”“\n ”之外的任何单个字符。要匹配包括“\r ”“\n ”在内的任何字符,请使用像“(.|\r|\n) ”的模式。 |
(pattern) | 匹配pattern并获取这一匹配的子字符串。该子字符串用于向后引用。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\( ”或“\) ”。可带数量后缀。 |
(?:pattern) | 匹配pattern但不获取匹配的子字符串(shy groups),也就是说这是一个非获取匹配,不存储匹配的子字符串用于向后引用。这在使用或字符“(|) ”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies) ”就是一个比“industry|industries ”更简略的表达式。 |
(?=pattern) | 正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000) ”能匹配“Windows2000 ”中的“Windows ”,但不能匹配“Windows3.1 ”中的“Windows ”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000) ”能匹配“Windows3.1 ”中的“Windows ”,但不能匹配“Windows2000 ”中的“Windows ”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 |
(?<=pattern) | 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows ”能匹配“2000Windows ”中的“Windows ”,但不能匹配“3.1Windows ”中的“Windows ”。 |
(?<!pattern) | 反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows ”能匹配“3.1Windows ”中的“Windows ”,但不能匹配“2000Windows ”中的“Windows ”。 |
x|y | 没有包围在()里,其范围是整个正则表达式。例如,“z|food ”能匹配“z ”或“food ”。“(?:z|f)ood ”则匹配“zood ”或“food ”。 |
[xyz] | 字符集合(character class)。匹配所包含的任意一个字符。例如,“[abc] ”可以匹配“plain ”中的“a ”。特殊字符仅有反斜线\保持特殊含义,用于转义字符。其它特殊字符如星号、加号、各种括号等均作为普通字符。脱字符^如果出现在首位则表示负值字符集合;如果出现在字符串中间就仅作为普通字符。连字符 - 如果出现在字符串中间表示字符范围描述;如果如果出现在首位(或末尾)则仅作为普通字符。右方括号应转义出现,也可以作为首位字符出现。 |
[^xyz] | 排除型字符集合(negated character classes)。匹配未列出的任意字符。例如,“[^abc] ”可以匹配“plain ”中的“plin ”。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,“[a-z] ”可以匹配“a ”到“z ”范围内的任意小写字母字符。 |
[^a-z] | 排除型的字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z] ”可以匹配任何不在“a ”到“z ”范围内的任意字符。 |
[:name:] | 增加命名字符类(named character class)[注 1]中的字符到表达式。只能用于方括号表达式。 |
[=elt=] | 增加当前locale下排序(collate)等价于字符“elt”的元素。例如,[=a=]可能会增加ä、á、à、ă、ắ、ằ、ẵ、ẳ、â、ấ、ầ、ẫ、ẩ、ǎ、å、ǻ、ä、ǟ、ã、ȧ、ǡ、ą、ā、ả、ȁ、ȃ、ạ、ặ、ậ、ḁ、ⱥ、ᶏ、ɐ、ɑ 。只能用于方括号表达式。 |
[.elt.] | 增加排序元素(collation element)elt到表达式中。这是因为某些排序元素由多个字符组成。例如,29个字母表的西班牙语, "CH"作为单个字母排在字母C之后,因此会产生如此排序“cinco, credo, chispa”。只能用于方括号表达式。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b ”可以匹配“never ”中的“er ”,但不能匹配“verb ”中的“er ”。 |
\B | 匹配非单词边界。“er\B ”能匹配“verb ”中的“er ”,但不能匹配“never ”中的“er ”。 |
\cx | 匹配由x指明的控制字符。x的值必须为A-Z 或a-z 之一。否则,将c视为一个原义的“c ”字符。控制字符的值等于x的值最低5比特(即对3210进制的余数)。例如,\cM匹配一个Control-M或回车符。\ca等效于\u0001, \cb等效于\u0002, 等等… |
\d | 匹配一个数字字符。等价于[0-9]。注意Unicode正则表达式会匹配全角数字字符。 |
\D | 匹配一个非数字字符。等价于[^0-9]。 |
\f | 匹配一个换页符。等价于\x0c和\cL。 |
\n | 匹配一个换行符。等价于\x0a和\cJ。 |
\r | 匹配一个回车符。等价于\x0d和\cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。注意Unicode正则表达式会匹配全角空格符。 |
\S | 匹配任何非空白字符。等价于[^ \f\n\r\t\v]。 |
\t | 匹配一个制表符。等价于\x09和\cI。 |
\v | 匹配一个垂直制表符。等价于\x0b和\cK。 |
\w | 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_] ”。注意Unicode正则表达式会匹配中文字符。 |
\W | 匹配任何非单词字符。等价于“[^A-Za-z0-9_] ”。 |
\xnn | 十六进制转义字符序列。匹配两个十六进制数字nn表示的字符。例如,“\x41 ”匹配“A ”。“\x041 ”则等价于“\x04&1 ”。正则表达式中可以使用ASCII编码。. |
\num | 向后引用(back-reference)一个子字符串(substring),该子字符串与正则表达式的第num个用括号围起来的捕捉群(capture group)子表达式(subexpression)匹配。其中num是从1开始的十进制正整数,其上限可能是9[注 2]、31[注 3]、99甚至无限[注 4]。例如:“(.)\1 ”匹配两个连续的相同字符。 |
\n | 标识一个八进制转义值或一个向后引用。如果*n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n*为一个八进制转义值。 |
\nm | 3位八进制数字,标识一个八进制转义值或一个向后引用。如果*nm之前至少有nm个获得子表达式,则nm为向后引用。如果*nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则*nm将匹配八进制转义值nm*。 |
\nml | 如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。 |
\un | Unicode转义字符序列。其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配著作权符号(©)。 |
三 正则表达式的使用
1 普通字符
分类
[ABC] | 匹配 […] 中的所有字符,例如 [aeiou] 匹配字符串 “google runoob taobao” 中所有的 e o u a 字母。 |
---|---|
[^ABC] | 匹配除了 […] 中字符的所有字符,例如 [^aeiou] 匹配字符串 “google runoob taobao” 中除了 e o u a 字母的所有字母。 |
[A-Z] | [A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母。 |
\S \s | 匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行。 |
. | 匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\r\n] 。 |
\w | 匹配字母、数字、下划线。等价于 [A-Za-z0-9_] |
\W | 匹配非字母、非数字、非下划线。等价于 [^ A-Za-z0-9_] |
public static void main(String[] args) {
String content = "abcAcaac BC_192346%%###@@@$$$))((";
/**
* 第一部分:普通字符
*/
// 1,[ABC] 匹配ABC中任一字符,只能匹配一个。默认是区分大小写的。底层使用ASCII码匹配
// Pattern pattern = Pattern.compile("[ABC]");
// 2,[^ABC] 匹配非A B C的字符
//Pattern pattern = Pattern.compile("[^ABC]");
// 3,[A-Z]表示 表示A到Z之间的一个字符
// Pattern pattern = Pattern.compile("[A-Z]");
// 4,[^A-Z]表示 表示非A到Z直接的一个字符
// Pattern pattern = Pattern.compile("[^A-Z]");
// 5,\\s 匹配所有空白符,包括换行符
// Pattern pattern = Pattern.compile("\\s");
// 6,\\S 匹配所有非空白字符,不包括换行符
// Pattern pattern = Pattern.compile("\\S");
// 7,\\w 匹配 字母,数字和下划线_
// Pattern pattern = Pattern.compile("\\w");
// 8,\\W 匹配非字母,非数字,非下划线_
Pattern pattern = Pattern.compile("\\W");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("匹配到:"+matcher.group(0));
}
}
总结
基本字符是正则表达式的关键,基本字符的含义,已经^在中括号中的含义。
2 非打印字符
分类
非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:
字符 | 描述 |
---|---|
\cx | 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。 |
\f | 匹配一个换页符。等价于 \x0c 和 \cL。 |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等价于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v] 。 |
\t | 匹配一个制表符。等价于 \x09 和 \cI。 |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
public static void main(String[] args) {
int i = 1;
// \cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
//\f 匹配一个换页符。等价于 \x0c 和 \cL。
//\n 匹配一个换行符。等价于 \x0a 和 \cJ。
//\r 匹配一个回车符。等价于 \x0d 和 \cM。
//\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。
//\S 匹配任何非空白字符。等价于 [^\f\n\r\t\v]。
//\t 匹配一个制表符。等价于 \x09 和 \cI。
//\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
// 主要看\n \r \s \S \t
String content = "abc 你好 ";
//
// \\s 匹配任何空白字符:包括空格 \n,制表符 \t,换页符 \f
// Pattern pattern = Pattern.compile("\\s");
// \\S 匹配任何非空白字符, 包括非空格,非制表符,非换页符
Pattern pattern = Pattern.compile("\\S");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("匹配到第"+(i++) + "个为:" + matcher.group(0));
}
总结
3 特殊字符
分类
所谓特殊字符,就是一些有特殊含义的字符,如上面说的 runoo*b 中的 *****,简单的说就是表示任何字符串的意思。如果要查找字符串中的 ***** 符号,则需要对 ***** 进行转义,即在其前加一个 ****,runo*ob 匹配字符串 runo*ob。
许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符**** 放在它们前面。下表列出了正则表达式中的特殊字符:
特别字符 | 描述 |
---|---|
$ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。要匹配 $ 字符本身,请使用 $。 |
( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。 |
* | 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 |
+ | 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 |
. | 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用\ . 。 |
[ | 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。 |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。’\n’ 匹配换行符。序列 ‘\’ 匹配 “”,而 ‘(’ 则匹配 “(”。 |
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 ^。 |
{ | 标记限定符表达式的开始。要匹配 {,请使用 \{。 |
| | 指明两项之间的一个选择。要匹配 |,请使用 \ |
代码总结
- 特殊字符要想实际被匹配,需要使用转义。
- 特殊字符在[]表达式中不具有特殊字符的含义,也就是实际的字符匹配。例如:[. ] 表 示 匹 配 . 或 者 ] 表示 匹配.或者 ]表示匹配.或者
- []用来表示一个表达式,() 用来表示分组
public static void main(String[] args) {
/*
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 \[。
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 \^。
{ 标记限定符表达式的开始。要匹配 {,请使用 \{。
| 指明两项之间的一个选择。要匹配 |,请使用 \|。
*/
String content = "absdfsadf21\f34312#\t%#@$%asd\nfda\rs@#b$#UK UIku1";
// 1,$ 匹配输入字符串的结尾位置。从结尾位置向前匹配
// 那么结尾:包含字符串本身的结尾,也包括\n \r
//Pattern pattern = Pattern.compile("[a-zA-Z]$");
// 2, ( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。用于比爱是分组
// Pattern pattern = Pattern.compile("([a-d])([c-e])");
// 3 * 表示匹配0次或者多次 相当于{0,}
// Pattern pattern = Pattern.compile("[a-z]*"); // 匹配0个或多个字符.为什么会匹配到空呢?为匹配上的,相当于0次,因此也是会被匹配到的
// 4,+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
// Pattern pattern = Pattern.compile("[a-z]+"); // 表示a-z出现一次或者多次的组合匹配
// 5,. 匹配除换行符 \n,\r回车符 之外的任何单字符。要匹配 . ,请使用 \.
// Pattern pattern = Pattern.compile(".+"); // 匹配换行符之外的任意字符(贪婪匹配,因此匹配的结果可以这样理解:只有到换行符\n才会出现得到一个结果,否则一直匹配完字符串)
// 6 [ 标记一个中括号表达式的开始。要匹配 [,请使用 \[。
// [ ] 和 ( )一起理解,[ ]表示一个个表示。( ) 表示表达式的组合
// Pattern pattern = Pattern.compile("[a-z](\\d\\d)"); // 这里就有一个分组\\d ,也就是两个表达式的组合,表示一个消协字母加上一个数字
// 7 ? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
// Pattern pattern = Pattern.compile("^\\d?"); // 表示从输入字符串的后面匹配出现两个数字一次或者没有出现。肯定会被匹配上的
// 8 ^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 \^
// 总结:^ 在方括号中表示否定的意思,取反的意思
// 否则就是表达匹配字符串开始的位置
// Pattern pattern = Pattern.compile("^[a-zA-Z]+"); // 从字符串的开始位置开始匹配,最少出现一个字母的贪婪匹配
// 如果放在[]内,表示取反的意思
// Pattern pattern = Pattern.compile("[^a-zA-Z]+"); // 匹配整个字符串中,最少出现一次非字母的贪婪贪婪匹配。很多个结果
// Pattern pattern = Pattern.compile("^[^a-zA-Z]+"); // 从字符串开始位置匹配,因此只能得到一个结果或者没有结果
// Pattern pattern = Pattern.compile("^[^a-zA-Z]?"); // 这里肯定是一次结果,要么是匹配到,要么是每匹配到
// 9 { 标记限定符表达式的开始。要匹配 {,请使用 \{。
// 什么叫限定符:表示范围的
// 例如:
// {1,3} 表示最少一次,最多三次
// {3} 表示必须出现3次
// {3,} 表示最少出现3次
// * 相当于 {0,}
// + 相当于 {1,}
// ? 相当于 {0,1}
// 限定符作用在表达式上
// Pattern pattern = Pattern.compile("[a-zA-Z]{3}"); // 匹配出现三次以上的字母组合。这里是贪婪匹配,因此匹配按照最多个数匹配
// 限定符作用在单个字符上
// Pattern pattern = Pattern.compile("a{1,3}b"); // 表示ab aab aaab
// Pattern pattern = Pattern.compile("a?b"); // 表示ab或者b
// Pattern pattern = Pattern.compile("a+b"); // 表示ab aab aaab ...
Pattern pattern = Pattern.compile("a*b"); // 表示b ab aab ...
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(2)); // 第二组匹配到的元素
}
}
4 限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 ***** 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。
正则表达式的限定符有:
分类
字符 | 描述 |
---|---|
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
总结
限定符使用的注意:
- java默认使用的是贪婪匹配,在限定符后加上?表示非贪婪匹配。
- 主要是正对 * 和 + 的贪婪匹配到非贪婪匹配的方式
- 可以在后面加上 ?
- 可以在构造Patter对象的时候设定Mulity的属性
- 限定符的都可以使用{} 来指定
- 在使用的过程中,需要知道限定符生效的字符
- 例如:
[1-9][0-9]*
此限定符*只能在后面的[0-9]生效
- 例如:
- 如果我们想匹配到标签中的内容,需要通过分组的方式获取组中的内容。否则会容易得到全局匹配的结果。如下代码中的
标签匹配
public static void main(String[] args) {
String content = "<h1>RUNOOB-菜鸟教程</h1>";
// 限定符作用在单个字符上
// Pattern pattern = Pattern.compile("a{1,3}b"); // 表示ab aab aaab
// Pattern pattern = Pattern.compile("a?b"); // 表示ab或者b
// Pattern pattern = Pattern.compile("a+b"); // 表示ab aab aaab ...
// Pattern pattern = Pattern.compile("a*b"); // 表示b ab aab ...
// Pattern pattern = Pattern.compile("[1-9][0-9]*"); // 表示一个正整数
// Pattern pattern = Pattern.compile("[0-9]{1,2}"); // 表示匹配一位数或者两位数
// 关于贪婪限定符 + * 的使用
// Pattern pattern = Pattern.compile("<h1>.*</h1>"); // 会匹配到所有的内容
// 匹配整个标签的内容。可以使用如下的方式
// Pattern pattern = Pattern.compile("<.*>"); // 将会匹配到整个标签内容,因为它是贪婪匹配。如果想限定为非贪婪匹配的,需要在限定符后加上?
// Pattern pattern = Pattern.compile("<.*?>"); // 非贪婪的匹配方式。匹配两个标签 <h1> </h1>
// 匹配整个h1标签
// Pattern pattern = Pattern.compile("<\\w*>"); // \\w只能匹配数字,字符,和下划线。 因此只能匹配到一个结果 <h1> 不能匹配</h1>
// 如果想取出标签中的内容。需要通过分组的方式
Pattern pattern = Pattern.compile("<h1>(\\S+)</h1>");
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(2)); // 第二组匹配到的元素
}
}
5 定位符
定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。
分类
定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
正则表达式的定位符有:
字符 | 描述 |
---|---|
^ | 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。 |
$ | 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。 |
\b | 匹配一个单词边界,即字与空格间的位置。 |
\B | 非单词边界匹配。 |
注意:不能将限定符与定位符一起使用。由于在紧靠换行或者单词边界的前面或后面不能有一个以上位置,因此不允许诸如 ^* 之类的表达式。
若要匹配一行文本开始处的文本,请在正则表达式的开始使用 ^ 字符。不要将 ^ 的这种用法与中括号表达式内的用法混淆。
若要匹配一行文本的结束处的文本,请在正则表达式的结束处使用 $ 字符。
若要在搜索章节标题时使用定位点,下面的正则表达式匹配一个章节标题,该标题只包含两个尾随数字,并且出现在行首:
public static void main(String[] args) {
// 1. ^ 字符串开始位置开始
// String content = "nihao a A111\n112";
String content = "Chapter Chap Chaoor Chte";
// 默认从输入字符串的开始位置匹配
// Pattern pattern = Pattern.compile("^\\w");
// 设定类Multiline 属性,可以从\n \r开始匹配
// Pattern pattern = Pattern.compile("^\\w", Pattern.MULTILINE); // 匹配的结果为 n 1
// 2,$ 从输入字符串末尾开始匹配
// 从输入字符串末尾匹配,得到12
// Pattern pattern = Pattern.compile("\\w{2}$");
// Pattern pattern = Pattern.compile("\\w{2}$", Pattern.MULTILINE);
// 3, \\b 匹配单词边界,即,空格和单词之间
// \\b的位置非常重要,出现在前面,就是向前匹配,出现在后面,就是向后匹配
// Pattern pattern = Pattern.compile("\\bCha");
// 可以简单的这样理解 \\b就是字符断档的位置。因为单词中不可能存在不连续的字符
// Pattern pattern = Pattern.compile("[e|r|er]\\b");
// 4, \\B 就表示匹配在单词中的。也就是表示不能是一个不连续的位置。\\B就相当于必须是一个字母
// 例如 \\Bllo 可以匹配 Hello 但是不能匹配 llower
Pattern pattern = Pattern.compile("te\\B"); // 只能匹配 Chapter 不能匹配 Chte
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("匹配到:"+matcher.group());
}
}
6 选择
用圆括号 () 将所有选择项括起来,相邻的选择项之间用 | 分隔。
() 表示捕获分组,() 会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(n 是一个数字,表示第 n 个捕获组的内容)。
总结
-
使用( )表示了新建一个分组
-
如果使用了( )建了分组,改组就可以被捕获,也就是改组匹配到的内容会被保存到内存中
-
针对分组捕获的概念,又出现了非捕获的概念。三个非捕获非组
?: ?= ?!
//1 ?: 表示当前括号是一个非捕获分组,也就是不会记录在分组中,通能通过group(1)的方法取出分组中的信息 //2 ?= 为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串, //3 ?! 为负向预查,在任何开始不匹配,该正则表达式模式的位置来匹配搜索字符串。 // 总结: // 后面是某个条件的匹配 exp1(?=exp2) 匹配后面是exp2的exp1 // 前面是某个条件的匹配 (?<=exp2)exp1 匹配前面是exp2的exp1,箭头指向条件在待匹配的方向 // 后面不是某个条件的匹配 exp1(?!exp2) 匹配后面不是exp2的exp1 // 前面不是某个条件的匹配 (?<!exp2)exp1 匹配前面不是exp2的exp1
public static void main(String[] args) {
String content = "123456java122javascript1341springjavaboot23987java123";
// 1 用圆括号 () 将所有选择项括起来,相邻的选择项之间用 | 分隔。
// 说明:+ 只作用在a-z上
// Pattern pattern = Pattern.compile("[0-9|a][a-z]+");
// 加上分组的概念
// 这里就分了两组
// Pattern pattern = Pattern.compile("([0-9|a])([a-z]+)");
// 但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在第一个选项前来消除这种副作用。
// Pattern pattern = Pattern.compile("(?:[0-9|a])([a-z]+)");
// 但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在第一个选项前来消除这种副作用。
// 其中 ?: 是非捕获元之一,还有两个非捕获元是 ?= 和 ?!,这两个还有更多的含义,前者为正向预查,
// 在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配
// 该正则表达式模式的位置来匹配搜索字符串。
//2, 下面列举 ?=、?<=、?!、?<! 的使用区别
// 2.1 ?= 在任何开始匹配 圆括号内 的正则表达式的位置来匹配搜索字符串
// 使用:?= 后面跟上一个表达式或者一个组的表达式
//Pattern pattern = Pattern.compile("java(?=[\\d]+3)");
// 2.2 (?<=exp2)exp1:查找 exp2 后面的 exp1。
// (?<=exp2)exp1 包含?<=的表达式必须在一个小括号内
// Pattern pattern = Pattern.compile("(?<=[0-9][0-9][0-9][0-9])java");
// 2.3 exp1(?!exp2):查找后面不是 exp2 的 exp1
// 查找的是java,在写下小括号内的捕获元素
// Pattern pattern = Pattern.compile("java(?![\\d]+)"); // 捕获后面不是数字的java
// 2.4 (?<!exp2)exp1:查找前面不是 exp2 的 exp1。
// Pattern pattern = Pattern.compile("(?<![[\\d]+])java"); // 捕获前面不是数字的java
// 总结:
// 后面是某个条件的匹配 exp1(?=exp2) 匹配后面是exp2的exp1
// 前面是某个条件的匹配 (?<=exp2)exp1 匹配前面是exp2的exp1,箭头指向条件在待匹配的方向
// 后面不是某个条件的匹配 exp1(?!exp2) 匹配后面不是exp2的exp1
// 前面不是某个条件的匹配 (?<!exp2)exp1 匹配前面不是exp2的exp1
// Pattern pattern = Pattern.compile("(?<=[a-zA-Z])java");
// Pattern pattern = Pattern.compile("java(?!123)"); // 后面不是123的java
Pattern pattern = Pattern.compile("(?<![\\d])java");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group());
// System.out.println(matcher.group(1));
}
}
7 反向引用
概念
正则表达式中分组信息会被记录在名为group的数组中,那么我们在编写正表达式时,可以通过\\n 的方法拿到组中匹配到的内容。(注意:n表示组号,组是从左向右,从1开始递增的)。
反向引用的两种方式:
- 在正则表达式中,可以通过\\n的方法引用
- 在正则表达式在外(需在同一行代码中),可以通过$n,那么组中数据
反向引用的案例
/**
* 反向引用的练习
* 分组:使用()进行分组
* 捕获:将正则表达式中子表达式/分组匹配到的内容,保存到内存中以数字编码或者显示命令的阻力,方便后面引用。
* 从左向右,以分组的左括号为标志,第一个出现的组号为1,第二个为2,一次类推。组0代表整个正则表达式
* 反向引用:可以通过捕获拿到扩后中的内容,从而得到一个比较新的匹配模式。
* 这种尽可以在正则表达式内部使用,也可以在外部使用。当然需要针对的是同一个mather对象。
* 内部使用\\组号,外部使用$组号
*/
public static void main(String[] args) {
// 1,匹配五个连续相同的数字
// String content = "111111113335666";
// 第一步就要分组,然后在通过反向引用
// \\d:代表数字 \
// \1:代表反向引用到第一组中,也就是\\d匹配到的值
// {4}表示 \\1需要出现4次
// String regex = "(\\d)\\1{4}";
// Matcher matcher = Pattern.compile(regex).matcher(content);
// while (matcher.find()) {
// System.out.println(matcher.group());
// }
// 2 匹配个位数和千位相同,十位和百位相同的千位数
// String content = "5245";
// String regex = "(\\d)(\\d)\\2\\1"; //表示四位数
// Matcher matcher = Pattern.compile(regex).matcher(content);
// System.out.println(matcher.matches());
// // 这里如果想取出反向引用的值,必须通过相同的matcher对象取出
// System.out.println("$1");
// 3 重复汉字的去重
// 说明:使用String中的replace方法传入正则的方式
String content = "我...我要...学学学学....编程java";
String regex1 = "\\.+";
String content1 = content.replaceAll(regex1, ""); // 去掉省略号
// [\\u4e00-\\u9fa5]表示汉字,只针对汉字去重
// \\1 反向引用到组1中,也只有一个组
// + 表示最少一个,因此针对的是重复汉字
String regex2 = "([\\u4e00-\\u9fa5])\\1+";
// 这里就使用到了非常重要的一个东西,在他同一个对象中,在regex外部,可以使用$组名 的方式拿到分组的内容
System.out.println(content1.replaceAll(regex2,"$1"));
}
反向引用总结
- 反向引用最经典的就是重复汉字的去重。这里需要使用外部引用的方法拿到我们匹配重复的汉字。需要使用$n方式
- 反向引用需要在组后使用\\n的方法得到确定组,在\\n{m}可以指定出现匹配的次数,{m}限定符
四 正则表达式中三个常用类
主要是Matcher类中的方法说明:
- start() 返回以前匹配的初始索引。其实就是维护在内部group数组中的其实元素值 group[0] 的值
- Start(int group) 返回对应组中匹配到的开始索引
- lookingAt()尝试从区域开始的输入序列和该模式匹配
- find()
- find(int start) 重制次匹配器,然后尝试查找匹配模式,从指定索引的位置开始。所以如果能找到,会一直出现找到的情况,会出现死循环
- matches() 尝试将整个区域与模式匹配,判断是否匹配成功。这个用的是最多的。相当于带着开头和结果的
/**
* 正则表达式的三个常用类
* Pattern 正则表达式对象,只能通过静态方法构造实例
* Matcher 是对输入的字符串和正则表达式进行解释和匹配的引擎。与Pattern类一样,没有公共的构造方法,需要通过mather方法来构造对象实 * 例
* PatternSyntaxException 非强制异常,可以表示一个正则表达式模式中的语法错误
*/
public class Regex09 {
// 主要是了解和使用一下Mather类中的不同方法
public static void main(String[] args) {
// 1,start() 返回以前匹配的初始索引。其实就是维护在内部group数组中的其实元素值 group[0] 的值
String content = "ABC123AbcABC";
// String regex = "ABC";
// 2,start(int group) 返回对应组中匹配到的开始索引
String regex = "([A-C]{3})(\\w*)?"; // 两个匹配结果
// 3,lookingAt()尝试从区域开始的输入序列和该模式匹配
// 4,find()
// 5,find(int start) 重制次匹配器,然后尝试查找匹配模式,从指定索引的位置开始。所以如果能找到,会一直出现找到的情况,会出现死循环
// 6 matches() 尝试将整个区域与模式匹配,判断是否匹配成功。这个用的是最多的。相当于带着开头和结果的
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
System.out.println(matcher.lookingAt()); // 判断开头是否满足
System.out.println(matcher.matches()); // 判断整个字符串是否满足
// while (matcher.find()) { // start(0)就是start整个匹配到的开始索引
// System.out.println("匹配到一条数据:" + matcher.group(0));
// System.out.println("初始索引:"+matcher.start());
// System.out.println("组一中开始索引:"+matcher.start(1));
// System.out.println("组二中开始索引:"+matcher.start(2)); // 没有的是-1
//
//
// System.out.println("最后匹配字符的偏移量:"+matcher.end());
// }
}
}
五 String类中的正则使用
String类中的使用正则的方法:replaceAll(String regex, String replacement)
matches(String regex)
// 匹配正则后替换
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
// 判断一个字符串是否满足正则。这个相当于限定头和尾的正则校验
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
案例:字符串的替换
public static void main(String[] args) {
// String 中的很多方法是可以传入正则表达式的
// 案例一:替换jdk1.3 jdk1.4为jdk
String content = "JDK1.3,JDK1.4都说是JDK的初始时代";
String res = content.replaceAll("JDK1\\.[3|4]", "JDK");
System.out.println(res);
// String 中的matches方法。判断字符串是否满足正则
// \\s 匹配空白
// \\S匹配非空白
// \\w 匹配字母,数字和下划线
// 此方法本质还是使用Pattern.matchers()匹配方法
boolean matches = content.matches("^[a-zA-z][\\S]*$");
System.out.println(matches);
}
六 案例练习
案例一:判断是否全是汉字
String regex = "^[\\u4e00-\\u9fa5]+$";
案例二:邮政编码
// 要求:1-9开头的是6位数
// String content = "0124121";
// String regex = "^[1-9]\\d{5}$"; // 表示1-9开头的六位数
案例三:QQ号码
// 要求:1-9开头的一个(5位数-10位数)
// String content = "12342";
// String regex = "^[1-9]\\d{4,9}";
案例四:手机号码
// 要求:以13 14 15 18 开头的11位数
// String content = "13865782178";
// String regex = "^[13|14|15|18]\\d{9}$"; // 这样写是错的,记住操作的只能是单个字符
// String regex = "^1[3|4|5|8]\\d{9}$";
案例五:匹配URL
String content = "https://www.baidu.com/s?wd=袁海洋" +
"%B8%AD%E6%96%87&rsv_spt=1&rsv_iqid=0xf1d50925000040d3&issp=1&f=8&rsv_bp=1&rsv_idx=2&" +
"ie=utf-8&tn=baiduhome_pg&rsv_dl=ib&rsv_enter=1&rsv_sug3=39&rsv_sug1=33&rsv_sug7=101";
// \\w 表示字符 数字 和 下划线
// + 表示最少一个
// 不能在表达式[]中在使用表达式,否则会不生效
// 在表达式[]括号中,所有的字符都是其本意,不是正则中符号的含义
String regex = "^(https?://)([\\w-]+\\.?)+(\\/*[\\w-?=&/%.#\\u4e00-\\u9fa5]*)?$";
案例六:验证电子邮件格式是否合法
/**
* 综合练习一:
* 验证电子邮件格式是否合法
* 规定:
* 1,只有一个@
* 2,@前面是用户名,可以是a-z A-Z _- 字符
* 3,@后面是域名,并且域名只能是英文字母,比如soho.com
* 4,写出对应的正则表达式,验证输入的字符串是否满足规则
*/
public class RegexExercise03 {
public static void main(String[] args) {
// ([a-zA-Z]+\.)+与[a-zA-Z.]+的区别。 前面是多个字符结合一个.出现,后面是可以出现多个...的组合
String regex = "^[a-zA-Z0-9-_]+@[a-zA-Z.]+$";
//String regex = "^[\\w-]+@([a-zA-Z]+\\.)+";
// String email = "haiyang@163.com";
String email = "haiyang_163@sohu....com";
System.out.println(email.matches(regex));
}
}
案例七:验证是不是整数或者为小数(需要考虑负数和正数)
public class RegexExercise04 {
public static void main(String[] args) {
// String digit = "-10.11";
// String digit = "0.001";
// String digit = "0002.00001";
String digit = "1.001";
// 如果有多个表达式进行组合,可以使用 在 () 中使用或者的方式
// String regex = "^-?(0.|[1-9]\\d*\\.?)\\d+$"; // 我这里有个问题,就是\\d必须出现,所以不能匹配到单个数字
// String regex = "^-?(0.|[1-9]\\d*\\.?)\\d*$"; // 也有问题,会出现.后面没有数字,因此需要将.和\\d一样
// String regex = "^-?(0.|[1-9]\\d*\\.?)\\d*$"; // 还有个问题,不能匹配0
// 改造
String regex = "^-?(0|0.\\d+|[1-9]\\d*\\.?\\d*)$"; // 这样就可以
// 如果使用[]就不能使用0. 的方式,因为这里表示的是0 或者 .
// String regex = "^-?[0|[1-9]\\d*](\\.?\\d+)?$";
// 这里是不能排除掉前面有很多000的字符串
// 这里的问题在与 [0|[1-9]\\d] \\d只会作用在[1-9]后面,因此是完整的匹配方式
//String regex = "^[+-]?[0|[1-9]\\d*](\\.\\d+)?$"; // 这个匹配方式是补完整的
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(digit);
while (matcher.find()) {
System.out.println("匹配到:"+matcher.group());
}
}
}
案例八:对url进行解析
/**
* 对url进行解析
* https://www.sohu.com:8080/abc/index.html
* 要求得到协议是什么? http
* 域名是什么 www.sohu.com
* 端口是什么 8080
* 文件名是什么 index.html
*/
public class RegexExercise05 {
public static void main(String[] args) {
String content = "https://www.sohu.com:8080/abc/index.html";
// 分四组的方式,每组按照指定的规则进行正则校验
String regex = "^(https|http)://([a-zA-Z.]+):(\\d+)([\\w.@#$%/]+)$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("匹配到整个字符串为URL:"+matcher.group(0));
System.out.println("第一组数据:"+matcher.group(1));
System.out.println("第二组数据:"+matcher.group(2));
System.out.println("第三组数据:"+matcher.group(3));
System.out.println("第四组数据:"+matcher.group(4));
}
}
}
七 常用正则表达式
1 校验数字的表达式
数字:^[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)$
2 校验字符的表达式
汉字:^[\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]+
3 特殊需求表达式
Email地址:^\w+@[a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$
域名:[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-./?%&=]*)?$
手机号码1:^(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])\d{8}$ (由于工信部放号段不定时,所以建议使用泛解析 ^([1][3,4,5,6,7,8,9])\d{9}$)
手机号码2:^[1][3,4,5,6,7,8,9][0-9]{9}$
电话号码("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}
18或15位身份证号码(数字、字母x结尾):^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$
帐号是否合法(字母开头,允许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])[\da-zA-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})?$
备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式:[\u4e00-\u9fa5]
双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:\n\s*\r (可以用来删除空白行)
HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)) (由@飞龙三少 提供,感谢共享)