Java正则表达式

Lu Lv3

Java正则表达式

1.概述

  • 正则表达式是一种描述字符串模式的语言。
  • 正则表达式可以用来搜索、编辑或处理文本。
  • Java 提供了 java.util.regex 包,它包含了 Pattern 和 Matcher 类,用于处理正则表达式的匹配操作。

2.作用

  • 验证用户输入的数据是否合法。[检验字符串是否满足某种格式]
  • 提取数据中的有效信息。
  • 替换字符串中的部分字符。[对文本进行格式化]

3.基本语法

符号含义例子
[]里面的内容出现一次[] 内不能嵌套使用 [ ][a-z]: a-z之间的任意字符
()分组[按左括号计算组数,从1开始]a(bc)+: bc为第一组,+作用在第一组(bc)
^取反,在表示取反的时候必须与 [] 搭配使用,位于 [] 的开头[^a-z]: 不是a-z之间的任意字符
&&交集,不能写单个的 &[a-z&&[^bc]]: a-z之间的任意字符,但排除bc
|写在方括号外面表示并集,不能写两个的 ||[a-zA-Z]: a-z或A-Z之间的任意字符。 x|X: 大小写x都可以。 注意: [a-z|A-Z]中的 | 只是一个普通的字符,并不表示并集。
.任意一个字符[回车符号不匹配]a.b: 可以是acb、a0b、a吖b等。
\转义字符\\d : 匹配一个数字字符[0-9]。其中第一个\是用作转义字符,用于将后面的\转义成普通字符。第二个\后面紧跟着的d表示匹配一个数字字符。在正则表达式中,\d 是一个特殊的元字符,用于匹配数字字符。
\d数字\\d: 匹配一个数字字符[0-9]。
\D非数字\\D: 匹配一个非数字字符[^0-9]。
\s空格\\s: 匹配一个空白字符[ \t\n\x0B\f\r]。
\S非空格\\S: 匹配一个非空白字符[^ \t\n\x0B\f\r]。
\w字母数字下划线\\w: 匹配一个单词字符[a-zA-Z_0-9]。
\W非字母数字下划线\\W: 匹配一个非单词字符[^a-zA-Z_0-9]。
?0或1次[作用在前者]a?b: ab或b。?作用在a上,表示a为可选字符。
*0或n次[作用在前者]a*b: ab、abb、abbbb、…。*作用在a上,表示a为0或n次。
+1或n次[作用在前者]a+b: ab、abb、abbbb、…。+作用在a上,表示a为1或n次。
{n}n次[作用在前者,填写具体次数]a{2}b: abb。{n}作用在a上,表示a为n次。
{n,m}n到m次[作用在前者,填写范围]a{2,4}b: abb、abbb、abbbb。{n,m}作用在a上,表示a为n到m次。
{n,}n次及以上[作用在前者,填写范围]a{2,}b: abb、abbb、abbbb、…。{n,}作用在a上,表示a为n次以上。
{,m}0到m次[作用在前者,填写范围]a{,4}b: abb、abbb、abbbb、…。{,m}作用在a上,表示a为0到m次。
(?i)忽略大小写[作用在后者](?i)abc: 匹配abc或ABC、…。a((?i)b)c: 只忽略b的大小写。
[\u4E00-\u9FFF·]中文[\u4E00-\u9FFF·]

Java的反斜杠\:

  • 在其他语言中,\ 表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。
  • 在 Java 中,\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。

所以,在其他的语言中(如 Perl),一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达式是 \\d,而表示一个普通的反斜杠是 \\。

1
2
System.out.print("\\");    // 输出为 \
System.out.print("\\\\"); // 输出为 \\

^ 的两层含义:

  • 在方括号中^ 用于定义一个字符集合的取反。它位于方括号 [] 的开头(即字符集的第一个位置)时,表示匹配不在该字符集合中的任意字符。
  • 在方括号外^ 作为行开头的锚点(anchor),用于匹配字符串的开头(或在多行模式下,匹配每一行的开头)

4.常用到的类

  • Pattern: 正则表达式的编译表示。
  • Matcher: 正则表达式的匹配表示。
4.1. Pattern类 ^mk-20240904110906

Pattern类表示正则表达式模式,并提供了一系列静态方法来编译和处理正则表达式。

  • 编译正则表达式: 使用Pattern.compile()方法来编译正则表达式字符串,并返回一个Pattern对象。
    1
    Pattern pattern = Pattern.compile("ab+c");// 参数是你要匹配的正则表达式字符串。
4.2. Matcher类

Matcher类用于对字符串进行匹配操作,并提供了一系列方法来执行匹配和检索操作。

  • 创建Matcher对象: 使用Pattern.matcher()方法将要匹配的字符串与Pattern对象相关联,创建一个Matcher对象。

    1
    Matcher matcher = pattern.matcher("abbbbbbcsa");// 参数是你要匹配的字符串。
  • 匹配操作:

    • matches():
      • 作用: matches() 方法尝试将整个输入序列与指定的正则表达式进行匹配。
      • 返回值: 该方法返回一个布尔值 (truefalse)。如果整个输入序列与模式完全匹配,则返回 true;否则返回 false
      • 示例:
      1
      2
      3
      4
      5
      String input = "12345";
      Pattern pattern = Pattern.compile("\\d+");
      Matcher matcher = pattern.matcher(input);
      boolean isMatch = matcher.matches(); // isMatch = true
      //在这个例子中,matches() 方法会返回 true,因为整个字符串 "12345" 都是由数字组成,完全匹配模式 \\d+。
    • find(): 尝试查找输入序列中与模式匹配的子序列。
      - 作用: find() 方法尝试在输入序列中查找与模式匹配的子序列。与 matches() 不同,它不要求整个输入序列匹配,而是只需找到一个部分匹配即可。
      - 返回值: 该方法返回一个布尔值 (truefalse)。如果找到了匹配的子序列,则返回 true,否则返回 false
      - 多次调用: find() 可以多次调用,每次调用会继续查找下一个匹配的子序列,直到返回 false 为止。
      - 示例:
      1
      2
      3
      4
      5
      6
      7
      String input = "a12b34c";
      Pattern pattern = Pattern.compile("\\d+");
      Matcher matcher = pattern.matcher(input);
      while (matcher.find()) {
      System.out.println(matcher.group()); // 输出 "12" 和 "34"
      }
      在这个例子中,find() 方法找到两个匹配的子序列 "12""34"
    • group(): 返回当前匹配的子字符串。 ^mk-20240904000921
      - 作用: group() 方法用于返回当前匹配的子字符串。这通常是在 matches()find() 方法之后调用的,用于提取实际匹配的内容。
      - 返回值: 返回当前匹配的子字符串。
      - 注意: group() 方法依赖于之前的匹配操作(如 find()matches())。如果没有进行成功的匹配,调用 group() 会抛出 IllegalStateException
      - 示例:
      1
      2
      3
      4
      5
      6
      7
      String input = "hello123";
      Pattern pattern = Pattern.compile("(\\d+)");
      Matcher matcher = pattern.matcher(input);
      if (matcher.find()) {
      System.out.println(matcher.group()); // 输出 "123"
      }
      在这个例子中,find() 找到了子字符串 "123",而 group() 返回了该匹配的子字符串
  • 检索操作:

    • start(): 返回当前匹配的子字符串的起始索引。
    • end(): 返回当前匹配的子字符串的结束索引后一位。
      • 字符串长度= .end ( ) - .start ( )
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      String text = "The quick brown fox jumps over the lazy dog.";
      // 匹配单词中的元音字母
      Pattern pattern = Pattern.compile("the");
      Matcher matcher = pattern.matcher(text);

      // 输出匹配的子字符串的起始和结束索引
      while (matcher.find()) {
      System.out.println("Start index: " + matcher.start()); //输出: 31 -> 't'
      System.out.println("End index: " + matcher.end());// 输出: 34 -> ' '
      }
  • 替换操作:

    • replaceAll(String replacement): 将匹配的子字符串替换为指定的字符串, 并返回新的字符串。
    • replaceFirst(String replacement): 将匹配的第一个子字符串替换为指定的字符串,并返回新的字符串。
    • appendReplacement(StringBuffer sb, String replacement): 将匹配的子字符串替换为指定的字符串,并将替换后的字符串添加到 StringBufferStringBuilder 对象中,通常与 find( ) 方法一起使用。每次调用 appendReplacement() 时,Matcher 会找到下一个匹配的子字符串,将其替换为 replacement,并把从上一次匹配结束位置到这次匹配开始之间的内容(未匹配部分)以及替换后的内容追加到 StringBuffer sb 中。

      它允许你逐步地控制替换的过程,并且可以在多次匹配和替换之间动态修改 replacement 的内容

    • appendTail(StringBuffer sb): 当所有匹配和替换都完成后,appendTail() 会将输入字符串中最后一次匹配之后的所有剩余部分追加到 StringBufferStringBuilder 对象中,完成整个字符串的构建。 ^mk-20240904000942

      这个方法通常与 appendReplacement() 一起使用,用于完成整个替换过程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // replaceAll(String replacement)
    String input = "abc123xyz456";
    Pattern pattern = Pattern.compile("\\d+");
    Matcher matcher = pattern.matcher(input);
    String result = matcher.replaceAll("#");
    System.out.println(result); // 输出 "abc#xyz#"

    // replaceFirst(String replacement)
    String input = "abc123xyz456";
    Pattern pattern = Pattern.compile("\\d+");
    Matcher matcher = pattern.matcher(input);
    String result = matcher.replaceFirst("#");
    System.out.println(result); // 输出 "abc#xyz456"

    // appendReplacement(StringBuffer sb, String replacement)
    // appendTail(StringBuffer sb)
    String input = "abc123xyz456def";
    Pattern pattern = Pattern.compile("\\d+");
    Matcher matcher = pattern.matcher(input);
    StringBuffer sb = new StringBuffer();

    while (matcher.find()) {
    matcher.appendReplacement(sb, "#");
    // 此时 sb 的内容依次变为:
    // 第一次匹配 "123": "abc#"
    // 第二次匹配 "456": "abc#xyz#"
    }

    // 所有匹配处理完后,使用 appendTail 将剩余部分追加到 sb 中
    matcher.appendTail(sb);

    // 最终的 sb.toString() 是 "abc#xyz#def"
    System.out.println(sb.toString());
4.3. 正则表达式标志

正则表达式编译时可以使用一些标志来修改匹配的行为。这些标志通常作为第二个参数传递给Pattern.compile()方法。

  • 忽略大小写(CASE_INSENSITIVE / (?i)): 使匹配不区分大小写。

    1
    2
    3
    Pattern pattern = Pattern.compile("cat", Pattern.CASE_INSENSITIVE);
    // 或者
    Pattern pattern = Pattern.compile("(?i)cat");
  • 多行模式(MULTILINE / (?m)): 使^$匹配每行的开始和结束,而不是整个输入序列的开始和结束。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Pattern pattern = Pattern.compile("^dog", Pattern.MULTILINE);
    // 或者
    Pattern pattern = Pattern.compile("(?m)^dog");

    // 示例:
    String text = "dog\ncat\ndog";
    Pattern patternDefault = Pattern.compile("^dog"); // 默认模式下,"^" 只匹配整个文本的开始
    Pattern patternMultiline = Pattern.compile("^dog", Pattern.MULTILINE); // 多行模式下,"^" 匹配每一行的开始

    Matcher matcherDefault = patternDefault.matcher(text);
    Matcher matcherMultiline = patternMultiline.matcher(text);

    System.out.println(matcherDefault.find()); // 输出 true,只匹配第一行 "dog"
    System.out.println(matcherMultiline.find()); // 输出 true,因为匹配了第一行的 "dog"
    System.out.println(matcherMultiline.find()); // 再次输出 true,因为匹配了第三行的 "dog"

    /*
    * 在默认模式下,由于正则表达式中 ^ 的存在,只能调用一次matcherDefault.find(),而调用多次matcherDefault.find()是没有意义的
    * 在多行模式下,find函数的调用并不是说一次只能匹配一行,而是可以跨行匹配直到找到匹配字符串后返回,或者遍历完整个字符串为止
    * */
  • 单行模式(DOTALL / (?s)): 使.匹配包括换行符在内的所有字符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Pattern pattern = Pattern.compile(".*", Pattern.DOTALL);
    // 或者
    Pattern pattern = Pattern.compile("(?s).*");

    // 示例:
    String text = "Hello\nWorld";
    Pattern patternDefault = Pattern.compile(".*"); // 默认模式下,"." 不匹配换行符
    Pattern patternDotAll = Pattern.compile(".*", Pattern.DOTALL); // 单行模式下,"." 可以匹配换行符

    Matcher matcherDefault = patternDefault.matcher(text);
    Matcher matcherDotAll = patternDotAll.matcher(text);

    System.out.println(matcherDefault.matches()); // 输出 false,因为 "." 无法匹配换行符
    System.out.println(matcherDotAll.matches()); // 输出 true,因为 "." 可以匹配换行符

这些是一些常见的标志,但Java支持更多其他标志,可以根据需要使用。标志可以组合使用,例如Pattern.compile("pattern", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE)

5.捕获分组和非捕获分组

5.1. 捕获分组[后续还要继续使用本组的数据]

基本概念:

  • 每组都是由()括起来。
  • 捕获分组:捕获分组会记住匹配的文本,并可以在匹配后引用这些文本。
    分组规则: ^mk-20240904000918
  • 从1开始,连续不间断
  • 以左括号为基准,最左边的是第一组,其次为第二组,以此类推
    使用规则:
  • 正则表达式内部使用: \\组号
  • 正则表达式外部使用: $组号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 内部使用
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexInternalExample {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\b\\w+\\b) \\1");
Matcher matcher = pattern.matcher("hello hello");

if (matcher.matches()) {
System.out.println("Matched: " + matcher.group(0));
} else {
System.out.println("No match found.");
}
}
}
// 外部使用
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexExternalExample {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\b\\w+\\b) \\1");
Matcher matcher = pattern.matcher("hello hello world world");

String result = matcher.replaceAll("$1");
System.out.println("Result: " + result);
}
}

5.2. 非捕获分组[分组后不再使用本组数据]
符号含义例子
(?:regex)匹配regex,获取所有文本,包括regexJava(?:8|11|17)
(?=regex)匹配regex,只获取前面部分文本,不包括regexJava(?=8|11|17)
(?!regex)获取不匹配regex的文本的前面部分,不包括regexJava(?!8|11|17)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";

//?: 匹配`regex`,获取所有文本,包括`regex`
String regex2 = "Java(?:8|11|17)";//Java8 Java11 Java17 Java17
//?= 匹配`regex`,只获取前面部分文本,不包括`regex`
String regex1 = "Java(?=8|11|17)";//Java Java Java Java
//?! 获取不匹配`regex`的文本的前面部分,不包括`regex`
String regex3 = "Java(?!8|11|17)";//Java

Pattern p = Pattern.compile(regex1);

Matcher matcher = p.matcher(str);

while (matcher.find()){
System.out.print(matcher.group()+" ");
}
  • Title: Java正则表达式
  • Author: Lu
  • Created at : 2024-03-24 16:07:22
  • Updated at : 2025-03-11 12:44:58
  • Link: https://lusy.ink/2024/03/24/java正则表达式/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments