【正则表达式详解|Java】从0学习实战够用
学习Java爬虫时顺手补缺笔记。正则表达式学了不用经常忘记并且大部分工作可以直接ai,因此这里要求够用,能看到即可。
·
目录
学习Java爬虫时顺手补缺笔记。正则表达式学了不用经常忘记并且大部分工作可以直接ai,因此这里要求够用,能看到即可。
Java 正则前置须知
- 核心类:Java 中操作正则依赖
java.util.regex包下的两个类:Pattern:编译正则表达式(Pattern.compile(regex)),生成不可变的正则对象。Matcher:执行匹配操作,通过pattern.matcher(input)获取,提供find()、group()等方法。
- 关键转义:Java 字符串中
\是转义字符,因此正则中的\必须写成\\(双重转义),例如:- 原生正则
\d(匹配数字),在 Java 中要写为\\d。 - 原生正则
\"(匹配双引号),在 Java 中要写为\\\"(先转义\,再转义")。
- 原生正则
- 匹配流程:编译正则 → 获取匹配器 → 执行匹配 → 提取结果(对应你之前的代码逻辑)。
Java 正则常用语法分类详解
分类 1:基础元字符(匹配单个字符)
元字符是正则的基础,用于匹配单个特定字符或字符类型,以下是最常用的元字符,标注「原生正则」和「Java 写法」的区别。
| 原生正则 | Java 写法 | 含义说明 | 适用示例 |
|---|---|---|---|
. |
. |
匹配除「换行符 \n」外的任意单个字符 |
匹配 a、1、@、/ 等 |
\d |
\\d |
匹配单个数字(0-9),等价于 [0-9] |
匹配手机号、身份证号中的数字 |
\D |
\\D |
匹配单个非数字字符,等价于 [^0-9] |
提取字符串中的非数字部分 |
\w |
\\w |
匹配单个「单词字符」(字母、数字、下划线),等价于 [a-zA-Z0-9_] |
匹配变量名、用户名 |
\W |
\\W |
匹配单个「非单词字符」,等价于 [^a-zA-Z0-9_] |
匹配标点符号、特殊符号 |
\s |
\\s |
匹配单个空白字符(空格、制表符 \t、换行符 \n 等) |
去除字符串中的多余空白 |
\S |
\\S |
匹配单个非空白字符 | 提取字符串中的有效内容(排除空白) |
\\ |
\\\\ |
匹配反斜杠 \ 本身(特殊字符需要转义) |
匹配文件路径中的 \ |
\. |
\\. |
匹配小数点 . 本身(. 是元字符,需转义) |
匹配邮箱、URL 中的小数点 |
示例代码(匹配单个数字)
String regex = "\\d"; // Java写法,对应原生\d
String input = "abc123def";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
System.out.println("匹配到数字:" + matcher.group()); // 输出:1、2、3
}
分类 2:量词(控制匹配次数)
量词用于指定「前面的字符 / 元字符 / 捕获组」的匹配次数,分为「贪婪匹配」(默认,尽可能多匹配)和「非贪婪匹配」(加 ?,尽可能少匹配),是正则的核心常用语法。
| 语法 | 含义说明 | 贪婪 / 非贪婪 | 示例(Java 写法) |
|---|---|---|---|
* |
匹配前面的内容 0 次或多次 | 贪婪 | \\d*:匹配 0 个或多个数字(可空) |
+ |
匹配前面的内容 1 次或多次 | 贪婪 | \\d+:匹配 1 个或多个数字(非空) |
? |
匹配前面的内容 0 次或 1 次 | 贪婪 | \\d?:匹配 0 个或 1 个数字 |
{n} |
匹配前面的内容恰好 n 次 | 贪婪 | \\d{6}:匹配恰好 6 个数字(如验证码) |
{n,} |
匹配前面的内容至少 n 次 | 贪婪 | \\d{8,}:匹配至少 8 个数字(如密码) |
{n,m} |
匹配前面的内容至少 n 次,最多 m 次 | 贪婪 | \\d{6,18}:匹配 6-18 个数字(如账号) |
*? |
匹配前面的内容 0 次或多次 | 非贪婪 | (.*?):你之前提取 firstUrl 用到的非贪婪捕获 |
+? |
匹配前面的内容 1 次或多次 | 非贪婪 | \\d+?:匹配最少 1 个数字,遇到非数字即停止 |
关键区别:贪婪 vs 非贪婪(实战重点)
String input = "num1=123&num2=456";
// 贪婪匹配:\\d+(尽可能多匹配,直到最后一个数字)
String greedyRegex = "num1=(\\d+)";
Matcher greedyMatcher = Pattern.compile(greedyRegex).matcher(input);
if (greedyMatcher.find()) {
System.out.println("贪婪匹配结果:" + greedyMatcher.group(1)); // 输出:123(正确,因为&是非数字,停止)
}
// 若输入为 "num1=123456",贪婪和非贪婪结果一致;若输入为 "num1=123num2=456"
String input2 = "num1=123num2=456";
String nonGreedyRegex = "num1=(\\d+?)"; // 非贪婪匹配
Matcher nonGreedyMatcher = Pattern.compile(nonGreedyRegex).matcher(input2);
if (nonGreedyMatcher.find()) {
System.out.println("非贪婪匹配结果:" + nonGreedyMatcher.group(1)); // 输出:1(最少匹配1个数字即停止)
}
分类 3:字符类(匹配指定范围的字符)
用 [] 包裹,用于匹配「其中任意一个字符」,支持范围指定和排除逻辑,是验证场景的常用语法。
| 语法 | 含义说明 | Java 示例写法 |
|---|---|---|
[abc] |
匹配 a、b、c 中的任意一个字符 |
"[abc]"(匹配单个小写字母 a/b/c) |
[a-z] |
匹配任意一个小写英文字母(a 到 z) | "[a-z]"(范围匹配,用 - 连接) |
[A-Z] |
匹配任意一个大写英文字母(A 到 Z) | "[A-Z]" |
[0-9] |
匹配任意一个数字(等价于 \\d) |
"[0-9]" |
[a-zA-Z0-9] |
匹配任意一个大小写字母或数字(等价于 \\w,不含下划线) |
"[a-zA-Z0-9]" |
[^abc] |
匹配除 a、b、c 之外的任意一个字符(^ 表示排除) |
"[^abc]" |
[^0-9] |
匹配除数字之外的任意一个字符(等价于 \\D) |
"[^0-9]" |
示例(验证是否为小写字母)
String regex = "^[a-z]+$"; // ^ 行首,$ 行尾,+ 至少1个
String input1 = "abc";
String input2 = "Abc123";
System.out.println(Pattern.matches(regex, input1)); // 输出:true
System.out.println(Pattern.matches(regex, input2)); // 输出:false
分类 4:边界匹配器(匹配位置,非字符)
用于匹配「字符串 / 行的边界位置」,不匹配具体字符,常用于「完整验证」(如手机号、邮箱的全量匹配)。
| 原生正则 | Java 写法 | 含义说明 | 适用场景 |
|---|---|---|---|
^ |
^ |
匹配字符串的行首(整个输入的开头) | 验证字符串是否以指定内容开头 |
$ |
$ |
匹配字符串的行尾(整个输入的结尾) | 验证字符串是否以指定内容结尾 |
\b |
\\b |
匹配单词边界(单词与非单词的分界) | 提取单个单词、避免部分匹配 |
\B |
\\B |
匹配非单词边界 | 匹配单词内部的内容 |
关键示例(完整验证手机号,核心场景)
// 手机号规则:1开头,第二位3-9,后面9位数字,完整匹配(不能有多余字符)
String phoneRegex = "^1[3-9]\\d{9}$";
String phone1 = "13812345678";
String phone2 = "138123456789"; // 多1位
String phone3 = "a13812345678"; // 前面有多余字符
System.out.println(Pattern.matches(phoneRegex, phone1)); // true
System.out.println(Pattern.matches(phoneRegex, phone2)); // false
System.out.println(Pattern.matches(phoneRegex, phone3)); // false
值得注意:
在Pattern.matches()中,显式书写^和$是可选的,因为方法本身会自动隐含完整匹配逻辑,结果不受影响;
在Matcher.find()(通用正则匹配)中,^和$是必不可少的,用于实现「完整字符串验证」,避免匹配到多余字符的子串;
分类 5:捕获组与非捕获组(提取 / 复用内容)
用 () 包裹,用于「捕获匹配结果」或「分组匹配」,分为「捕获组」(可提取、可反向引用)和「非捕获组」(仅匹配,不保存结果,节省资源)。
1. 捕获组(常用)
| 语法 | 含义说明 | Java 示例 |
|---|---|---|
(exp) |
普通捕获组,编号从 1 开始(按左括号顺序) | "(\\d{3})-(\\d{8})"(匹配座机号,分组 1 为区号,分组 2 为号码) |
(?<name>exp) |
命名捕获组,给捕获组起名字,方便提取 | "(?<area>\\d{3})-(?<number>\\d{8})"(命名 area 和 number) |
\\n |
反向引用,引用第 n 个捕获组的内容(匹配与该组相同的内容) | "(\\w)\\1"(匹配连续两个相同的单词字符,如 aa、11) |
示例(提取座机号的区号和号码)
String regex = "(\\d{3})-(\\d{8})";
String input = "北京区号:010-12345678,上海区号:021-87654321";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
System.out.println("完整座机号:" + matcher.group(0)); // 第0组:整个匹配结果
System.out.println("区号:" + matcher.group(1)); // 第1组:3位区号
System.out.println("号码:" + matcher.group(2)); // 第2组:8位号码
}
2. 非捕获组(仅匹配,不提取)
语法:(?:exp),用于分组匹配但不需要保存结果,适合复杂正则的分组逻辑,避免多余的捕获组占用资源。
// 匹配 "abc" 或 "abd",用非捕获组分隔,无需提取分组结果
String regex = "ab(?:c|d)";
String input1 = "abc";
String input2 = "abd";
String input3 = "abe";
System.out.println(Pattern.matches(regex, input1)); // true
System.out.println(Pattern.matches(regex, input2)); // true
System.out.println(Pattern.matches(regex, input3)); // false
正则语法部分,我觉得捕获组,贪婪/非贪婪匹配,边界匹配的概念知道就很够用了。
如有疏漏与错误还请在评论区讨论交流!

更多推荐



所有评论(0)