# index

## 正则基础匹配：

> **\d** 是`[0-9]`。表示是一位数字。
>
> **\D**就是`[^0-9]`。表示除数字外的任意字符。
>
> **\w**就是`[0-9a-zA-Z_]`。表示数字、大小写字母和下划线。记忆方式：w是word的简写，也称单词字符。
>
> **\W**是`[^0-9a-zA-Z_]`。非单词字符。
>
> **\s**是`[ \t\v\n\r\f]`。表示空白符，包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。
>
> **\S**是`[^ \t\v\n\r\f]`。 非空白符。
>
> **.** 就是`[^\n\r\u2028\u2029]`。通配符，表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式：想想省略号...中的每个点，都可以理解成占位符，表示任何类似的东西。

### 量词

#### {m, n}

> **{m,}** 表示至少出现m次。
>
> **{m}** 等价于`{m,m}`，表示出现m次。
>
> **?** 等价于`{0,1}`，表示出现或者不出现。记忆方式：问号的意思表示，有吗？
>
> **+** 等价于`{1,}`，表示出现至少一次。记忆方式：加号是追加的意思，得先有一个，然后才考虑追加。
>
> \* 等价于`{0,}`，表示出现任意次，有可能不出现。记忆方式：看看天上的星星，可能一颗没有，可能零散有几颗，可能数也数不过来。

* {1, 5}

  表示重复，1-5个。比如/\d{1,5}/ , 这个会匹配1到5个数字
* ?

  这个是惰性匹配，相当于{0,1}，表示有没有出现。/\d{1,5}?/。这个就是表示匹配数字，出现一个也可以。

## 正则匹配位置

### 位置

#### `^`和`$`

`^`（脱字符）匹配开头，在多行匹配中匹配行开头。

`$`（美元符号）匹配结尾，在多行匹配中匹配行结尾。

看下面一个匹配开头或者结尾的例子：

```javascript
var result = "hello".replace(/^|$/g, '#');
console.log(result);  //"#hello#"
```

如果是多行的情况，那也比较有意思：

```javascript
var result = "I\nlove\njavascript".replace(/^|$/gm, '#');
console.log(result);
/*
#I#
#love#
#javascript#
*/
```

#### `\b`和`\B`

`\b`是单词边界，具体就是`\w`和`\W`之间的位置，也包括`\w`和`^`之间的位置，也包括`\w`和`$`之间的位置。

看这个例子：

```javascript
var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#');
console.log(result); 
// => "[#JS#] #Lesson_01#.#mp4#"
```

为什么会这样？我们知道，`\w`是字符组`[0-9a-zA-Z_]`的简写形式，即`\w`是字母数字或者下划线的中任何一个字符。而`\W`是排除字符组`[^0-9a-zA-Z_]`的简写形式，即`\W`是`\w`以外的任何一个字符。

`\B`就是`\b`的反面的意思，非单词边界。例如在字符串中所有位置中，扣掉`\b`，剩下的都是`\B`的。

```javascript
var result = "[JS] Lesson_01.mp4".replace(/\B/g, '#');
console.log(result); 
// => "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"
```

#### `(?=p)`和`(?!p)`

`(?=p)`，其中`p`是一个子模式，即`p`前面的位置。就是表示前面的位置。

比如`(?=l)`，表示'l'字符前面的位置，例如：

```javascript
var result = "hello".replace(/(?=l)/g, '#');
console.log(result); 
// => "he#l#lo"
```

而`(?!p)`就是`(?=p)`的反面意思，比如：

```javascript
var result = "hello".replace(/(?!l)/g, '#');

console.log(result); 
// => "#h#ell#o#"
```

二者的学名分别是`positive lookahead`和`negative lookahead`。

中文翻译分别是正向先行断言和负向先行断言。

### 位置的特性

对于位置的理解，我们可以理解成空字符""。

比如"hello"字符串等价于如下的形式：

```javascript
"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "o" + "";
```

也等价于：

```javascript
"hello" == "" + "" + "hello"
```

因此，把`/^hello$/`写成`/^^hello$$$/`，是没有任何问题的：

```javascript
var result = /^^hello$$$/.test("hello");
console.log(result); 
// => true
```

### 位置的一些案例

* 数字千分位分割

  比如把"12345678"，变成"12,345,678"。

  这个可以使用`(?=\d{3}$)`。

  ```javascript
  var result = "12345678".replace(/(?=\d{3}$)/g, ',')
  console.log(result); // "12345,678"
  ```

  但是这个只有一个逗号，咋搞？使用量词`+`。

  ```javascript
  var result = "12345678".replace(/(?=(\d{3})+$)/g, ',')
  console.log(result); 
  // => "12,345,678"
  ```

  但是匹配12345？6789呢？你试试，会发现有问题，开头处也匹配了。

  如何不匹配开头？匹配开头是`^`，不匹配开头就是`(?!^)`。

  来看看：

  ```javascript
  var string1 = "12345678",
  string2 = "123456789";
  reg = /(?!^)(?=(\d{3})+$)/g;

  var result = string1.replace(reg, ',')
  console.log(result); 
  // => "12,345,678"

  result = string2.replace(reg, ',');
  console.log(result); 
  // => "123,456,789"
  ```
* 如果要把"12345678 123456789"替换成"12,345,678 123,456,789"。

  此时我们需要修改正则，把里面的开头`^`和结尾`$`，替换成`\b`：

  ```javascript
  var string = "12345678 123456789",
  reg = /(?!\b)(?=(\d{3})+\b)/g;

  var result = string.replace(reg, ',')
  console.log(result); 
  // => "12,345,678 123,456,789"
  ```

  其中`(?!\b)`怎么理解呢？

  要求当前是一个位置，但不是`\b`前面的位置，其实`(?!\b)`说的就是`\B`。

  因此最终正则变成了：`/\B(?=(\d{3})+\b)/g`。
* 验证密码

  密码长度6-12位，由数字、小写字符和大写字母组成，但必须至少包括2种字符。

  不考虑“但必须至少包括2种字符”这一条件。我们可以容易写出：

  ```javascript
  var reg = /^[0-9A-Za-z]{6,12}$/;
  ```

  假设，要求的必须包含数字，怎么办？此时我们可以使用`(?=.*[0-9])`来做。

  `(?=.*[0-9])`表示任意字符前面包含数字(任意字符也表示空字符，开头处可以认为是一个空字符，比如上面的`"hello" = "" + "" + "hello"`)。

  所以这个正则可以那么写：

  ```javascript
  var reg = /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/;
  ```

  那么同时包含两种呢？

  ```javascript
  var reg = /(?=.*[0-9])(?=.*[a-z])^[0-9A-Za-z]{6,12}$/;
  ```

  所以最终的是这样的结果：

  ```javascript
  var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/;
  console.log( reg.test("1234567") ); // false 全是数字
  console.log( reg.test("abcdef") ); // false 全是小写字母
  console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
  console.log( reg.test("ab23C") ); // false 不足6位
  console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
  console.log( reg.test("abcdEF234") ); // true 三者都有
  ```

  * 换种解法

    “至少包含两种字符”的意思就是说，不能全部都是数字，也不能全部都是小写字母，也不能全部都是大写字母。

    那么要求“不能全部都是数字”，怎么做呢？`(?!p)`出马！对应下面的正则：

    ```javascript
    var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;
    ```

    三种都不能呢？

    ```javascript
    var reg = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/;
    console.log( reg.test("1234567") ); // false 全是数字
    console.log( reg.test("abcdef") ); // false 全是小写字母
    console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
    console.log( reg.test("ab23C") ); // false 不足6位
    console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
    console.log( reg.test("abcdEF234") ); // true 三者都有
    ```

> 本文摘于：<https://juejin.im/post/5965943ff265da6c30653879>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaohesong.gitbook.io/today-i-learn/front-end/regex/index.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
