目录
- 一、代码逻辑
- 二、底层逻辑分析
-
- 1.matcher.find()
- 2.matcher.group(0)
- 三、正则表达式语法
-
- 1.转义符
- 2.字符匹配符
- 3.选择匹配符
- 4.限定符
- 5.定位符
- 6.分组
- 7.反向引用
- 四、常用的类
-
- 五、应用实例
-
- 1.验证输入的字符串是不是汉字
- 2.验证邮政编码
- 3.验证QQ号
- 4.验证手机号码
- 5.验证URL地址
- 6.结巴程序
- 6.String类的替换分割匹配
一、代码逻辑
public class Test2 {
public static void main(String[] args) {
String s = "一百多年后,四色问题仍未解决。1969年,Heinrich Heesch发表了一个用计算机" +
"解决此问题的方法。1976年,阿佩尔(Appel)和哈肯(Haken)借助计算机给出了一个证明," +
"此方法按某些性质将所有地图分为1936类并利用计算机,运行了1200个小时,验正了它们可" +
"以用四种颜色染色。四色定理是第一个主要由电脑证明的理论,这一证明并不被所有的数学家接受" +
",因为采用的方法不能由人工直接验1111证。最终,人们必须对电脑编译的正确性以及运行这一程序" +
"的硬件设备充分信任。主要是因为此证明缺乏数学应1112有的规范,以至于有人这样评论“一个好的数" +
"学证明应6666当像一首诗——而3333这纯粹是一本电话簿!";
String regStr = "\\d\\d\\d\\d";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println("找到: " + matcher.group(0));
}
}
}
二、底层逻辑分析
1.matcher.find()
matcher.find()完成的任务
1.根据匹配规则,定位满足规则的子字符串(比如1969)
2.找到后,将子字符串的开始索引记录到 matcher 对象的属性 int[] groups,groups[0]=0,
把该子字符串的结束的索引+1的值记录到groups[1]=4
如果正则表达式有分组,则groups[2] groups[3]记录第一组字符串的开始和结束位置
其余组以此类推
3.同时记录 oldLast 的值,即该子字符串的结束的索引+1的值,即下次执行find时,就从4开始匹配
4.当一次匹配成功后,下一次groups[0]存储下一个符合条件的字符串的开始位置,groups[1]存储的
是其结束位置 周而复始直到文本遍历结束
2.matcher.group(0)
public String group(int group) {
if (first < 0)
throw new IllegalStateException("No match found");
if (group < 0 || group > groupCount())
throw new IndexOutOfBoundsException("No group " + group);
if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
return null;
return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
}
三、正则表达式语法
1.转义符
\\
在我们使用正则表达式去检索某些特殊字符时,需要用到转移符号,负责检索不到
在java的正则表达式中,两个 \\ 代表其他语言中的一个 \
需要用到转移符号的字符有如下字符:
. * + ( ) $ / \ ? [ ] ^ { }
private static void test(){
String s = "abc.$(abc.(123(";
String regStr = "\\(";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
2.字符匹配符
符号 |
含义 |
示例 |
解释 |
匹配输入 |
[ ] |
可接收的字符列表 |
[abcew] |
匹配a b c e w中的任意一个字符 |
|
[^] |
不接收的字符列表 |
[^abc] |
匹配除a b c之外的任意一个字符,包括数字和特殊字符 |
|
- |
连接符 |
a-z |
匹配任意单个小写字母 |
|
. |
匹配除 \n 以外的任何字符 |
a…b |
匹配以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 |
aaab #a1o |
\\d |
匹配单个数字字符,相当于[0-9] |
\\d{3}(\\d)? |
包含3个或4个数字的字符串 |
123 4351 |
\\D |
匹配单个非数字字符,相当于[^0-9] |
\\D(\\d)* |
以单个非数字字符开头,后接任意个数字字符串 |
a #124 |
\\w |
匹配单个数字、大小写字母、下划线字符,相当于[0-9a-zA-Z] |
\\d{3}\\w{4} |
以3个数字字符开头的长度为7的字符串 |
123nmed 4322swe |
\\W |
匹配单个非 数字、大小写字母下划线字符,相当于[^0-9a-zA-Z] |
\\W+\\d{2} |
以至少1个非数字字母字符开头,2个数字字符结尾的字符串 |
#23 %()10 |
\\s |
匹配任何空白字符(空格 制表符等) |
\\s |
|
|
\\S |
匹配任何非空白字符,与\\s相反 |
\\S |
|
|
java匹配字母字符时默认区分大小写,可以使用以下方法设置不区分大小写
(1)(?i)
(?i)abc 表示abc都不区分大小写
a(?i)bc 表示bc不区分大小写
a((?i)b)c 表示只有b不区分大小写
(2)Pattern.CASE_INSENSITIVE 在创建模式对象时加这个
Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
private static void test1(){
String s = "a11c8AQ4Wz";
String regStr = "[a-z]";
Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
3.选择匹配符
符号 |
含义 |
示例 |
解释 |
| |
匹配 | 之前或之后的表达式 |
ab|cd |
ab或者cd |
private static void test2(){
String s = "1231one一";
String regStr = "1|one|一";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
4.限定符
用于指定其前面的字符和组合项连续出现多少次
符号 |
含义 |
示例 |
说明 |
匹配输入 |
* |
指定字符重复0次或者n次(无要求) |
(abc)* |
仅包含任意个abc的字符串 |
abc abcabc |
+ |
指定字符重复1次或者n次 |
m+(abc)* |
以至少1个m开头,后接任意个abc的字符串 |
m mabc mabcabc |
? |
指定字符重复0次或者1次 |
m+abc? |
以至少1个m开头,后接ab或abc的字符串 |
mab mabc mmab |
{n} |
只能输入n个字符 |
[abcd]{3} |
由abcd中字母组成的任意长度为3的字符串 |
abc dbc adc |
{n,} |
指定至少n个匹配 |
[abcd]{3,} |
由abcd中字母组成的任意长度不小于3的字符串 |
aab dbc aaadca |
{n,m} |
指定至少n个但不多于m个匹配 |
[abcd]{3,5} |
由abcd中字母组成的任意长度不小于3 不大于5的字符串 |
abc abcd aaaaa cdba |
java中默认是贪婪匹配:a{3,5} 在匹配过程中尽量多的匹配a,匹配优先级:aaaaa > aaaa > aaa
private static void test3(){
String s = "12adee3421dsacc543";
String regStr = "\\d+";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
5.定位符
规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置
符号 |
含义 |
示例 |
说明 |
匹配输入 |
^ |
指定起始字符 |
+[a-z]* |
以至少1个数字开头,后接任意个小写字母的字符串 |
123 3ac |
$ |
指定结束字符 |
+[a-z]+$ |
以至少1个数字开头,并以至少1个小写字母结尾的字符串 |
11a |
\\b |
匹配目标字符串的边界 |
hello\\b |
边界:子串间有空格,或者是目标字符串的结束位置 |
|
\\B |
匹配目标字符串的非边界 |
hello\\B |
和\\b的含义相反 |
|
6.分组
常用分组构造形式 |
说明 |
(pattern) |
非命名捕获 捕获匹配的子字符串 编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号 |
(?pattern) |
命名捕获 将匹配的子字符串捕获到一个组名或者编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替<> |
|
|
private static void test4(){
String s = "1234adace2314de9999";
String regStr = "(\\d\\d)(\\d\\d)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.print("整个内容: " + matcher.group(0) + " ");
System.out.print("第1个分组内容: " + matcher.group(1) + " ");
System.out.println("第2个分组内容: " + matcher.group(2));
}
}
7.反向引用
反向引用:圆括号的内容被捕获后,可以在这个括号后被使用
这种引用既可以是在正则表达式的内部,也可以是在正则表达式的外部
内部反向引用使用 \\分组号 ,外部反向引用使用 $分组号
举例:
匹配两个连续的相同数字:(\\d)\\1
匹配五个连续的相同数字:(\\d)\\1{4}
匹配个位与千位相同,十位与百位相同的数:(\\d)(\\d)\\2\\1
private static void test11(){
String s = "1111and2332sdniew1234kd13";
String regStr = "(\\d)(\\d)\\2\\1";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println("找到: " + matcher.group(0));
}
}
private static void test12(){
String s = "12321-333999111";
String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println("找到: " + matcher.group(0));
}
}
四、常用的类
1.Pattern类
private static void test10(){
String s = "hello welcome to china";
String regStr = ".*come.*";
boolean matches = Pattern.matches(regStr, s);
System.out.println("整体匹配: " + matches);
}
2.Matcher类
五、应用实例
1.验证输入的字符串是不是汉字
private static void test5(){
String s = "为中华之崛起而读书";
String regStr = "^[\u0391-\uffe5]+$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
2.验证邮政编码
private static void test6(){
String s = "123540";
String regStr = "^[1-9]\\d{5}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
3.验证QQ号
private static void test7(){
String s = "1904459082";
String regStr = "^[1-9]\\d{4,9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
4.验证手机号码
private static void test8(){
String s = "15109546897";
String regStr = "^1[3|4|5|8]\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
5.验证URL地址
private static void test9(){
String s = "https://leetcode.cn/problemset/algorithms/?status=NOT_STARTED&page=1";
String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/.]*)?$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
6.结巴程序
例如 我...是...你你..爸爸爸爸...
输出 我是你爸爸
private static void test13(){
String s = "我我我...是是是是是...你你..爸爸爸爸...";
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = pattern.matcher(s);
s = matcher.replaceAll("");
pattern = Pattern.compile("(.)\\1+");
matcher = pattern.matcher(s);
while (matcher.find()){
}
s = matcher.replaceAll("$1");
System.out.println(s);
s = Pattern.compile("(.)\\1+").matcher(s).replaceAll("$1");
System.out.println(s);
}
6.String类的替换分割匹配
private static void test14(){
String s = "JDK1.3是JDK1.4之前的一个版本,JDK1.3与JDK1.4之间有一些区别";
s = s.replaceAll("JDK1\\.3|JDK1\\.4","JDK");
System.out.println(s);
}
private static void test15(){
String s = "13811112222";
if (s.matches("1(38|39)\\d{8}")){
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
}
private static void test16(){
String s = "hello-boy~welcome#1to3china";
String[] splits = s.split("#|-|~|\\d+");
for (String cur : splits){
System.out.print(cur + " ");
}
}