有些內容只有在 Java 里面才能使用。 ------原文作者:逗號 (他不高興發博客)
## 字符種類
部分使用頻率幾乎為 0 的這里不會提及(比如 \x 之流)
### 普通
| | 含義 | 助記 |
| ---- | ------------------------------------- | ----- |
| \s | 非空白 | space |
| \s | 空白 | |
| \d | 數字 | digit |
| \D | 非數字 | |
| [] | 自定義字符種類可使用 ^ \| && 隨意組合 | |
### 特別
| | 含義 | 助記 |
| ------- | ------------------------------------------------------------ | ---------- |
| \h | 橫向字符 | horizontal |
| \h | 非橫向字符 | |
| \v | 縱向字符(比如說換行符) | vertical |
| \V | 非縱向字符 | |
| \w | 單詞字符(包括 - _) | word |
| \W | 非單詞字符 | |
| \p{XXX} | POSIX,這個並不常用,在有些時候會方便,Character 有很多 is開頭的方法,將方法名中的`is`替換為`Java`可直接使用進行字符匹配:\p{javaLowerCase} | posix |
| \Q | 字符轉義開始符 | |
| \E | 字符轉義結束符,這兩個符號之間的內容全部按照字面量處理,里面是所有字符都不會有什么含義,比如 . 就是 .,\s 必須匹配\s,而不是匹配空白。 | |
## 量詞
| | 含義 | 助記 |
| ----- | ------------- | ---- |
| ? | 0個或者1個 | |
| * | 0個或多個 | |
| + | 1個或多個 | |
| {m} | m個 | |
| {m,n} | 介於m~n個之間 | |
`?`修飾在量詞之后,表盡可能少的匹配!
## 邊界
### 普通
| | 含義 | 助記 |
| ---- | ------------ | ---- |
| ^ | 字符開始位置 | |
| $ | 字符結束位置 | |
### 特別
| | 含義 | 助記 |
| ---- | ------------------------------ | ---- |
| \b | 單詞邊界,單詞的最前面和最后面 | |
| \B | 非字符串邊界 | |
下面的內容普通人基本上就已經不管了!
## 分組捕獲
正則表達式中,每出現一對括號就是新建了個新的組。比如:`(\\w+)|(\\d+)`
正則表達式的組可以用使用索引進行引用,形式為:`$index`
`$0` 默認指向原來的全部字符
然后索引遞增規律如下:
`((a)|(bc))`
`$1` 指向 `((a)|(bc))` 匹配的內容
`$2` 指向 `(a)` 匹配的內容
`$3` 指向 `(bc)` 匹配的內容
然而當正則復雜之后,改動將會對索引有較大影響,也不方便計算索引,此時,你可以為組制定名稱:
比如匹配字符串:`a=123,b=456`中間的鍵值,使用索引,需要寫成:`(\\w+)=(\\S+)`,然后使用`$1`指向 key,使用 `$2` 指向 value。
我們可以將正則改為:`(?<key>\\w+)=(?<value>\\S+)`,上述表達式使用 `?<name>` 的形式在正則中為組定義名稱。
## 引用分組
在 Java 中,你可以在兩種地方使用引用:
* 正則表達式中
匹配 `xxx-xxx` 字符 `-` 兩邊的內容相同,使用索引引用前面的組(`\\index`):
```regexp
(\\w+)-\\1
```
使用名字引用前面的組:
```regexp
(?<content>\\w+)-\\k<content>
```
* 在 Java 的字符串替換中
Markdown 中的字符串轉為斜線的html:
```java
"*name*".replaceAll("\\*(?<content>\\w+)\\*", "<em>$1</em>")
"*name*".replaceAll("\\*(?<content>\\w+)\\*", "<em>${content}</em>")
```
## 模式
你可以在構造 `Pattern`時設定模式:
```Java
Pattern.compile("abc", Pattern.CASE_INSENSITIVE)
```
模式可以自行到 `Pattern`下查看,這里只說使用方式,在組開頭使用 `?flags:`的方式聲明模式,比如:
匹配單詞,不區分大小寫
```regex
(?i:\\w+)
```
記住`i`(忽略大小寫)、`s`(. 匹配全部字符)即可,其他基本不用。
## 前瞻后顧
前瞻后顧,是正則表達式中的高級技巧,主要有5種:
> 注意:雖然這貨也有括號,但是這貨只算個條件,並不會捕獲任何內容,也不會影響索引
### 前瞻
前瞻中不帶`<`,並且只影響前面的表達式,意思就是偷偷往前看一下(這看一下沒有限制的,可以看到結尾)。
* `exp1(?=exp2)`
尋找后面是 exp2 的 exp1
* `exp1(?!exp2)`
### 后顧
* `(?<=exp1)exp2`
匹配 exp1 后面的 exp2,前面沒有exp1的內容將被忽略,比如,替換`abcba`替換`c`后面的`b`為 `xxx`,需要寫為:
```java
"abcba".replaceAll("(?<=c)b", "xxx")
```
`a`后面的`b`則不受影響。
> 注意:正則表達式`(?<=c)b`不會匹配字符串`cb`,意思就是:
>
> ```java
> assertFalse("cb".matches("(?<=c)b"));
> assertFalse("b".matches("(?<=c)b"));
> assertTrue("cb".matches("c(?<=c)b"));
> ```
* `(?<!exp1)exp2`
后顧中帶有 `<` ,並且只影響后面的表達式,也就是說,你如果寫成這樣:`exp2(?<=exp1)`,是沒什么卵用的。
## 牛刀小試
以下所有內容只能使用`String#repalceAll`或者`String#matches`完成。
### 手機號遮罩
將`11`位手機號除前4位之外,其他的使用`*`替換。
### 格式化數字
例如:`1234567890` 使用正則替換之后,變為 `1,234,567,890`。
### Trim 字符串
使用正則去除字符串開始和結束的空白字符。
### 去除數字結尾的`0`
`1234.4500`應該處理為`1234.45`,`1234.00`應該處理為`1234`。
### 處理文檔
將字符串的`* 測試 * ** 測試 **`的使用單個`*`包圍的內容兩邊分別加上`<h1>`、`</h1>`,不能影響兩個`**`包圍的內容。
### 測試某個字符串是不是合法的Java標識符
### 測試`IPv4`
已知`IPv4`中每一段的數字需要介於0~255,測試某個字符串是否是合法的`IPv4`表達式。
### 去除`//`注釋
已知`Java`中可以使用`//` 進行單行注釋,請去除一段代碼中的這種注釋。
### 去除`/* 內容 */`注釋
已知`Java`中可以使用`/* */` 進行多行注釋,請去除代碼中的這種注釋。