RegExp重学与整理

什么是正则表达式

Regular Expression 使用单个字符串来描述、匹配一系列符合某个句法规则的字符串
按照某一种规则去匹配符合条件的字符串

修饰符

g

名称:global

说明:全文搜索,不添加的话,搜索到第一个匹配停止

i

名称:ignore case

说明:忽略大小写,默认大小写敏感

m

名称:multiple linse

说明:执行多行匹配

元字符

  1. 正则表达式由两种基本字符类形成:
    原义文本字符
    元字符
  2. 元字符是在正则表达式中有特殊含义的非字母字符
* + ? $ ^ . | \ / : = ( ) { } [ ] 
字符 含义
\t 水平制表符
\v 垂直制表符
\n 换行符
\r 回车符
\o 空字符
\f 换页符
\cX 与X对应的控制字符(Ctrl+X)
\xnn 由十六进制数nn指定的拉丁字符,列如:\x0A等价于\n
\uxxxx 由十六进制数xxxx指定的Unicode 字符,列如\u0009等价于\t

字符类

  • 我们可以使用元字符 [] 来构建一个简单的类
  • 所谓类是指符合某些特性对象,一个泛指,而不是特指某个字符
  • 表达式[abc] 把字符 abc 归为一类,表达式可以匹配这类的字符
  var reg = /[abc]/g
  '1a6c1d6v5d1b5'.replace(reg, 'X')
  // "1X6X1d6v5d1X5"
  // 我们将当前字符串中的abc 替换成了X

字符类取反

  • 使用元字符 ^ 创建反向类/负向类
  • 反向类的意思是不属于某类的内容
  • 表达式 [^abc] 表示,不是字符a 或 b 或 c 的内容
  var reg = /[^abc]/g
  '1a6c1d6v5d1b5'.replace(reg, 'X')
  // "XaXcXXXXXXXbX"
  // 我们将当前字符串中不是abc的所有内容 替换成了X

范围类

  • 我们可以使用 [a-z] 来连接两个字符表达式从a到z的任意字符,这是一个闭区间,也就包含a和z本身。
  var reg = /[a-z]/g
  'a1b2c3d45z3x'.replace(reg, 'Y')
  // "Y1Y2Y3Y45Y3Y"
  // a-z的所有字符替换为Y


  // 范围类也可以连这写
  var reg = /[a-zA-Z]/g
  'QDa1B2c3d45Z3xASDK'.replace(reg, 'Y')
  // "YYY1Y2Y3Y45Y3YYYYY"
  // a-z和A-Z的所有字符替换为Y


  // 如果我们在范围类中,比如说需要匹配这样的 8375-32*29 将-以及小于我的全部替换成Y
  var reg = /[1-5-]/g
  '8375-32*29'.replace(reg, 'Y')
  // "8Y7YYYY*Y9"

字符类

字符 等价类 含义
. [^\r\n] 除了回车和换行符之外的所有字符
\d [0-9] 查找数字
\D [^0-9] 非数字字符
\s [\t\n\x0B\f\r] 空白符
\S [^\t\n\x0B\f\r] 非空白符
\w [a-zA-Z_0-9] 字母 数字 下划线
\W [^a-zA-Z_0-9] 非单词字符
[...] 方括号内的任意字符
[^...] 不在方括号内的任意字符
[\b] 退格直接量(特列)

边界

字符 含义
^ 以xxx开始
$ 以xxx结尾
\b 单词边界
\B 非单词边界
(?=p) 零宽正向先断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符
(?!p) 零宽负正先行断言,要求接下来的字符不与p匹配

量词

量词 含义
匹配前一项n次
匹配前一项至少n次,但不能超过m次
匹配前一项n次或者更多次
+ 出现一次或多次 (最少出现一次)等价于
* 出现零次或多次 (任意次)等价于
? 出现零次或一次(最多出现一次)

贪婪模式

  var reg = /\d{3,6}/g
  '12345678'.replace(reg, 'X')
  // X78 
  // 他匹配了123456 而78没有被匹配
  // 我们这个正则表达式 是匹配3-6次,可以满足3 4 5 6 都可以满足,而这时候正则表达会尽可能多的匹配,直到匹配失败
  // 

非贪婪模式

让正则表达式尽可能少的匹配,一旦成功匹配不再继续尝试就是非贪婪模式。
做法很简单,只要再量词后面加上即可

  var reg = /\d{3,6}?/g
  '12345678'.replace(reg, 'X')
  // XX78
  // 他匹配了 123 456
  // 3 次一组,后面两个没有被匹配掉

分组

使用()可以达到分组的功能,使量词作用与分组

  var reg = /([a-z]\d){3}/g
  'a1b2c3d4'.replace(reg, 'X')
  // Xd4


  // 在使用或的时候,我们同样需要分组
  var reg = /(java|type)script/g
  'javascripttypescript'.replace(reg, 'X')
  // XX

反向引用

  var reg = /(\d{2})-(\d{2})-(\d{4})/g
  '02-13-2020'.replace(reg, '$3/$1/$2')
  // 2020/02/13
  // 他会捕获每一个分组,分别从$1-$n

忽略分组

不希望捕获某些分组,我们只需要再分组内加上?:就可以

  var reg = /(?:\d{2})-(\d{2})-(\d{4})/g
  '02-13-2020'.replace(reg, '$3/$1/$2')
  // $3/13/2020
  // 我们可以看到我们只有$1 和 $2 捕获到了

前瞻

  • 正则表达式从文本头部向尾部开始解析,文本尾部方向,称为: 文本尾部叫做
  • 前瞻就是在正则表达式匹配到的规则的时候,向前检查是否符合断言(断言就是前瞻语法的一部分),后顾/后瞻方向相反。
  • 前瞻比如:当我们匹配到一个人叫张三的时候,我们还需要往前看看他爸爸是否叫张二
  • JavaScript 不支持后顾
  • 符合和不符合特定断言称为肯定/正向匹配和否定/负向匹配
名称 正则 含义
正向前瞻 exp(?=assert)
负向前瞻 exp(?!assert)
正向后顾 exp(?<=assert) JavaScript 不支持
负向后顾 exp(?<!assert) JavaScript 不支持
  // 正向前瞻
  // 匹配单词字符,并且要求后面需要是数字
  var reg = /\w(?=\d)/g
  'a2*3'.replace(reg, 'X')
  // X2*3
  'a2*3dasd2e5'.replace(reg, 'X')
  // X2*3dasX2X5

  // 负向前瞻
  var reg = /\w(?!\d)/g
  'a2*3'.replace(reg, 'X')
  // aX*X
  'a2*3dasd2e5'.replace(reg, 'X')
  // "aX*XXXXdXeX"

Js 正则 对象属性

每一个RegExp对象都包含5个属性。

  • global:是否全文搜索, 默认 false
  • ignore case:是否大小写敏感,默认 false
  • multiline: 多行搜索,默认值是 false
  • lastIndex: 当前匹配结果的 最后一个字符的 下一个字符
  • source:source是一个只读的字符串,包含正则表达式的文本。
var reg1 = /\w/
var reg2 = /\w/gim

reg1.global false
reg1.ignoreCase false
reg1.multiline false
reg1.global = true 这是不起作用的
reg1.global false

reg2.global true
reg2.ingnoreCase true
reg2.multiline true

reg1.source "\w"
reg2.source "\w"

reg1.lastIndex 0
reg2.source 0

Js 正则 方法

test

  • 用于测试字符串参数中是否存在匹配正则表达式模式的字符串
  • 如果存在则返回 true,否则返回 false
var reg1 = /\w/
var reg2 = /\w/g
reg1.test('a') true
reg1.test('|') false
reg1.test(')') false
// 判断当前字符串 是否包含传递的值

当我们加上g的时候 我们多执行几次结果,会反向奇怪的现象
我们来开一个有意思的例子:

reg2.test('ab')  true
console.log(reg2.$1)
reg2.test('ab')  true
reg2.test('ab')  false
reg2.test('a')  true
reg2.test('a')  false
/*
原因:这个其实是lastIndex在作怪
reg2.test('ab') 拆分
第一遍执行 我们找当前匹配结果 a,a所在字符的位置是1,所以lastIndex = 1
第二遍执行 当前匹配结果的最后一个字符 的下一个字符 b,b所在字符位置是2,所以lastIndex = 2
第三遍执行的时候 后面没有可以匹配到的字符了 所以返回为false 并且将 lastIndex1 重置为 0 
所以第四次匹配的时候,他有开始重新查找 返回为true

注意: 每次匹配的时候,他把上一个结果记住了,从下一个开始
*/

解决方法:

  // 每次都实列化一个新的
  (/\w/g).test('a')

exec

  • 使用正则表达式模式对字符串执行搜索,并将更新全局RegExp对象的属性以反映匹配结果。
  • 如果没有匹配到文本返回null,否则返回一个结果数组:
    • index 表示匹配结果第一个字符的位置
    • input 存放被检索的字符串 string
非全局调用
  • 调用非全局的RegExp对象的exec() 时,返回数组
  • 第一个元素是与正则表达式相匹配的文本
  • 第二个元素是与RegExpObject 的第一个子表达式相匹配的文本(如果有的话)
  • 第三个元素与RegExp 对象的第二个子表达式相匹配的文本(如果有的话),以些类推
var reg1 = /\d(\w)\d/;
var reg2 = /\d(\w)\d/g;
var str = '1a2b3c4d5e'
var ret = reg1.exec(str)
console.log(`${reg1.lastIndex} \t ${ret.index} \t ${ret}`)

// lastIndex在全局里面才显示
// index 从第几个字符开始匹配
// [匹配结果, 分组]
// 0 	 0 	 ["1a2", "a"]
console.log(`${reg1.lastIndex} \t ${ret.index} \t ${ret}`)
// 0 	 0 	 1a2,a
while(ret = reg2.exec(str)) {
  console.log(`${reg2.lastIndex} \t ${ret.index} \t ${ret.toString()}`)
  // 3 	 0 	 1a2,a
  // 7 	 4 	 3c4,c
  /** lastIndex
  第一次匹配,1a2 lastIndex 索引记录到 3
  第二次匹配,3c4 lastIndex 索引记录到 7
  */
  /** index
  第一次匹配,当前匹配到的,第0向开始
  第二次匹配,当前匹配到的,第4向开始
  */
}

正则表达式的字符串方法

  • search 方法用于检索字符串中指定的字符串,或检索与正则表达式相匹配的子字符串
  • 方法返回第一个匹配结果 index,查不到返回 -1
  • search 方法不执行全局匹配,它将忽略标志g,并且总是从字符串的开头进行检索
'a1b2c3d4e1'.search('1') 1
'a1b2c3d4e1'.search('10') -1

'a1b2c3d4e1'.search(1) 1
// 为什么 写数字1 也可以呢
// 原因:我们传递的参数不是正则的时候,他就尝试将我们传递的参数转换为正则。

'a1b2c3d4e1'.search(/1/) 1
'a1b2c3d4e1'.search(/1/g) 1 
// 开启全局模式和不开启全局模式,结果都一样

match

  • match 方法将检索字符串,以找到一个或多个与regexp匹配的文本
  • regexp 是否具有标志g 对结果影响很大
非全局调用
  • 如果regexp没有标志g,那么match 方法就只能在字符串中执行一次匹配
  • 如果没有找到任何匹配的文本,将返回null
  • 否则它将返回一个数组,其中存放了与找到的文本匹配文本有关的信息
  • 返回数组的第一个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本
  • 除了常规的数组元素之外,返回的数组还包含有 2个对象属性
    • index 生命匹配文本的起始字符在字符串的位置
    • input 生明对stringObject的引用
var reg1 = /\d(\w)\d/
var str = 'a1b2c3d4e5'
var ret = str.match(reg1)
ret ["1b2", "b"]
ret.index 1
reg1.lastIndex 0
全局调用
  • 如果 regexp 具有标志 g 则 match 方法将执行全局检索,找到字符串中的所有匹配字符串
    • 没有找到任何匹配的字符串,则返回null
    • 如果找到了一个或多个匹配字符串,则返回一个数组
  • 数组元素中存放的是字符串中所有的匹配字符串,而且也没有index属性或input 属性
var reg2 = /\d(\w)\d/g
var str = 'a1b2c3d4e5'
var ret = str.match(reg2)
ret ["1b2", "3d4"]
ret.index undefined
reg2.lastIndex 0

splic

  • 我们经常使用 split 方法把字符串分割为字符数组
'a,b,c,d'.split(','); // ['a', 'b', 'c', 'd']
  • 在一些复杂的分割情况下,我们还可以使用正则表达式解决
'a1b2c3d'.split(/\d/); // ["a", "b", "c", "d"]

说明: split 其实默认也是给我们尝试转成正则表达式 /,/

replace

  • 参数1 string | reg 找谁
  • 参数2 string | function 替换成谁
    • function 会在每次匹配替换的时候调用,有四个参数 (注意:参数不固定)
      1. 匹配字符串
      2. 正则表达式分组内容,没有分组则没有该参数
      3. 匹配项在字符串中的index
      4. 原字符串
'a1cde'.replace('1', 'b') // abcde
'a1cde'.replace(/\d/, 'b') // abcde

'a1b2c3d4'.replace(/\d/g, (match, index, origin)=>{
  return parseInt(match) + 1
}) // a2b3c4d5


'a1b2c3d4'.replace(/(\d)(\w)(\d)/g, (match, group1, group2, group3, index, origin)=>{
  console.log(group1, group2)
  return group1 + group3
}) // a12c34

posted @ 2020-12-02 17:08  渡心°  阅读(108)  评论(0编辑  收藏  举报