关于javascript:JS系列正则表达式

33次阅读

共计 8921 个字符,预计需要花费 23 分钟才能阅读完成。

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式被用于 RegExpexectest 办法, 以及 StringmatchmatchAllreplacesearchsplit 办法。

正则的用意

正则表达式是 匹配模式 ,要么匹配 字符串 ,要么匹配 地位

正则表达式是 匹配模式 ,要么匹配 字符串 ,要么匹配 地位

正则表达式是 匹配模式 ,要么匹配 字符串 ,要么匹配 地位

正则的创立

  • 形式一:字面量的创立

    const reg = //
  • 形式二:构造函数

    const reg = new RegExp()

补充

  1. 字面量 是罕用的形式。
  2. 字面量创立 构造函数的创立 是存在区别的

    // eg:
    const num = 12.34
    
    // 字面量
    const reg = /\d+.\d+/ 
    
    // 构造函数
    const reg = new RegExp('\d+.\d+')

    reg 的 正则字符串是一样的,然而它们代表一样的意思吗?不是

    先看一段代码:

    console.log('\') // 报错 Javascript: unterminated string literal
    console.log('\d') // d

    在这里 \,在 JS 解析的时候是有非凡意义的。

    所以下面的构造函数形式的创立:

    const reg = new RegExp('\d+.\d+')  // d+.d+  匹配的 d 这个字符
    
    // 须要本义
    const reg = new RegExp('\d+\.\d+')

    这种写法挺麻烦的,所以举荐应用 字面量的写法。

正则的原子

原子是正则表达式的最根本的组成单位,而且在每个模式中起码蕴含一个原子。

原子分为可见原子和不可见原子。

  • 可见原子: Unicode 编码表中键盘输入肉眼可见的字符。
  • 不可见原子: Unicode 编码表中键盘输入肉眼不可见的字符, 如空格,制表符。

通用字符类型:

\d 匹配 0 - 9 任意一个数字[0-9]
\D 除了数字以外的任意一个字符匹配[^0-9]
\w 数字 字母 下划线匹配[a-zA-Z0-9]
\W 除了数字 字母 下划线以外的任意一个字符[^a-zA-Z0-9]
\s 与任意一个空白字符匹配
\S 与任意一个空白字符意外的字符匹配[^\n\f\r\t\v]

\f 换页字符;
\n 换行字符;
\r 回车字符;
\t 制表符;
\v 垂直制表符;

正则的特殊字符

开始(^)和 完结($

对匹配的做开始的限定和完结的限定

const reg = /abc/  // abc aabc abcabc 这些都是匹配胜利
const reg = /^abc$/  // 只能

转义字符(\

\这个无论是在 正则还是在其余的中央都有着非凡的意义。

正则中有跟多的符号是有意义的,如果想要匹配他们都是须要本义的

\ () [] ^ $ * + ? 这些符号都是有着本人各自的意义,上面会顺次介绍

const str = '()[]^$*+?'   // 这是一个字符串
const reg = /\(\)\[\]\^\$\*\+\?/  // 大量的应用本义

或(|

左右两边看成一个整体,满足其中一个就行

const reg = /abc|acc/  // 匹配 abc 或者 acc

点元字符(.

匹配除 \n 之外的任何单个字符。

 let str = `
      james
      kobe
      `
console.log(str.match(/.+/)) // 'james'

. 基本上都匹配了所有。那么匹配全副该怎么写呢?

// 匹配所有
const reg = /[\d\D]+/
const reg = /[\s\S]+/

const reg = /.+/s  // s 模式 

正则的量词(* + ?

{m,n} : 匹配 m 到 n 个字符,都是数字,还能够写成{m,}:m 到有限个

const reg = /d{2,4}/   // 匹配 d 的数量为 2 到 4 个

*: 0 个到无穷个 等价于 {0,}

const reg = /d*/

+: 1 个到无穷个 等价于 {1,}

const reg = /d+/

?: 1 个或者 0 个

const reg = /d?/

留神: * + ? {m,n} 它们都是 贪心模式,上面会介绍。

原子表([]

[]满足其中的一个内容

const reg = /[abc]/  // a b c ab 都满足  

区间匹配

[0-9]   // 匹配 0 - 9 的数字
[a-z]   // 匹配 a - z 的小写字母
[A-Z]   // 匹配 A - Z 的大写字母

取反

[^abc]  // 匹配除了 abc 以外的字符

这里 ^ 尽管跟开始字符是一样的,然而示意的意义不一样。这里的 ^ 是在 []应用。

不解析字符

[.()+]  // 在原子表中,它们都是单纯的字符

原子组(()

() 与 原子表 [] 不同,()会把其中的内容看成一个整体。

const dom = `<h1>copyer</h1>`
const reg = /^<h([1-6])>[\s\S]*</h([1-6])>$/i
// 等价于
const reg1 = /^<h([1-6])>[\s\S]*</\1>$/

([1-6])看成一个整体,就是为了匹配 h1~h6的标签

在第一个正则中,匹配 开始标签 完结标签,就会写两次的([1-6]),看起来是比拟麻烦的

1、组名

组名:就是为了下面反复写的问题。

从左到右,顺次找出 原子组 (),排在第几个,就应用\num 就行了;

比方:([1-6]) 排在原子组的第二个,那么应用 \2 就能够了。

简略记忆:只有判断了是原子组,就数 ( 从左到右排在第几个

2、嵌套组

// 匹配 url
const url = 'http://www.baidu.com'
const reg = /^https?://[\w]+.[\w]+.(com|cn)$/
console.log(url.match(reg))

match 办法 前面也会总结

[
    0:"http://www.baidu.com"
    1: "com"
    groups: undefined
    index: 0
    input: "http://www.baidu.com"
]

数组第一个,就是匹配到的字符串

如果有 原子组 的应用,匹配到的后果,就会顺次放在第一个的前面

3、不记录组

下面不是对com 组提取进去了嘛,如果不想要记录该怎么做呢?

const reg = /^https?://[\w]+.[\w]+.(?:com|cn)$/

在原子组中:应用?: 就不会记录在组中

(?:com|cn) ?:就是放在最开始

4、组的理论应用场景

给一个 dom 节点字符串,拿取外面的内容

let str = '<h1>copyer</h1>'
const reg = /<h([1-6])>(.*)</h([1-6])>/
console.log(str.match(reg)[2])

下面应用了三个原子组:([1-6]) (.*) ([1-6])

这个三个都是 push 到数组中,内容就是第二个原子组,所以,咱们取下标为 2,就能够拿到内容了

正则的贪心模式

在下面,晓得了 正则的量词(* + ? 的根本应用,然而呢,它们还是有一种个性:贪心

何为贪心?

const str = 'aaaaaa'
const reg = /a+/
console.log(str.match(reg))  // aaaaaa

只有在条件的满足范畴内,能匹配到多少就是多少,贪心思维

那么怎么 阻止贪心 呢?

正则量词 前面增加一个?

const str = 'aaaaaa'
const reg = /a+?/
console.log(str.match(reg))  // a

*? +? ?? {m,n}? 相似这种组合

正则的属性lastIndex

lastIndex从字面了解:最初一个索引值。实际上,它的意思是 正则表达式开始下一次查找的索引地位

lastIndex 第一次的时候总是为 0 的,

第一次查找完了的时候会把 lastIndex 的值设为匹配到得字符串的最初一个字符的索引地位加 1

const str = 'aaa_aa_aaaa'
const reg_g = /a+/g
console.log(reg_g.lastIndex) // 0  默认值
console.log(reg_g.exec(str)) // aaa
console.log(reg_g.lastIndex) // 3 上次匹配 aaa 下标值为 2,在下面的一次加 1,所以为 3
console.log(reg_g.exec(str)) // 所以这次匹配是在 下标为 3 的地位开始匹配

如果没有匹配到的话,lastIndex 就会从新置为 0

重要事项:不具备标记 g 和不示意全局模式的 RegExp 对象不能应用 lastIndex 属性。

前面,如同 y 修饰符 也反对了。

lastIndex 是个 可读可写 的属性

正则的修饰符

s修饰符

/s 示意将字符串视为单行来匹配。

const dom = `
  <h1>123</h1>
  `;
const reg = /.+/s;
console.log(dom.match(reg));

s 模式,会把字符串看一行来解析

[
    0: "\n      <h1>123</h1>\n"
    groups: undefined
    index: 0
    input: "\n      <h1>123</h1>\n"
]

\n就解析进去了

i修饰符

正则是辨别大小写的,如果是在 i 模式下,就不辨别大小写了

g修饰符

全局匹配。

个别状况下,没有量词的状况下,匹配到一个,就不会持续匹配上来了。如果增加了 \g,就会始终匹配上来满足条件的。

y修饰符

y 修饰符的作用与 g 修饰符相似,也是全局匹配,后一次匹配都从上一次匹配胜利的下一个地位开始。

不同之处:g 修饰符只有残余地位中存在匹配就可,而 y 修饰符确保匹配必须从残余的第一个地位开始

在这里,了解 y 修饰符 g 修饰符 须要了解下面的lastIndex 正则属性

实例:

const str = 'aaa_aa_aaaa'
const reg_g = /a+/g
const reg_y = /a+/y
console.log('第一次')
console.log(reg_g.lastIndex) // 0
console.log(reg_y.lastIndex) // 0
console.log(reg_g.exec(str)) // aaa
console.log(reg_y.exec(str)) // aaa
console.log('第二次')
console.log(reg_g.lastIndex) // 3
console.log(reg_y.lastIndex) // 3
console.log(reg_g.exec(str)) // aa
console.log(reg_y.exec(str)) // null
console.log('第三次')
console.log(reg_g.lastIndex) // 6
console.log(reg_y.lastIndex) // 0
console.log(reg_g.exec(str)) // aaaa
console.log(reg_y.exec(str)) // aaa

第一次, y 修饰符 g 修饰符 是一样的,lastIndex 都从 0 变成了 3

第二次, g 修饰符 能够持续匹配前面的残余字符 aa,所以lastIndex 从 3 变成了 6

然而 y 修饰符 须要的是残余字符 的第一个须要满足,很显然第二次的残余字符是不满足的,所以 匹配的值为 nulllastIndex 也从 重置了,变成 0

第三次, y 修饰符 有从新开始匹配。

u修饰符

正则的办法

字符串 的办法

match()

语法:

str.match(regexp)

参数:

一个正则表达式对象。如果传入一个非正则表达式对象,则会隐式地应用 new RegExp(obj) 将其转换为一个 RegExp。如果你没有给出任何参数并间接应用 match() 办法,你将会失去一 个蕴含空字符串的 Array:[“”]。

返回值:

  • 如果有g 修饰符,则返回所有匹配的信息,不会返回捕捉值
  • 如果没有 g 修饰符,则返回一个残缺以及相干的捕捉值。

示例:

// 状况一:存在 g 修饰符
const str = "copyer";
console.log(str.match(/c/g));    // ['c']

// 状况二:不存在 g 修饰符
const str = "copyer";
console.log(str.match(/c/));
// 残缺的信息
[
    0: "c" // 捕捉值
    groups: undefined
    index: 0
    input: "copyer"
]

matchAll()

语法:

str.matchAll(regexp)

参数:

正则表达式对象。如果所传参数不是一个正则表达式对象,则会隐式地应用 new RegExp(obj) 将其转换为一个 RegExp

RegExp必须是设置了全局模式 g 的模式,否则会抛出异样TypeError

返回值:

一个迭代器

实例:

const str = "abaca";
const matchs = str.matchAll(/a/g)  // 必须是全局模式
// 返回值是一个迭代对象,应用 for...of 遍历
for(let key of matchs) {console.log(key)
}
// ['a', index: 0, input: 'abaca', groups: undefined]
// ['a', index: 2, input: 'abaca', groups: undefined]
// ['a', index: 4, input: 'abaca', groups: undefined]

这里获取所有捕捉值的办法。然而在 matchAll() 呈现之前,是通过正则办法 exec()来实现同样的目标。在上面会介绍到。

replace()

语法:

str.replace(regexp|substr, newSubStr|function)

参数:

  • regexp(pattern)

    一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。

  • substr(pattern)

    一个将被 newSubStr 替换的 字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。

  • newSubStr (replacement)

    用于替换掉第一个参数在原字符串中的匹配局部的 字符串。该字符串中能够内插一些非凡的变量名。

  • function (replacement)

    一个用来创立新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的后果。

返回值:

不扭转原来的字符串,会返回一个新的字符串

实例:

// 状况一:参数为字符串:参数看为一个整体,匹配到的第一项会被替换
const str = "abaca";
const newStr = str.replace('a', 'f')
console.log(newStr) // fbaca

// 状况二:参数为正则:匹配到的字符被新的字符串替换
const str = "abaca";
const newStr = str.replace(/a/g, 'f')
console.log(newStr) // fbfcf

// 状况三: 第二个参数为一个函数,返回的字符串用来替换
const str = "abaca";
const newStr = str.replace(/a/g, () => {return '$#'})
console.log(newStr)

当然,这个函数不是这么简略,还有其余的用法。

search()

语法:

str.search(regexp)

参数:

一个 正则表达式(regular expression)对象。如果传入一个非正则表达式对象 regexp,则会应用 new RegExp(regexp) 隐式地将其转换为正则表达式对象。

返回值:

如果匹配胜利,则 search() 返回正则表达式在字符串中首次匹配项的索引; 否则,返回 -1

实例:

const str = "abaca";
console.log(str.search(/[b]/g))    // 1
console.log(str.search(/[d]/g))    // -1

split()

语法:

str.split([separator [, limit]])

参数:

  • separator

    指定示意每个拆分应产生的点的字符串。separator 能够是一个字符串或正则表达式。如果纯文本分隔符蕴含多个字符,则必须找到整个字符串来示意宰割点。如果在 str 中省略或不呈现分隔符,则返回的数组蕴含一个由整个字符串组成的元素。如果分隔符为空字符串,则将 str 原字符串中每个字符的数组模式返回。

  • limit

    一个整数,限定返回的宰割片段数量。当提供此参数时,split 办法会在指定分隔符的每次呈现时宰割该字符串,但在限度条目已放入数组时进行。如果在达到指定限度之前达到字符串的开端,它可能依然蕴含少于限度的条目。新数组中不返回剩下的文本。

返回值:

返回源字符串以分隔符呈现地位分隔而成的一个 Array

实例:

const str = "abaca";
console.log(str.split('c'))     // ['aba', 'a']
console.log(str.split('@'))     // ['abaca']
console.log(str.split(''))      // ['a','b','a','c','a']
console.log(str.split('a'))     // ['','b','c','']
console.log(str.split('', 3))   // ['a','b','a']

形容:

  1. 找到分隔符后,将其从字符串中删除,并将子字符串的数组返回。
  2. 如果没有找到或者省略了分隔符,则该数组蕴含一个由整个字符串组成的元素。
  3. 如果分隔符为空字符串,则将 str 转换为字符数组。
  4. 如果分隔符呈现在字符串的开始或结尾,或两者都离开,别离以空字符串结尾,结尾或两者开始和完结。

留神:

如果 分隔符是蕴含捕捉括号的正则表达式,则每次分隔符匹配时,捕捉括号的后果(包含任何未定义的后果)将被拼接到输入数组中。然而,并不是所有浏览器都反对此性能。

const str = "abaca";
console.log(str.split(/(a)/))    // ['','a','b','a','c','a','']

正则 的办法

test()

语法:

regexObj.test(str)

参数:

  • str

    用来与正则表达式匹配的字符串

返回值:

如果正则表达式与指定的字符串匹配,返回true;否则false

实例:

let str = 'hello world!';
let result = /^hello/.test(str);
console.log(result);  // true

exec()

语法:

regexObj.exec(str)

参数:

  • str

    要匹配正则表达式的字符串。

返回值:

如果匹配胜利,exec() 办法返回一个数组(蕴含额定的属性 indexinput),并更新正则表达式对象的 lastIndex属性。

齐全匹配胜利的文本将作为返回数组的第一项,从第二项起,后续每项都对应正则表达式内捕捉括号里匹配胜利的文本。

如果匹配失败,exec() 办法返回 null,并将 lastIndex 重置为 0。

实例:

const str = "abaca";
const reg = /a/g
console.log(reg.lastIndex)  // 0
console.log(reg.exec(str))  // ['a', index: 0, input: 'abaca', groups: undefined]
console.log(reg.lastIndex)  // 1

lastIndex会被更新。
获取全副的匹配信息:

let str = "abaca";
let reg = /a/g;
let result;

while ((result = reg.exec(str)) != null) {console.log(result)
}
// ['a', index: 0, input: 'abaca', groups: undefined]
// ['a', index: 2, input: 'abaca', groups: undefined]
// ['a', index: 4, input: 'abaca', groups: undefined]

谬误写法:

let str = "abaca";
let reg = /a/g;
let result;

while (reg.exec(str) !== null) {console.log(result)
}

正告: 不要把正则表达式字面量(或者 RegExp 结构器)放在 while 条件表达式里。因为每次迭代时 lastIndex 的属性都被重置,如果匹配,将会造成一个死循环。并且要确保应用了 ’g’ 标记来进行全局的匹配,否则同样会造成死循环。

正则的断言

正后行断言(?=

前面的内容,满足后面的条件

实例:

str='我有一只铅笔,我有一只钢笔',把铅笔后面的 一只,换成 2 只

let str = "我有一只铅笔,我有一只钢笔";
const reg = / 一只(?= 铅笔)/g
const newStr = str.replace(reg, '两只')
console.log(newStr) // 我有两只铅笔,我有一只钢笔

了解:

就看成是 条件 就行了,前面的内容 是否等于 ‘ 匹配的内容 ’。

在下面的例子中,想把 一只 改成 两只 ,全局搜寻的话,就会搜寻出两个,咱们只须要匹配 铅笔 的那一个,所以 判断条件 是否等于(?= 铅笔就行了

负后行断言(?!=

前面的内容,不满足后面的条件

实例:

let str = "我有一只铅笔,我有一只钢笔";
const reg = / 一只(?! 钢笔)/g
const newStr = str.replace(reg, '两只')
console.log(newStr) // 我有两只铅笔,我有一只钢笔

取反即可

正后发断言(?<=

后面的内容,满足前面的条件

实例:

let str = "我有一个大爸和二爸";
const reg = /(?<= 大)爸 /g
const newStr = str.replace(reg, '爷')
console.log(newStr) // 我有一个大爷和二爸

大爸 和 二爸 中的 大爸的爸改成爷(只是举例,其余形式更加的简略)

负后发断言(?<!

后面的内容,不满足前面的条件

实例:

let str = "我有一个大爸和二爸";
const reg = /(?<! 二)爸 /g
const newStr = str.replace(reg, '爷')
console.log(newStr) // 我有一个大爷和二爸

意思跟下面差不多

总结

断言:就看成 条件

  • 后行: 就是前面的内容须要满足的条件 / 表达式(条件)/ 条件是写在前面的

<!—->

  • 后发: 就是后面的内容须要满足的条件/(条件)/ 表达式 条件是写在后面的

留神:

应用了小括号,然而不能看成是 原子组

总结

花了两天的工夫,从头学习了一遍正则的根本语法。当然,在理论实际中,还是须要积攒的。

如果下面的内容有误,请提出来,多谢指教。

正文完
 0