---
url: /doc/Java/gj7n91ao/index.md
---
Regular expression 简称：RegExp

> 正则表达式：对字符串执行模式匹配的技术。一个正则表达式，就是用某种模式去匹配字符串的一个公式。除 Java 外，还有许多语言支持正则表达式。

```java
String content = "HeruinKCoin";				//对象文本
String regular = "[A-Z]";					//[1] 创建规则
Pattern pattern = Pattern.compile(regular);	//[2] 创建模式对象
Matcher matcher = pattern.matcher(content);	//[3] 创建匹配器
while (matcher.find()){						//[4] find() 是否找到下一个
    System.out.println(matcher.group(0));	//[5] group(0) 输出找到的当前对象
}
```

> ![image-20231110180206653](https://gitee.com/kilomi/pic-bed/raw/master/img/202311101802722.png)在这里 matcher.group(0),返回的是 0 到 1 这个两个索引表示的字符串。
>
> ![image-20231110175724388](https://gitee.com/kilomi/pic-bed/raw/master/img/202311101757453.png)
>
> `Matcher` 底层维护了一个 `group[]` 数组。如果 **\[4]** 在文本里匹配到对象，会在 `group[0]` 记载该起始位置 n1，在 `group[1]` 记录该结束位置的下一位 n2。即 \[n1,n2) 为匹配的字符串，n2 位置是下次匹配的起始位置。
>
> 当 **\[1]** 创建的规则包含分组（如 `String regular = "(\\d\\d)(\\d\\d)";`），则第一组的起止位置记录在 `group[2]`、`group[3]`，第二组在 `group[4]`、`group[5]`。以此类推。这时，**\[5]** 的 `group(0)` 代表输出全部，`group[1]` 代表输出第一组，group\[2]表示输出第二组，以此类推。

## 25.1 语法

**元字符**

* 限定符
* 选择匹配符
* 分组组合和反向引用符
* 特殊字符
* 字符匹配符
* 定位符

### 25.1.1 转义符号 `\`

使用正则表达式去检索某些特殊字符时，需要加上转义符号（如：`(` 需要写成 `\(`）

在 Java 的正则表达式中，`\\` 代表一个 `\`。

\==需要用到转义符号的字符有==：`.`、`+`、`(`、`)`、`$`、`/`、`\`、`?`、`[`、`]`、`^`、`{`、`}`

### 25.1.2 字符匹配符

| 符号  | 含义                                                         | 示例         | 解释                                                                                                                           |
| ----- | ------------------------------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `[ ]` | 可接收的字符列表                                             | `[abcd]`     | abcd 中的任一字符                                                                                                              |
| `[^]` | 不接收的字符列表                                             | `[^abcd]`    | 非 abcd 的任意字符                                                                                                             |
| `-`   | 连字符                                                       | `[a-z]`      | a - z 中的任意字符                                                                                                             |
| `.`   | 除了斜杆 n、斜杆 r、斜杆 r 和斜杆 n，都匹配；                | `a..b`       | a 开头，b 结尾，中间含 2 字符                                                                                                  |
| `\d`  | 匹配单个数字字符，相当于\[0-9]                                | `\d{3}(\d)?` | 包含 3 个数字，或 4 个数字的字符串，`（\d）?`要注意，这个很有用,表示前面的条件加这个条件，但是这个条件只是可能存在第四个数字。 |
| `\D`  | 匹配单个非数字字符，相当于`[^0-9]`                           | `\D(\d)*`    | 单个非数字字符开头，后接任意个数字字符                                                                                         |
| `\w`  | 匹配单个数字、==下划线==、大小写字母字符，相当于\[0-9a-zA-Z\_] | `\w{2}\d{3}` | 2 个数字字母字符开头(任意组合，全为数字，全为字符或者都有)，后接 3 个数字字符                                                  |
| `\W`  | 匹配单个非数字、非大小写字母字符,相当于`[^0-9a-zA-Z_]`       | `\W+\d{2}`   | 以至少 1 个非数字字母字符开头，后接 2 个数字字符                                                                               |
| `\s`  | 匹配空白字符（空格、制表位等）                               |              |                                                                                                                                |
| `\S`  | 匹配非空白字符                                               |              |                                                                                                                                |

* 关于 `.`：特别地，出现 `[.]` 的场合，那个小圆点依然表示小圆点。`[?]` 同理，表示问号

* \==正则表达式默认区分大小写。要不区分大小写，就加上 `(?i)`==

  * `(?i)abc`：即 abc 都不区分大小写

  * `a(?i)bc`：即仅 bc 不区分大小写

  * `a((?i)b)c`：即仅 b 不区分大小写

  * 创建模式对象时，若如此做：

    ```java
    Pattern pattern = Pattern.compile(regular, Pattern.CASE_INSENSITIVE);
    ```

这个场合，也能不区分大小写。

### 25.1.3 选择匹配符 `|`

……我的感想是，和 Java 的逻辑或 `|` 一样！

### 25.1.4 限定符

| 符号    | 含义                                | 示例         | 解释                                                   |
| ------- | ----------------------------------- | ------------ | ------------------------------------------------------ |
| `*`     | 指定字符重复任意次（可以为 0 次）   | `(abc)*`     | 仅包含任意个 abc 字符串的字符串                        |
| `+`     | 指定字符重复至少一次                | `m+(abc)*`   | 以任意个 m 开头，后面可以有 abc 字符串的字符串         |
| `?`     | 指定字符重复最多一次（可以为 0 次） | `m+abc?`     | 以任意个 m 开头，后面可以有最多一个 abc 字符串的字符串 |
| `{n}`   | n 个匹配                            | `[abc]{3}`   | 长度为 3 的 abc 中的任意字符的组合                     |
| `{n,}`  | 至少 n 个匹配                       | `[abc]{3,}`  | 长度不小于 3 的 abc 中的任意字符的组合                 |
| `{n,m}` | n 到 m 个匹配                       | `[abc]{3,5}` | 长度介于 3 到 5 之间的 abc 中的任意字符的组合          |

* Java 的匹配模式默认是==贪婪匹配==。即：`aaaaa` 匹配 `a{3,5}` 的场合，会匹配到 `aaaaa`

  \==希望实现非贪婪匹配，可以添加额外的 `?`。如：`*?`、`+?`、`??` 代表各自规则的非贪婪匹配==

  注意，除非语句要求限定长度，否则语法都会尽量长的返回字符串

### ==25.1.5 定位符==

| 符号 | 含义                   | 示例            | 解释                                                                                                                                             |
| ---- | ---------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `^`  | 指定起始字符           | `^[0-9]+[a-z]*` | 至少一个数字开头，后接任意小写字母字符串，如果是 123abc12 可以匹配到‘123abc’，如果是 ab123abc 则会匹配不到，因为开头必须为数字。结尾没有做要求。 |
| `$`  | 指定结束字符           | `^[0-9][a]$`    | 一个数字开头，一个 a 结尾,结尾也必须是 a，否则匹配不到。                                                                                         |
| `\b` | 匹配目标字符串的边界   | `K\.C\b`        | 匹配边界的 K.C                                                                                                                                   |
| `\B` | 匹配目标字符串的非边界 | `K\.C\B`        | 匹配非边界的 K.C                                                                                                                                 |

* 边界即字符串的末尾，或字符串中空格间隔的子串的末尾。
* 用的最多的是`^`和`$`，他们经常搭配使用。例如要限定一个邮箱格式，则写为`^ [a-zA-Z0-9_-]+@ [a-zA-Z0-9_-]+ (\\. [a-zA-Z0-9_-]+)+$`

### 25.1.6 分组

| 符号                                   | 含义                                                                                                                                      |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `(pattern)`                            | 非命名捕获。捕获匹配的字符串。                                                                                                            |
| `(?<name>pattern)`、`(?'name'pattern)` | 命名捕获，给组取名，==可以在 group 方法中传入这个命名的字符串==来得到相应分组字符串。用于 name 的字符串不能包含标点符号，也不能以数字开头 |

* 编号为 0 的第一个捕获是由整个正则表达式匹配的文本。其他捕获结果根据左括号的顺序从 1 开始自动编号。

### ==25.1.7 非捕获分组==

写法简洁，但是这样就不能 gourp(1)和 gourp(2)访问了。

| 符号          | 含义                                            | 示例               | 解释                                          |
| ------------- | ----------------------------------------------- | ------------------ | --------------------------------------------- | -------------------------------------------- |
| `(?:pattern)` | 匹配 pattern ==但不捕获该匹配的子表达式==       | `industr(?:y       | ies)`                                         | 和 industry|industries 一样，写法更加简洁。 |
| `(?=pattern)` | 匹配处于 pattern 前的搜索字符串。非捕获分组。   | `Windows(?=7\|10） | 只得到 windows 7 或 windows 10 前的 Windows   |
| `(?!pattern)`| 匹配不处于 pattern 前的搜索字符串。非捕获分组。 |`Windows(?!7|10） | 得到除了 windows7 或 windows10 之前的 windows |

## 25.2 常用类

* `Pattern` 类：

  `Pattern` 对象是一个正则表达式对象，该类没有公共构造方法。

  用 `Pattern.compile(reg)` 获取一个 `Pattern` 对象。

* `Matcher` 类：

  `Matcher` 对象是输入字符串进行解释和匹配的引擎，也没有公共构造方法。

  用 `Pattern` 对象的 `matcher(content)` 方法获得一个 `Matcher` 对象。

* `PatternSyntaxExcption` 类：

  `PatternSyntaxExcption` 是一个非强制异常类，表示一个正则表达式中的语法错误。

### 25.2.1 `Pattern` 类常用方法

* `Pattern.matches(reg, content)`：==整体匹配，==输入的字符串是否符合表达式。返回布尔值。

  `matcher.matches()`：整体匹配，字符串是否符合表达式。返回布尔值。前面的方法实际上就是这个方法。

* `Pattern.compile(reg)`：返回一个指定表达式的 `Pattern` 对象

* `pattern.matcher(content)`：返回一个字串的 `Matcher` 对象

* `matcher.pattern()`：返回该 `Matcher` 对象的表达式

  `pattern.pattern()`：返回该 `Pattern` 对象的表达式

* `matcher.find()`：尝试查找下一个匹配的序列，返回布尔值

  `matcher.find(int)`：重置该匹配器，从指定索引位置开始重新查找

* `matcher.start()`：返回本次匹配的字符起始位置的索引

  `matcher.end()`：返回本次匹配的字符结束位置 + 1 的索引

  \==这个场合，`content.substring(matcher.start(), matcher.end())` 就是匹配的字符串==

* `matcher.start(int)`：返回本次匹配的字符的该组内容的起始位置的索引

  `matcher.end(int)`：返回本次匹配的字符的该组内容的结束位置 + 1 的索引

* \==`matcher.replaceAll(str)`：替换匹配到的全部内容,返回替换的字符，原来的字符不变化，可以通过用原字符串引用接受这个返回值来变相”改变“内容（字符串是不可以修改的，只是原来的引用指向了新的字符串对象）==

  `matcher.replaceFirst(str)`：替换第一次匹配到的内容

  这些场合，返回的字符串才是替换后的字符串。原字符串不变。

## 25.3 分组、捕获、==反向引用==

* 分组（子表达式）

* 捕获：把正则表达式中，子表达式（分组）的内容保存到内存中以数字编号或显式命名的组里，方便后面引用。以分组的左括号为标志，第一组组号为 1，第二组为 2，以 0 代表整个正则表达式。

* 反向引用：分组的内容被捕获后，可以在这个括号后使用。这种引用既可以是在正则表达式内部，也可以在外部。内部反向引用 `\分组号`、外部反向引用 `$分组号`

  ```java
  String regular = "(\\w)\\1+";			//即，重复的字母或数字
  Matcher matcher = Pattern.compile(regular).mathcer(content);
  content = matcher.replaceAll("$1");		//这样，就完成了去重
  ```

  ```java
  //前五位数字-后面9为每三位数字相同。
  String content ="12345-111222333";
  String regular = "[0-9]{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
  Pattern pattern = Pattern.compile(regular);
  Matcher matcher  = pattern.matcher(content);

  while (matcher.find()){
      System.out.println(matcher.group(0));
  }
  ```

## ==25.4 在 `String` 中使用正则表达式==

* `str.matches(reg)`：==整体匹配==
* \==`str.replaceAll(reg, reg)`：替换匹配到的全部内容==
* \==`str.split(reg)`：分割内容==

# 应用实例

## 1.验证汉字

```java
String content = "你好";
String content2 = "你hello好";
String regular = "^[\u0391-\uffe5]+$";	//这个范围是汉字的范围
Pattern pattern = Pattern.compile(regular);
Matcher matcher = pattern.matcher(content);

if(matcher.find()){
	System.out.println("满足格式");
}else {
	System.out.println("不满足格式");
}
```

## 2.验证 url

```java
String regular = "^((https|http):\\/\\/)([\\w-]+\\.)+([\\w-])+(\\/[\\w.?%/&=-]*)?$";
```

## 3.结巴程序去重

```java
String content = "我....我要....学学学学....编程java!";
String regular = "[.]+";
Pattern pattern = Pattern.compile(regular);
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
System.out.println(content);

pattern = Pattern.compile("(.)\\1+");
matcher = pattern.matcher(content);

while (matcher.find()) {
    System.out.println(matcher.group(0));
}
String target = matcher.replaceAll("$1");
System.out.println(target);


content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println(content);
```

实际上可以用 String 来做，这样就可以去重了。

```java
 content.replaceAll("(.)\\1+","$1");
```
