什么是正则表达式?

正则表达式(Regular Expression,简称 Regex) 是用于匹配、搜索、替换字符串的强大工具。它通过特定的模式(Pattern),匹配文本中的特定字符组合,广泛应用于数据验证、文本处理、爬虫、日志分析等场景。

基本语法

字符 描述
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了 RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。
* 匹配前面的子表达式零次或多次。例如:‘zo*’ 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如:‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如:‘do(es)?’ 可以匹配 “do” 或 “does” 中的 “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?’。请注意在逗号和两个数之间不能有空格。
x|y 匹配 x 或 y。例如,‘z|food’ 能匹配 “z” 或 “food”。‘(z|f)ood’ 则匹配 “zood” 或 “food”。
[xyz] 字符集合,匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。
[^xyz] 负值字符集合,匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’。
[a-z] 字符范围,匹配指定范围内的任意字符。例如,‘[a-z]’ 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。
[^a-z] 负值字符范围,匹配任何不在指定范围内的任意字符。例如,‘[^a-z]’ 可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。
\n 匹配一个换行符。
\r 匹配一个回车符。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。
\S 匹配任何非空白字符。
\t 匹配一个制表符。
\v 匹配一个垂直制表符。
\w 匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]'。
\W 匹配任何非单词字符。

常用实例

  1. 匹配字符
表达式 匹配内容 示例
. 任意单个字符 a.b 可匹配 acb
\d 数字 [0-9] \d\d 可匹配 12
\D 非数字 [^\d] \D 可匹配 a
\w 字母、数字、下划线 [a-zA-Z0-9_] \w+ 可匹配 abc123
\W 非字母、数字、下划线 \W 可匹配 @
\s 空格(包含 \t, \n, \r \s+ 可匹配 " "
\S 非空格字符 \S 可匹配 a
  1. 量词(重复次数)
表达式 匹配次数 示例
* 0 次或多次 a* 可匹配 ""aaaaa
+ 1 次或多次 a+ 可匹配 aaaaa
? 0 次或 1 次 a? 可匹配 ""a
{n} 恰好 n 次 a{3} 可匹配 aaa
{n,} 至少 n 次 a{2,} 可匹配 aaaaaaa
{n, m} n 到 m 次 a{2,4} 可匹配 aaaaaaaaa
  1. 边界匹配
表达式 匹配位置 示例
^ 匹配字符串开头 ^abc 匹配 "abc123" 但不匹配 "123abc"
$ 匹配字符串结尾 xyz$ 匹配 "123xyz" 但不匹配 "xyz123"
\b 匹配单词边界 \bword\b 仅匹配 " word " 而不匹配 "word123"
\B 非单词边界 \Bword\B 匹配 "awordb" 但不匹配 " word "
  1. 分组
表达式 作用 示例
() 分组 (abc)+ 可匹配 "abcabc"
(?:...) 非捕获分组 (?: abc)+ 仅用于匹配,不保存

Java 使用

  1. 直接使用字符串的 matches() 方法
1
2
3
4
5
6
7
8
9
10
11
public class RegexDemo {

public static void main(String[] args) {
String text = "abc123";
boolean isMatch = text.matches("[a-zA-Z]+\\d+");
System.out.println(isMatch); // true
text = "abc123aa";
isMatch = text.matches("[a-zA-Z]+\\d+");
System.out.println(isMatch); // false
}
}

注意:matches() 方法必须匹配整个字符串,如果只匹配部分,则返回 false。

  1. 使用 Pattern 和 Matcher
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RegexExample {

public static void main(String[] args) {
String text = "My email is test@example.com";
String pattern = "[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\\.[a-zA-Z]{2,}";

Pattern regex = Pattern.compile(pattern);
Matcher matcher = regex.matcher(text);

if (matcher.find()) {
System.out.println("匹配的邮箱: " + matcher.group()); //test@example.com
}
}
}

使用案例

  1. 验证邮箱格式
1
2
3
4
5
6
public static boolean isValidEmail(String email) {
String regex = "^[\\w.-]+@[a-zA-Z\\d.-]+\\.[a-zA-Z]{2,6}$";
return email.matches(regex);
}
// 示例
System.out.println(isValidEmail("test@example.com")); // true
  1. 提取手机号码
1
2
3
4
5
6
7
8
9
public static void extractPhoneNumbers(String text) {
String regex = "\\b1[3-9]\\d{9}\\b"; // 中国手机号
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println("手机号: " + matcher.group());
}
}
// 输入: "联系我:13812345678 或 13987654321"
  1. 替换敏感词
1
2
3
4
5
6
public static String replaceSensitiveWords(String input) {
String regex = "(?i)badword|敏感词"; // 不区分大小写
return input.replaceAll(regex, "***");
}
// 输入: "这是一个BadWord示例。"
// 输出: "这是一个***示例。"
  1. 分割字符串
1
2
3
String str = "apple,banana,orange";
String[] fruits = str.split("\\s*,\\s*"); // 允许逗号前后有空格
// 结果: ["apple", "banana", "orange"]
  1. 提取 URL 中的域名
1
2
3
4
5
6
7
8
9
10
11
public static String extractDomain(String url) {
String regex = "^(https?://)?([\\w.-]+)(/.*)?$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
return matcher.group(2); // 第二个分组为域名
}
return null;
}
// 输入: "https://www.example.com/path"
// 输出: "www.example.com"
  1. 分组捕获

使用 () 分组,通过 matcher.group(n) 获取:

1
2
3
4
5
6
Pattern p = Pattern.compile("(\\d{3})-(\\d{4})");
Matcher m = p.matcher("010-1234");
if (m.matches()) {
System.out.println("区号: " + m.group(1)); // 010
System.out.println("号码: " + m.group(2)); // 1234
}

修饰符

修饰符 含义 描述
i ignore - 不区分大小写 将匹配设置为不区分大小写,搜索时不区分大小写:A 和 a 没有区别。
g global - 全局匹配 查找所有的匹配项
m multiline - 多行匹配 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。
s 特殊字符圆点 . 中包含换行符 \n 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, .中包含换行符 \n。

JS 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var str="Google runoob taobao runoob"; 
var n1=str.match(/runoob/); // 查找第一次匹配项
var n2=str.match(/runoob/g); // 查找所有匹配项


var str="Google runoob taobao RUNoob";
var n1=str.match(/runoob/g); // 区分大小写
var n2=str.match(/runoob/gi); // 不区分大小写


var str="runoobgoogle\ntaobao\nrunoobweibo";
var n1=str.match(/^runoob/g); // 匹配一个
var n2=str.match(/^runoob/gm); // 多行匹配


var str="google\nrunoob\ntaobao";
var n1=str.match(/google./); // 没有使用 s,无法匹配\n
var n2=str.match(/runoob./s); // 使用 s,匹配\n

Java 使用

修饰符 作用
Pattern.CASE_INSENSITIVE (?i) 忽略大小写,如 “hello” 可匹配 “HELLO”
Pattern.MULTILINE (?m) 多行匹配,^$ 匹配每行的开头和结尾
Pattern.DOTALL (?s) 让 . 匹配换行符 \n
Pattern.UNICODE_CASE (?u) Unicode 大小写匹配(配合 CASE_INSENSITIVE)
Pattern.COMMENTS (?x) 忽略正则中的空格和注释
Pattern.UNICODE_CHARACTER_CLASS (?U) 使用 Unicode 字符类别
  1. 忽略大小写
1
2
3
4
5
6
7
8
9
Pattern pattern = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher("HELLO");
System.out.println(matcher.find()); // true


String regex = "(?i)hello"; // `(?i)` 代表忽略大小写
pattern = Pattern.compile(regex);
matcher = pattern.matcher("HELLO");
System.out.println(matcher.find()); // true
  1. 多行匹配

默认 ^ 只匹配整个字符串开头,$ 只匹配结尾。使用 MULTILINE 后,^$ 适用于每行

1
2
3
4
5
String text = "Hello\nJava";
Pattern pattern = Pattern.compile("^Java", Pattern.MULTILINE);
// 等价于 Pattern pattern = Pattern.compile("(?m)^Java");
Matcher matcher = pattern.matcher(text);
System.out.println(matcher.find()); // true
  1. . 匹配换行符 \n

默认情况下,. 不会匹配换行符,使用 DOTALL 使 . 能匹配换行符

1
2
3
4
5
6
7
String text = "Hello\nJava";
Pattern pattern = Pattern.compile("Hello.Java");
System.out.println(pattern.matcher(text).find()); // false

pattern = Pattern.compile("Hello.Java", Pattern.DOTALL);
// 等价于 pattern = Pattern.compile("(?s)Hello.Java");
System.out.println(pattern.matcher(text).find()); // true
  1. 组合多个修饰符

可以使用按位或 | 组合多个修饰符

1
2
Pattern pattern = Pattern.compile("hello", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
// 等价于 Pattern pattern = Pattern.compile("(?im)hello");

运算符优先级

运算符 描述
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n, m} 限定符
^, $, \任何元字符、任何字符 定位点和序列(即:位置和顺序)
| 替换,“或” 操作
字符具有高于替换运算符的优先级,使得 “m|food” 匹配 “m” 或 “food”。若要匹配 “mood” 或 “food”,请使用括号创建子表达式,从而产生 “(m|f)ood”。