1、正则表达式
在开发中,通常很多数据都会使用String类存储。原因:操作字符串的功能比较多,比较方便。
在操作String类对象时,会经常遇到对字符串进行验证的功能,而按照我们之前学习的String类,我们使用String类中的诸多函数是可以完成对字符串校验功能的,但是代码相对来说比较麻烦,所以在Java中引入正则表达式的概念来解决上述问题,即简化代码。
正则表达式:专门用于操作字符串的技术,并且可以简化代码,用于对字符串的复杂操作。
正则表达式弊端:代码可读性比较差。
1.1、案例引入
需求:验证QQ号码是否合法。
分析:
1、第一位不能是零;
2、QQ号码在5到12之间(包含);
3、QQ号码都是由数字组成;
步骤:
1)定义一个RegexDemo类,在这个类中定义一个主函数main和一个自定义函数method_1;
2)在main函数中调用自定义函数method_1;
3)在自定义函数method_1中定义一个字符串变量QQ,并赋值为12345;
4)使用if-elseIf-else结构对字符串QQ分别按照上述给定的三个条件进行判断;
5)使用String类中的charAt()函数获取字符串QQ中的第一个字符和字符0进行比较,相等则告诉用户不能以0开始;
6)使用String类中的length()函数获得字符串QQ的长度,判断是否在5~12之间;
7)验证字符串QQ中是否都是数字,使用Long.parseLong(QQ)把一个字符串转成 long 类型的数据,此函数容易发生异常,所以使用try-catch代码块对该代码进行捕获异常处理;
package xuexi.a_regex_demo;
/*
* 需求:验证QQ号码是否合法。
分析:
1、第一位不能是零;
2、QQ号码在5到12之间(包含);
3、QQ号码都是有数字组成;
*/
public class RegexDemo {
public static void main(String[] args) {
method_1();
}
private static void method_1() {
// 定义一个字符串变量
String QQ = "12345676";
/*
* 使用判断结构判断字符串是否合法
*/
// 判断字符串的第一位是否是0 QQ.charAt(0)表示通过charAt函数根据指定的下标获取下标对应的字符
if (QQ.charAt(0) == '0') {
// 说明字符串以0开始
System.out.println("QQ号码不能以0开始");
} else if (QQ.length() < 5 || QQ.length() > 12) {
// 说明qq的号码的长度不在5~12之间
System.out.println("QQ号码的长度错误");
} else {
/*
* 说明QQ的号码一定不是以0开始,并且长度一定在5~12之间,接下来验证是否为数字
* 使用包装类Long中的parseLong()函数判断字符串中的字符是否为数字
* 如果不为数字,这个函数会抛异常alt+shift+z写try-catch代码块
*/
try {
long parseLong = Long.parseLong(QQ);// 将字符串转换为long类型的数字
System.out.println("QQ号码是:" + QQ);
System.out.println("QQ号码是:" + parseLong);
} catch (NumberFormatException e) {
System.out.println("QQ号码中有其他非数字的字符");
}
}
}
}
说明:
1)使用包装类Long中的parseLong()函数判断字符串中的字符是否为数字。如果不为数字,这个函数会抛异常,既然要抛异常所以写个try-catch代码块对异常进行处理;
2)对某块代码进行try-catch处理,可以按alt+shift+z快捷键生成try-catch代码块;
在开发中字符串是我们使用频率最高的一类数据,针对上述程序仅仅就是为了验证一个字符串中的数据是否正确,用上述代码能够解决问题,但是代码很麻烦。在计算机语言中针对这类数据的验证和其他操作给出了更加简单的处理方案。
这个方案就是正则表达式。正则表达式它的存在就是用来简化代码的书写,方便完成对字符串的操作。
说明:
1、String类中提供一个matches()函数,可以判断字符串对象是否匹配正则表达式。
1)如果匹配,则返回true;
2)如果不匹配,则返回false;
2、 [1-9]:表示字符串中第一位能够出现1~9任何一个数字;
3、 [0-9]{4,11}:表示字符串中从第2位开始后面的数字只能出现0~9之间的数字,并且最少出现4次,最多出现11次;
4、 如果满足上述条件则返回true,否则返回false
将上述代码进行优化,结果如下:
package xuexi.a_regex_demo;
/*
* 需求:验证QQ号码是否合法。
分析:
1、第一位不能是零;
2、QQ号码在5到12之间(包含);
3、QQ号码都是有数字组成;
*/
public class RegexDemo {
public static void main(String[] args) {
method_2();
}
// 使用正则表达式完成QQ号码的验证
private static void method_2() {
// 定义一个字符串变量
String QQ = "12345";
/*
* String类中提供一个matches()函数,可以判断字符串对象是否匹配正则表达式
* 如果匹配,则返回true
* 如果不匹配,则返回false
* [1-9]:表示字符串中第一位能够出现1~9任何一个数字
* [0-9]{4,11}:表示字符串中从第2位开始后面的数字只能出现0~9之间的数字,并且最少出现4次,最多出现11次
* 如果满足上述条件则返回true,否则返回false
*/
boolean flag = QQ.matches("[1-9][0-9]{4,11}");
System.out.println(flag);
}
}
总结:正则表达式其实就是通过一些符号简化了代码的书写,其实底层对应的还是代码,只不过是不用我们程序员来书写代码,我们只需要书写正则表达式即可完成相应的功能。
正则表达式的弊端:符号越多,阅读性越差。
所以要想学习正则表达式就得先学习一些符号。
1.2、正则表达式介绍
正则表达式:正确的规则组成的一个表达式。其实就是用来简化字符串的操作。通过一些限定符号组成一种规则,来验证字符串是否符合规则
它的功能主要是用来对字符串进行各种的操作(验证、匹配、切割、替换,获取等)。
结论:正则表达式只能使用在字符串上。
学习正则表达式:主要是学习正则表达式中的常用符号。
正则表达式:它是用我们所熟悉的 大括号、中括号 、小括号、字母 、数字、特殊符号等代替Java代码对字符串进行操作。
1.3、正则表达式常用符号介绍(掌握)
在api中有正则表达式的符号介绍:
我们使用正则表达式其中一个重要的功能就是验证字符串中每个字符是否正确:
学习怎么写一个正则表达式去匹配(验证)字符串的每一位。
正则表达式符号介绍:正则表达式一般也需要使用字符串进行展示:
1、一般符号
x: 指的是普通字符,x代表的是未知数,代表着任何一个普通字符,举例x可以代表a,也可以代表b,同时也可以代表c等普通字符。
举例说明:"a[1-9][0-9]{4,11}"这里所写的a就是普通字符x中的一种,这时x就代表a,那么也就是说此正则表达式的第一位只能是普通字符a;
\ : 表示反斜线 例:“\” 在正则中就是“\”
\t :制表符。相当于tab键。
2、给出某个位置上可以出现的多个字符
[ ]表示范围的意思。表示某一位上可以出现的字符数据,如果正则中需要匹配的某个字符串中某一位上的字符是唯一的,这时可以省略中括号。[]还有一个意思,在正则中还可以表示转义。
[abc] 表示要么是a要么是b还可以是c(只能是其中任意一个)
例:”NBA” 正则:”N[ABC]A” 匹配正确:NBA NAA NCA
[^abc] 当前要匹配的某个字符串中的某一位上不能是a或b 或c(除了a,b,c都可以)
[a-z] 表示26个小写字母
[A-Z] 表示26个大写字母
[a-zA-Z] 表示26个大小写字母
[a-d[m-p]] 当前要匹配的某个字符串中的某一位上 可以是a -d 或 m - p 的字符
[a-d&&[d-f]] 表示只能是d。必须在两个范围中都要符合。(交集)
[a-d&&[^d-f]] 表示只能是a,b,c
[a-z&&[^xyz]] 表示只能是除去x,y,z后的所有小写字母
[0-9] 表示0~9之间任意数字
3、提前定义好的一些符号,可以代替上述的[]书写的范围
·点 表示当前需要匹配的字符串位置上可以是任意的字符。例:以a开始后面可以是任意字符 “a.”
\d 表示数字。[0-9] 例:“A[\d]C” 表示A和C之间可以任意的0~9之间的数字。
说明:为什么在上述正则表达式“A[\d]C”中书写\d,而不是直接书写\d呢?
\d 代表着正则表达式中的一个符号,\和d放在一起代表0~9的十个数字。一旦将\d书写在””双引号中作为字符串,会出现一个问题,\就会把这个d转义了,一旦转义就不表示\d是一起的了,那怎么解决这种问题呢?
我们应该在\d前面在加一个,如:\d,第一个 \ 表示将第二个 \ 转义成普通的反斜线字符,而变成普通的反斜线之后和d组合就代表着正则中的数字,所以我们需要这样写:”\d” 等同于”[0-9]”
\\ 反斜线字符
\D 表示非数字。[^0-9]
\w 表示[a-zA-Z_0-9]</tt>。可以是任意数字、任意大小写字母、下划线。
\W 表示[^a-zA-Z_0-9]表示和\w相反。
4、边界匹配
^ 表示行的开头 例:“^h.” 表示只能是以h作为开头
$ 表示行的结尾 例:”^h.o$” 表示只能以h作为开头,以o作来结尾
5、数量词:表示当前正则表达式中某个规则可以出现的次数。注意:数量词前面必须有存在正则符号。
“A?” 表示当前需要匹配字符串这个位置开始往后大写字母A可以出现零次或一次;
举例:”[0-9]?”表示在当前的字符串位置上0~9之间的任何一个数只能出现零次或者一次;
“A*” 表示当前需要匹配字符串这个位置开始往后大写字母A可以出现零次或多次;
举例:”[0-9]*”表示在当前的字符串位置上0~9之间的任何一个数可以出现零次或者一次或者多次;
“A+” 表示当前需要匹配字符串这个位置开始往后大写字母A可以出现一次或多次;
举例:”[0-9]+”表示在当前的字符串位置上0~9之间的任何一个数可以出现一次或者多次;
“A{n}”----> “A{10}” 表示当前需要匹配字符串这个位置开始往后大写字母A必须出现10次;
举例:”[0-9]{10}”表示在当前的字符串位置上0~9之间的任何一个数可以出现10次;
“A{n,}----->”“A{10,}”表示当前需要匹配字符串这个位置开始往后大写字母A最少出现10次;
举例:”[0-9]{10,}”表示在当前的字符串位置上0~9之间的任何一个数最少出现10次;
“A{n,m}”---->“A{10,20}”表示当前需要匹配字符串这个位置开始往后大写字母A最少出现10次,最多20次;
举例:”[0-9]{10,20}”表示在当前的字符串位置上0~9之间的任何一个数最少出现10次,最多20次;
1.4、正则的功能介绍(掌握)
正则表达式的主要功能:
它主要是用来对字符串进行操作:匹配(验证)、切割、替换、获取。
正则表达式需要和String类中的某些函数结合使用。
1.4.1、匹配
根据指定的正则表达式匹配字符串,匹配正确返回的是true,匹配错误,返回false。
需求:验证手机号码
分析:手机号码的规则:
1)长度必须是11位;
2)第一位只能是数字1;
3)第二位可以是3 4 5 7 8;
4)从第三位开始可以是0-9
步骤:
1)定义一个RegexDemo1类,在这个类中定义一个主函数main;
2)在main函数中定义一个String类型的变量tel,并赋值为15066668888;
3)定义一个手机号码的正则规则regex=”1[34578][0-9]{9}”;
4)使用字符串变量tel调用String类中的matches()函数,regex正则规则作为参数进行传递,打印结果;
package xuexi.a_regex_demo;
/*
* 需求:验证手机号码
分析:手机号码的规则:
1)长度必须是11位;
2)第一位只能是数字1;
3)第二位可以是3 4 5 7 8;
4)从第三位开始可以是0-9
*/
public class RegexDemo1 {
public static void main(String[] args) {
// 定义一个字符串
String tel = "15066668888";
// 定义一个手机号的正则
String regex = "1[34578][0-9]{9}";
// 使用字符串对象tel调用String类中的matches函数,判断字符串是否匹配正则表达式
System.out.println(tel.matches(regex));
}
}
1.4.2、切割
需求:使用String类中的split函数根据正则表达式规则,以数字对已知的字符串进行切割。
1)定义RegexDemo2 类;
2)在这个类中定义一个字符串str,并赋值为”sfajs12321dbfj234d23sjfk454sdjf565sdhd757hf”;
3)定义一个正则表达式规则:regex=”\d+”;
4)使用定义好的字符串str调用split()函数对正则表达式进行切割;
5)遍历切割后的数组;
package xuexi.a_regex_demo;
/*
* 需求:使用String类中的split函数根据正则表达式规则,以数字对已知的字符串进行切割。
1)定义RegexDemo2 类;
2)在这个类中定义一个字符串str,并赋值为”sfljs12321dlfj234d23sjfk454sdjf565sdhd757hf”;
3)定义一个正则表达式规则:regex=”\\d+”;
4)使用定义好的字符串str调用split()函数对正则表达式进行切割;
5)遍历切割后的数组;
*/
public class RegexDemo2 {
public static void main(String[] args) {
// 定义一个字符串
String str = "sfajs12321dbfj234d23sjfk454sdjf565sdhd757hf";
// 定义一个正则表达式,以数字对上述字符串进行切割{"sfajs","dbfj","d","sjfk"}
String regex = "\\d+";
String[] strs = str.split(regex);
// 遍历数组
for (int i = 0; i < strs.length; i++) {
// 打印数组中的数据
System.out.println(strs[i]);
}
}
}
需求:使用String类中的split函数根据正则表达式规则,以叠词对已知的字符串进行切割。
叠词:就是重复出现的字符。
1)在RegexDemo2类中定义一个method_2函数;
2)在method_2函数中定义一个字符串str,并赋值为
”sfljs#######lfj234#######k454sd#####sdhd######hf”;
3)定义一个正则表达式规则:regex=”#+”;
4)使用定义好的字符串str调用split()函数对正则表达式进行切割;
5)遍历切割后的数组;
public class RegexDemo2 {
public static void main(String[] args) {
method_2();
}
//以同一个叠词 切割
public static void method_2() {
String str = "sfljs#######lfj234#######k454sd#####sdhd######hf";
String regex = "#{2,}";
String[] split = str.split(regex);
for (int i = 0 ; i<split.length ; i++) {
System.out.println(split[i]);
}
}
1.4.3、正则中的组
需求:以叠词对已知字符串”sfljs####lfj234TTTTTTTk454sdOOOOOOOsdhd11111111hf”进行切割。
分析:这个字符串不再像我们之前做过的字符串,他比较特殊,我们之前的叠词都是一样的字符,而这个叠词中的字符都不相同,如果按照我们之前去切割是不能够实现的,那么我们该如何去切割已知的字符串呢?
我们需要借助正则中的组来完成。
正则中组的概念:
组:把已经存在的一个正则规则使用小括号封装起来,当在正则表达式中的其他位置上需要使用
已经存在的正则规则的时候,这时没有必要再书写重复的规则,而直接去引用被封装好的正则规则。
例如:"([a-z_A-Z])bc[a-z_A-Z]"
上述正则表达式:在第一位和第四位上是相同的正则规则,同一正则表达式中不同位置上存在了相同规则的正则,在书写上重复没有必要。我们可以在正则表达式中的第一个位置上把[a-z_A-Z] 这个规则进行封装到一个组中。然后在正则的第四位上需要使用和第一位上相同的规则即可。 这时可以在第四位上引用这个被封装好的组。
在正则中一旦把某个规则使用小括号封装成组之后,由于我们只能使用小括号进行组的封装,而无法给组起名,这时会自动的给这些组进行编号,组的编号从1开始,一个小括号就是一组。
如果在当前分组的这个正则表达式中引用已经存在的组,需要使用\\组的编号
例如:"([a-z_A-Z])bc\\1"
需求:使用String类中的split函数根据正则表达式规则,以叠词对已知的字符串进行切割。(练习正则表达式中的组的概念)
1)在RegexDemo2类中定义一个method_3函数;
2)在method_3函数中定义一个字符串str,并赋值为
”sfljs####lfj234TTTTTTTk454sdOOOOOOOsdhd11111111hf”;
3)定义一个正则表达式规则:regex=”(.)\1+”;
4)使用定义好的字符串str调用split()函数对正则表达式进行切割;
5)遍历切割后的数组;
需求:电子邮箱匹配的练习;
分析:
1)电子邮箱的@符号是固定的,前面是用户名,后面是一般公司的域名信息;
2)用户名可以是数字 字母 下划线;
3)公司域名可以是数字 字母;
步骤:
1)定义RegexDemo3类;
2)在这个类中定义一个字符串email,并赋值为”heixuanfeng@163.com”;
3)定义一个正则表达式规则:
regex=”[0-9a-zA-Z_]+@[0-9a-zA-Z]+(\.[a-zA-Z]+){1,3}”;
4)使用字符串对象email调用String类中的matches函数,regex作为正则表达式规则,并打印结果;
package xuexi.a_regex_demo;
public class RegexDemo3 {
public static void main(String[] args) {
//定义一个字符串
String str="heixuanfeng@163.com.cn";
//定义一个正则表达式来验证邮箱
//(\\.[a-zA-Z]+){1,3}表示.com.cn可以出现1到3次
String regex="\\w+@[0-9a-zA-Z]+(\\.[a-zA-Z]+){1,3}";
System.out.println(str.matches(regex));
}
}
1.4.4、替换
案例1:替换的简单应用
需求:
1)定义一个RegexDemo4类,在这个类中定义一个main函数;
2)在main函数中定义一个字符串str,并赋值为hello world;
3)使用str字符串对象调用String类中的replaceAll()函数将字符串中的l替换为i,并重新生成一个字符串,并打印;
案例2:把字符串中的所有#号变为-号,最后变成一个-
需求:
1)在RegexDemo4类中定义一个method_2函数;
2)在method_2函数中定义一个字符串str,并赋值为
“sdhf#jksdhf1232###j45k45dsh54######65f765j#######7kd”;
3)使用str字符串对象调用String类中的replaceAll()函数将字符串中所有的#替换为-,并重新生成一个字符串,并打印;
案例3:把字符串中的多个相同字符,替换成一个字符
原字符串:“abc###nbaAAAAAAsh000000xuexi#####cbaXXXXXXcom”
替换后:” abc#nbaAsh0xuexi#cbaXcom
说明:
前面学习了在一个正则表达中使用前面已经封装的正则规则,使用\组号引用
如果需要在其他地方引用正则中的组,这里需要使用 $组号。
需求:
1)在RegexDemo4类中定义一个method_3函数;
2)在method_3函数中定义一个字符串str,并赋值为
“abc###nbaAAAAAAsh000000xuexi#####cbaXXXXXXcom”;
3)使用str字符串对象调用String类中的replaceAll()函数将字符串中所有的多个相同的字符替换成一个字符,并重新生成一个字符串,并打印;
案例4:隐藏手机号码中间4位 150****8888
需求:
1)在RegexDemo4类中定义一个method_4函数;
-
在method_4函数中定义一个字符串str,并赋值为”15066668888”;
3)把正则表达式分为三组,第二组使用****替换,使用str字符串对象调用String类中的replaceAll()函数将字符串中的中间四位手机号替换成****,并重新生成一个字符串,并打印;
1.4.5、获取
需求:案例:获取字符串中的所有手机号码
“shshska13966668888hdjd13966668888iaj”
正则的获取功能指的是从一个字符串中截取出来我们需要的子串。
需要学习2个对象:
1、正则表达式对象
2、匹配器对象
正则表达式对象:正则表达式是计算机语言中存在一类具备特定功能的表达式,那么Java对这类表达式使用类的描述和封装。
正则表达式是在java中存在的一种规则,而java语言对于存在的事物,都会使用类来描述。在java.util.regex包中存在一个用来描述正则表达式的类:
通过查阅API,可以获得:
1)Pattern类是最终的类,不能被继承;
2)Pattern类是java中正则表达式的实例对象,而我们书写好的一个正则表达式,被封装成Pattern的对象之后,这时我们就可以通过对象的方式来操作正则表达式;
3)Pattern类没有构造函数,我们不能直接new这个类的对象。一般不能new对象的类,都会在这个类中给我们提供静态的函数,获取本类的对象。所以需要使用compile()方法获取Pattern类实例;
4)先要有Pattern类的实例(正则表达式的实例对象),通过实例对象创建匹配器对象(Matcher类),最后是使用匹配器对象中的matcher方法来对正则进行验证;
就可以把一个字符串形式的正则表达式,变成Pattern对象。
需求:定义一个手机号的正则表达式,使用Pattern类中的compile函数将定义好的正则表达式编译成正则对象。
说明:经过对正则表达式的编译,我们就得到了一个正则对象,这个对象中封装的就是那个正则表达式。
把正则表达式编译成正则对象之后,这时只有一个正则对象,是无法和需要匹配或验证的字符串进行关联。
这时必须让自己的正则对象和需要匹配的字符串通过中间的匹配器进行关联,然后才能去操作。
匹配器对象:
在Pattern类中提供matcher函数,可以让当前的正则对象与需要匹配的字符串进行关联,然后得到一个匹配器对象,我们通过这个匹配器对象就可以去通过正则操作字符串。
案例:获取字符串中的所有手机号码
“shshska13966668888hdjd13966668888iaj”
步骤:
1)定义一个手机号码的正则表达式regex=”1[34578][0-9]{9}”;
2)使用Pattern类调用Pattern类中的compile函数对上述正则表达式进行编译生成一个正则对象p;
3)定义一个字符串str=”sdjfklsdjf13867891234ksdjfuiotk”;
4)使用正则对象p调用Pattern类matcher函数,str作为字符串,生成一个匹配器对象matcher;
5)使用匹配器对象matcher调用Matcher类中的find()函数去字符串str中查找根据指定的正则表达式的结果,找到手机号返回结果true,找不到返回false;
6)由于字符串可以有多个手机号,所以使用while循环去查找,而matcher.find()作为while循环的循环条件;
7)使用匹配器对象matcher调用Matcher类中的group()函数找出符合正则的子字符串;
说明:
1)find()表示根据正则表达式去字符串中查找,如果找到,则返回true,找不到返回false;
2)group()表示根据正则表达式去字符串中找出符合正则的子字符串;
正则对象和匹配器的使用:
1、需要把一个正则表达式,通过Pattern类中的compile函数编译成正则对象;
2、使用Pattern类中matcher方法让正则对象和需要操作的字符串产生关系,得到一个匹配器对象;
3、使用匹配器中的find进行匹配,使用group方法获取到匹配到的字符串;
正则的功能:
字符串的匹配、切割、替换、获取
正则的符号:
. 表示任意的字符
() 对已经存在的正则进行分组 同一正则引入组 \编号 ,不同 $编号
[] 某个字符串位置上可以出现的字符列表
{}当前某个正则规则出现的次数
? 表示当前的规则可以出现零次或一次
- 表示当前的规则可以出现零次或一次或多次
- 表示当前的规则可以出现一次或多次
^ 行开始
$ 行结尾
\d 数字字符 [0-9]
\w 数字 字母 下划线[0-9a-zA-Z_]