--- title: 正则表达式重新入门(1) date: 2018-12-10 16:34:42 categories: - JavaScript tags: - 正则表达式 --- ### RegExp对象 在JavaScript当中, `RegExp`对象表示正则表达式 创建RegExp对象有两种方式 1. 常量 : `let reg = /hello\n/g` > 后面的g是修饰符, 表示全局匹配; 这里的 **/** 是表示正则表达式的边界, 不属于内容 2. 构造函数 : `let reg = new RegExp('hello\\n', 'g')` > 修饰符是作为第二个参数传入的, 第一个参数是正则内容的**字符串**, 所以对于反斜杠需要转义 ### 正则表达式的构成 正则表达式由两种基本字符类型构成 + 原义字符 : 表示这个字符的原本意思 + 元字符 : 在正则表达式当中具备特殊含义, 如果要让元字符表达其本身含义(也就是作为原义字符使用), 需要加 **\\** 进行转义 #### 各种元字符 + `$` 匹配输入字符串的结尾位置。如果设置了RegExp对象的Multiline属性,则也匹配 'n' 或 'r'。 + `()` 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。 + `*` 匹配前面的子表达式零次或多次。 + `+` 匹配前面的子表达式一次或多次。 + `.` 匹配除换行符 \n 之外的任何单字符。 + `[]` 标记一个中括号表达式的开始和结束位置, 常用于表示范围。 + `{}` 标记限定符表达式的开始。 + `|` 指明两项之间的一个选择("或者"的含义)。 + `?` 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。 + `\` 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。 + `^` 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合("非"的含义)。 ### 边界 |字符|含义| |----|----| |^|以指定的字符开始| |$|以指定的字符结束| |\b|单词边界| |\B|非单词边界| 例如 ```javascript // eg.1 以AB或者AC开头 let reg1 = /^AB|^AC/ 'ABCDE'.replace(reg1, '123') // 123CDE 'ACEF'.replace(reg1, '123') // 123EF // eg.2 以X结尾 let reg2 = /X$/ 'ABCX'.replace(reg2, '0') //ABC0 // eg.3 is作为一个独立的单词, 且全局匹配 let reg3 = /\bis\b/g 'this is me is it'.replace(reg3, 'ok') // this ok me ok it // 如果没有全局匹配, 则只会替换第一个 // 如果没有单词边界匹配, 那么this当中的is也会被替换 ``` 虽然说单词边界找的是空白字符, 也就是\s 但是上面的eg.3却不能用\s, 会导致单词之间的空格也被替换掉 ### 范围类 如果要匹配某个范围的其中一个字符 那么可以使用`[]` 例如 ```javascript // eg.1 穷举的范围 let reg1 = /[abc]/g 'apbicy'.replace(reg, 'x') // xpxixy // eg.2 包含- ,按照编码的范围 let reg2 = /[0-9]/g 'a1b2c3d5'.replace(reg, '0') // a0b0c0d0 ``` ### 预定义类 其实就是一些预定义的范围类 为了使正则书写更简洁 + `\cX` 匹配由x指明的控制字符。例如, cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 \c 视为一个原义的 'c' 字符。 + `\f` 匹配一个换页符。等价于 x0c 和 cL。 + `\n` 匹配一个换行符。等价于 x0a 和 cJ。 + `\r` 匹配一个回车符。等价于 x0d 和 cM。 + `\t` 匹配一个制表符。等价于 x09 和 cI。 + `\v` 匹配一个垂直制表符。等价于 x0b 和 cK + `\s` 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。 + `\S` 匹配任何非空白字符。 |字符|等价类|含义| |---|-----|----| |.|[^\r\n]|除了回车和换行符之外的所有字符| |\d|[0-9]|数字字符| |\D|[^0-9]|非数字字符| |\s|[\t\n\x0B\f\r]|空白符| |\S|[^\t\n\x0B\f\r]|非空白符| |\w|[a-zA-Z0-9_]|单词字符(大小写字母 数字 下划线)| |\W|[^a-zA-Z0-9_]|非单词字符| ### 量词 用于指定某个组合出现的数量 |字符|含义| |---|---| |?|0次或1次| |+|1次或多次| |*|任意次数| |{n}|出现n次| |{n,m}|出现n到m次(包含n与m)| |{n,}|至少出现n次| 例如 ```javascript // 以1个或多个数字结尾 let reg = /\d+$/ 'abc123'.replace(reg, 'xx') //'abcxx' ``` ### 贪婪模式 正则针对字符串从左向右进行解析, 每个部分都会尽可能多地去匹配字符, 这称之为`贪婪模式` 例如 ```javascript let reg = /^\d{2,5}/ '123456'.replace(reg, 'xx') // xx6 ``` 按照这个正则的规则, 匹配2到5个数字都是可以的 但是它实际执行的时候是按照最多的方式来匹配的, 也就是默认的匹配模式是贪婪模式 如果要使用非贪婪模式, 需要在量词后面加上`?` ```javascript let reg = /^\d{2,5}?/ '123456'.replace(reg, 'xx') // xx3456 ``` ### 分支条件 使用`|`来代表分支条件, 也就是匹配前后的任何一个规则即可 比如 ![分支条件](/images/JavaScript/regex/分支条件.png) 表示字符串整体包含10个数字或者20个数字, 并且最后有2个字母 > 注意 : 这个 | 分割的是左右两边所有部分,而不是仅仅连着这个符号的左右两部分 所以需要加括号, 否则会一直涵盖到表达式的边界或者遇到下一个 | ### 断言 如果字符串中存在2个数字后面跟着2个字母, 就把这2个数字替换掉 如果只是这样写\d{2}[a-zA-Z]{2} 那么后面跟着的2个字母也会被替换掉 要实现这种需求, 就必须用到`断言` 由于正则是将字符串从左到右进行解析, 左侧可以称之为**头部**, 右侧可以称之为**尾部** 从头部向尾部移动可以称之为向前走 判断前面是什么可以称之为`前瞻` > JavaScript只支持前瞻, 而不能去判断后面是什么(后顾) 对于上面的需求, 就可以利用断言来做了 ```javascript let reg = /\d{2}(?=[a-zA-Z]{2})/ '@12ab'.replace(reg, '**') // @**ab ``` 语法规则 |名称|正则语法|含义| |--|--|--| |零宽正向前瞻|exp(?=assert)|我前面是符合assert的| |零宽负向前瞻|exp(?!assert)|我前面的是不符合assert的| |零宽正向后顾|exp(?<=assert)|我后面是符合assert的| |零宽负向后顾|exp(? 要让后一个数字和前一个数字不同 就必须要引用前一个数字, 那么只能对分组进行引用 同时配合零宽负向前瞻 ```javascript let reg = /(\d{1})(?!\1)\d{1}/ reg.test('a32') // true reg.test('a55') // false ``` + `(\d{1})`这里代表的是匹配一个数字, 这个简单 + `(?!\1)`这里代表的是后面的字符与第一分组不同(但是不占用字符) + `\d{1}`这里代表后面还是一个数字 所以就可以达到匹配两个连续数字, 并且这两个数字不同了