正则表达式:
正则表达式(简称 regex)是用来描述字符串模式的工具,常用于字符串的查找、匹配、替换等操作。它在文本处理、数据验证、以及编程中非常常见。以下是正则表达式的基本知识点:
1. 正则表达式的基础符号
-
.
(点): 匹配除换行符\n
以外的任何单个字符。- 例如,正则表达式
a.b
可以匹配"aab"
、"acb"
,但不能匹配"ab"
。
- 例如,正则表达式
-
^
(脱字符): 匹配字符串的开始位置。- 例如,正则表达式
^abc
只能匹配以"abc"
开头的字符串,如"abc123"
,但不能匹配"123abc"
。
- 例如,正则表达式
-
$
(美元符号): 匹配字符串的结束位置。- 例如,正则表达式
xyz$
只能匹配以"xyz"
结尾的字符串,如"123xyz"
,但不能匹配"xyz123"
。
- 例如,正则表达式
-
*
(星号): 匹配前面的字符 0 次或多次。- 例如,正则表达式
ab*
可以匹配"a"
、"ab"
、"abb"
、"abbb"
,其中b
可以出现任意次(包括 0 次)。
- 例如,正则表达式
-
+
(加号): 匹配前面的字符 1 次或多次。- 例如,正则表达式
ab+
可以匹配"ab"
、"abb"
,但不能匹配"a"
(b
至少出现 1 次)。
- 例如,正则表达式
-
?
(问号): 匹配前面的字符 0 次或 1 次。- 例如,正则表达式
ab?
可以匹配"a"
或"ab"
。
- 例如,正则表达式
-
[]
(方括号): 定义字符集合,匹配其中的任意一个字符。- 例如,正则表达式
[abc]
可以匹配"a"
、"b"
、"c"
中的任意一个字符。
- 例如,正则表达式
-
[^]
(脱字符加方括号): 匹配不在方括号中的任意一个字符。- 例如,正则表达式
[^abc]
可以匹配任何不是"a"
、"b"
、"c"
的字符。
- 例如,正则表达式
-
|
(竖线): 表示逻辑或,用于匹配多个模式之一。- 例如,正则表达式
abc|def
可以匹配"abc"
或"def"
。
- 例如,正则表达式
-
()
(括号): 用于分组,匹配括号内的整个模式。还可以用于捕获组以便后续引用。- 例如,正则表达式
(abc)+
可以匹配"abc"
、"abcabc"
。
- 例如,正则表达式
2. 元字符
-
\d
: 匹配任何数字,等价于[0-9]
。- 例如,正则表达式
\d
可以匹配"1"
、"7"
、"0"
等数字字符。
- 例如,正则表达式
-
\D
: 匹配任何非数字字符,等价于[^0-9]
。- 例如,正则表达式
\D
可以匹配"a"
、"#"
、" "
等非数字字符。
- 例如,正则表达式
-
\w
: 匹配任何字母、数字或下划线字符,等价于[a-zA-Z0-9_]
。- 例如,正则表达式
\w
可以匹配"a"
、"1"
、"_"
等字母、数字或下划线。
- 例如,正则表达式
-
\W
: 匹配任何非字母、数字或下划线字符,等价于[^a-zA-Z0-9_]
。- 例如,正则表达式
\W
可以匹配"@"
、" "
、"#"
等非字母、数字或下划线字符。
- 例如,正则表达式
-
\s
: 匹配任何空白字符(包括空格、制表符、换行符等)。- 例如,正则表达式
\s
可以匹配" "
、\t
、\n
等空白字符。
- 例如,正则表达式
-
\S
: 匹配任何非空白字符。- 例如,正则表达式
\S
可以匹配"a"
、"1"
、"#"
等非空白字符。
- 例如,正则表达式
3. 量词
-
{n}
: 精确匹配前面的字符n
次。- 例如,正则表达式
a{3}
可以匹配"aaa"
。
- 例如,正则表达式
-
{n,}
: 至少匹配前面的字符n
次。- 例如,正则表达式
a{2,}
可以匹配"aa"
、"aaa"
、"aaaa"
等。
- 例如,正则表达式
-
{n,m}
: 匹配前面的字符至少n
次,至多m
次。- 例如,正则表达式
a{2,4}
可以匹配"aa"
、"aaa"
、"aaaa"
。
- 例如,正则表达式
4. 特殊字符的转义
一些字符在正则表达式中有特殊含义(如 .
、*
、?
、+
等),如果要匹配这些字符本身,需要使用反斜杠 \
进行转义。
例如:
- 要匹配
"."
,你需要使用\.
。 - 要匹配
"*"
,你需要使用\*
。
代码示例:
String regex = "[1-9]\\d{16}[\\dxX]";
String s = "43122320050430012x";
System.out.println(s.matches(regex));
总结:
5. 正则表达式的常用方法
-
匹配:使用
Pattern
和Matcher
类进行匹配。Pattern p= Pattern.compile("\\d+"); Matcher m = p.matcher("123abc456"); boolean matches = m.matches();
-
查找:使用
find()
方法查找下一个匹配。while (matcher.find()) { System.out.println(matcher.group()); }
-
替换:使用
replaceAll()
方法进行替换。String result = matcher.replaceAll("X");
-
分割:使用
split()
方法按正则表达式分割字符串。String[] parts = pattern.split("one1two2three3");
package reptile;
public class Test3 {
public static void main(String[] args) {
/*有一段字符串:小诗诗dqwefqwfqwfwg12312小丹丹dqwefqwfqwfwg12312小惠惠
要求1:把字符串中三个姓名之间的字母替换为vs
要求2:把字符串中的三个姓名切割出来
*/
String s="小诗诗dqwefqwfqwfwg12312小月月dqwefqwfqwfwg12312小惠惠";
//细节:
//方法在底层跟之前一样也会创建文本解析器的对象
// 然后从头开始去读取字符串中的内容,只要有满足的,那么就用第二个参数去替换。
String resut1 = s.replaceAll("[\\w&&[^ ]]+","vs");
System.out.println(resut1);
String[]arr =s.split("[\\w&&[^_]]+");
for(int i=0;i<arr.length; i++) {
System.out.println(arr[i]);
}
}
}
6. 正则表达式的分组
捕获分组:
package reptile;
public class Test{
public static void main(String[] args) {
//需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
// 举例:a123a b456b 17891 &abc& a123b(false)
// \\组号: 表示把第x组的内容再出来用一次
String regex1="(.).+\\1";
System.out.println("a123a".matches(regex1));
System.out.println("b456b".matches(regex1));
System.out.println("17891".matches(regex1));
System.out.println("&abc&".matches(regex1));
System.out.println("a123b".matches(regex1));
//需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
//举例:abc123abc b456b 123789123 &!@abc&!@ abc123abd(false)
String regex2="(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
System.out.println("b456b".matches(regex2));
System.out.println("123789123".matches(regex2));
System.out.println("&!@abc&!@".matches(regex2));
System.out.println("abc123abd".matches(regex2));
//需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
//举例:aaa123aaa bbb456bbb 111789111 &&abc&&
//(.):把首字母看做一组
//\\2:把首字母拿出来再次使用
//*:作用于\\2,表示后面重复的内容出现0次或多次
String regex3="((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex3));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("aaa123aab".matches(regex3));
}
}
后续如何继续使用本组的数据?
正则内部使用:\组号
正则外部使用:$组号
非捕获分组:
package reptile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class test2 {
public static void main(String[] args) {
// 条件爬取
/* 需求1:爬取版本号为8,11,17的Java文本,但是只要Java,不显示版本号。
需求2:爬取版本号为8,11,17的]ava文本。正确爬取结果为:java8 Java11 Java17 Java17
需求3:爬取除了版本号为8,11,17的Java文本,
*/
String s="java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和JAva11," +
"因为这两个是长期支持版本,下一个长期支持版本是JAVa17,相信在未来不久JAVA17也会遂渐登上历史舞台";
//1.定义正则表达式
//?理解为前面的数据Java
//=表示在Java后面要跟随的数据
//但是在获取的时候,只获取前半部分
// 需求1:
String regex1="((?i)Java)(?=8|11|17)";
//需求2:
// ?i表示忽略大小写
String regex2="((?i)Java)(8|11|17)";
// ?理解为前面的数据Java
// 加上后面的数字就需要用:
String regex3="((?i)Java)(?:8|11|17)";
//需求3:
// ?理解为前面的数据Java,!表示去除后面的数字
String regex4="((?i)Java)(?!8|11|17)";
Pattern p=Pattern.compile(regex3);
Matcher m=p.matcher(s);
while(m.find()){
System.out.println(m.group());
}
}
}
总结:
爬虫:
带条件爬取:
带条件爬取是指在爬虫过程中只提取符合特定条件的数据。条件可以基于正则表达式的匹配规则或其他逻辑条件。
package reptile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) {
String s = " 来黑马称序员学习Java" +
"电话:18512516758,18512508907" +
"或者联系邮箱:[email protected]," +
"座机电话:01036517895,010-98951256"+
"邮箱:[email protected],"+
"热线电话:400-618-9090,400-618-4000,4006184000,4006189090";
/*
手机号的正则表达式:1[3-9]\d{9}
箱的正则表达式:\w+@[\w&&[^_]]{2,6}(\.[a-zA-Z]{2,3}){1,2}
座机电话的正则表达式:0\d{2,3}-?[1-9]\d{4,9}
热线电话的正则表达式:400-?[1-9]\\d{2}-?[1-9]\\d{3}
*/
String regex = "(1[3-9]\\d{9})|" +
"(0\\d{2,3}-?[1-9]\\d{4,9})|" +
"(\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2})|" +
"(400-?[1-9]\\\\d{2}-?[1-9]\\\\d{3})";
// 获取正则表达式的对象,相当于规则
Pattern p = Pattern.compile(regex);
// 获取文本匹配器的对象
// 利用m去读取s,会按照p的规则找里面的小串
Matcher m = p.matcher(s);
//利用循环获取每一个数据
// m.find()会返回boolean类型,true表示找到符合规则的小串,否则没有符合的小串,停止循环
// m.find()会标记符合规则的起始索引,结束索引+1,可以通过m.group()找到
while(m.find()){
System.out.println(m.group());
}
}
}
贪婪爬取和非贪婪爬取:
只写+和*表示贪婪匹配
+?非贪婪匹配
*?非贪婪匹配
贪婪爬取:在爬取数据的时候尽可能的多获取数据
非贪婪爬取:在爬取数据的时候尽可能的少获取数据
对于正则表达式为ab+:
贪婪爬取:abbbbbbbbbbbb。非贪婪爬取:ab
Java当中,默认的就是贪婪爬取。