# 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>
