Javascript正则表达式

正则表达式: 正则是一种强大的工具,也是一门语言, 是程序员必备技能。

参考《迷你正则表达式》

  • 自问自答: 请你简单的描述一下正则表达式

  • 答: 正则表达式是一种字符串匹配的模式(pattern),1:要么匹配字符, 要么匹配位置。

  • 使用注意: 能用字符串 API 出马的问题, 就不该由正则出马。

    正则表达式的作用

  • 用来检查一个字符串中是否含有某种子串, 也就是说是否匹配某个模式。test()方法来完成将匹配的子串替换 replace()来完成

  • 从某个字符串中提取符合条件的子串等。使用正则的构造函数 RegExp.$1 ~ RegRxp.$9 引用缓存空间里面被正则匹配到的值。
    RegExp. 1 − R e g R x p . 1 - RegRxp. 1RegRxp.n 这里有超过 9 个分组,只有很少有正则能用超过9个分组以上的。

常用方法

  • reg.test() 方法用于检测一个字符串是否匹配某个模式, 匹配最少一处就返回 true,否则返回 false。

  • str.replace str.replace(‘子串’/ reg, ‘要替换的值’);

  • str.match() str.match(regexp/‘字符串子串’) 返回一个或者多个被正则匹配模式匹配上的字符。 该方法类似 str.indexOf()方法。

  • reg.exec() reg.ecec(str) 正则对象的实例方法 exec() 与 match 返回的结果相似。

    match返回值

  • 第一个元素是整体匹配结果,然后是各个分组(括号里)匹配的 内容然后是匹配下标,最后是输入的文本(就是我所理解的目标文本)

  • 正则表达式是否有修饰符 g,match 返回的数组格式是不一样的

正则表达式两种匹配规则

  • 匹配字符
  • 匹配位置
    对于位置的理解:
    * 假设现在有一个目标字符串。可以把这个目标字符串想象成为由若干空格和若干字符组成。
    * 例如: hello = “” + h + “” + e + “” + l + “” + l + “” + o + “”
    * 遇到匹配位置的正则去匹配目标对象的位置, 遇到匹配字符的正则去匹配字符。

正则表达式两种匹配模式

  • 如果正则只有精确匹配是没多大意义的,比如 /hello/,也只能匹配字符串中的 “hello” 这个子串。此称精确匹配
  • 横向匹配模式
    横向模糊: 一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。其实现的方式是使用量词。譬如 {m,n},表示连续出现最少 m 次,最多 n 次。
  • 纵向匹配模式
    纵向模糊: 一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种 可能。其实现的方式是使用字符组。譬如 [abc],表示该字符是可以字符 “a”、“b”、“c” 中的任何一个。

正则表达式的组成部分

  • 普通字符
    * 可打印字符
    这包括这包括英文字母、所有数字、所有标点符号和一些其他符号。
    * 不可打印字符
    非打印字符指在计算机中有一些字符是确确实实存在,但是它们不能够显示或者打印出来,比如回车符。最重要的几个不可打印字符 \n 换行符 \s 匹配任何空白字符 \r 匹配一个回车符。

  • 元字符组成
    \d \w \s \W 等等等不再一一列举了。

  • 特殊字符
    对于特殊字符使用前必须先转义。12 个特殊字符
    . * + ? ^ $ [] {} () | \ (反斜杠) / (斜杠)
    \ (反斜杠) 将下一个字符标记为 特殊字符或不可打印字符或原义字符或向后引用(也就是反向引用)。
    例如:
    标记为特殊字符: \d 特殊字符
    标记为不可打印字符: ‘\n’ 匹配换行符。
    标记为原义字符: ‘\’ ‘(’ 则匹配 “(”
    标记为向后引用: \1(在正则中引用缓冲区的第一个分组) \2(在正则中引用缓冲区的第二个分组)

正则表达式修饰符

i 执行对大小写不敏感的匹配。
g 执行全局匹配
正则表达式默认不是全局搜索,只在目标字符串匹配上一处, 就不往后匹配了。
使用 /gi 开启全局匹配 i 忽略大小写。

例如:
{
       let str = 'aaa'
       let reg = /a/
       console.log(reg.test(str)) // true
       console.log(str.match(reg)) // ["a", index: 0, input: "aaa", groups: undefined]
       }

   {
   let str = 'aaa'
   let reg = /a/g
   console.log(reg.test(str)) // true
   console.log(str.match(reg)) // ["a", "a", "a"]
   }

   {
   let str = 'aaa'
   let reg = /a+/
   console.log(reg.test(str)) // true
   console.log(str.match(reg)) // ["aaa", index: 0, input: "aaa", groups: undefined]
    }

   {
   let str = 'aaa'
   let reg = /a+/g
   console.log(reg.test(str)) // true
   console.log(str.match(reg)) // ["aaa"]
    }

{
   let str = 'aaa'
   let reg = /a+?/ // 使用贪婪匹配
   console.log(reg.test(str)) // true
   console.log(str.match(reg)) // ["a", index: 0, input: "aaa", groups: undefined]
   }

{
   let str = 'aaa'
   let reg = /a+?/g // 使用贪婪匹配
   console.log(reg.test(str)) // true
   console.log(str.match(reg)) // ["a","a","a"]
  }

正则表达式精确匹配:

/test/ 定义的这个正则规则,
使用场景: 准确无误的匹配字符串。
如果正则只有精确匹配是没多大意义的,比如 /hello/,也只能匹配字符串中的 “hello” 这个子串。此称精确匹配

匹配字符集:
应用场景: 需要匹配一组字符集中的字符。

[abc] 表示匹配 a,b,c 中任意一个字符。
如果字符集里的字符很多时,[abcde123456]
可以使用范围表示法: [a-z0-9]

注意:2 [ ] 内的字符一般下属于 普通字符范畴的,一般情况下特殊字符在" [] "内也是普通字符范畴。
1: 除了 ^ 符号。为了完成匹配字符集的功能,这个^一定不是普通字符。
2: 除了与 在 0-9 | a-z | A-Z 中使用的 " - " 符号 。
这个符号在这里还是有特殊意思的。想要匹配 " - " 可以使用其中一种方法转义。 /[a-z]+/
3: 如果在其他场景中使用自然还是属于普通字符范畴。

排除字符集 ^
[^abc] 匹配除了 abc 的字符。
[^a-bA-B] 当然也可以使用范围表示法 匹配除了 a-bA-B 以外的字符。
注意: ^ 该符号必须放在开头位置,否则会失去原有的功能。
例如: /[a^b]/g ^ 此时^符号就了普通字符范畴了。

正则表达式重复出现:

? 指定字符出现 0 次或者 1 次。特殊记忆法: 有么? 有或者没有

  • 指定字符出现 0 次,或者 1 次,或者多次。特殊记忆法则: " + " 是追加的意思,得先有一个才能追加。
  • 指定字符出现 1 次,或者多次。 特殊记忆法: 类似天上的星星,可以一颗也没有,也可能有无数个
    {n}
    {n,}
    {n,m}

    这些重复出现影响前面的术语
    例如: 匹配连续出现的a字符 /a+b/g
    但是 let reg = /ab+/g 这个+重复出现只会影响 b 匹配 abbbb 连续出现的 b
    如果想要匹配连续出现的 ab 我们可以使用分组。
    let reg = /ab/g
    console.log(‘ababab’.match(reg)) // [“ab”, “ab”, “ab”] 有多个匹配的结果
    let reg = /(ab)+/g 就可以匹配连续出现的 ababab 了。
    console.log(‘ababab’.match(reg)) // [“ababab”] 只有一个匹配结果。

\d 一个数字就为一处
\d+ 多个数字连在一起为一处
\d{3} 每三个数字为一处
(\d{3})+ 每三个数字或者 3 的倍数为一处
\d{2,5} 匹配连续出现的 2 位 3 位 4 位 5 位数字。
\d{3}+ 报错: 无效的正则表达式 ,说 + 前面没什么可重复的:
注意: 这里必须写(\d{3})+ ( ) 否则语法错误。

正则表达式进一步学习

使用圆括号同时具有两种功能: 分组和创建捕获
以下是需要了解的几个知识点
1: 分组和分支结构 (结合使用)
2: 反向引用
3: 非捕获括号

  • 对括号的使用是否得心应手,是衡量对正则的掌握水平的一个侧面标准。 先将两个简单的使用
1: 分组在分支结构中的运用

let reg = /^I love (javascript|regular expr)$/g
let str = 'I love javascript'
let str1 = 'I love regular expr'
console.log(reg.test(str))  // true
console.log(str.match(reg)) // ["I love javascript"]

console.log(reg.test(str1)) // true
console.log(str1.match(reg)) // ["I love regular expr"]

2: 如果去掉分组

let reg = /^I love javascript|regular expr$/g

let str = 'I love javascript'
let str1 = 'I love regular expr'

console.log(reg.test(str))  // true
console.log(str.match(reg)) // ["I love javascript"]

console.log(reg.test(str1))  // true
console.log(str1.match(reg)) // ["regular expr"]

另外也复习一下多选分支 expr1|expr2 这个语法就像if else 分支判断
但是在条件分支中使用分组又不一样了。是不是很神奇呢?

反向引用

分组引用 除了使用相应 API 来引用分组(RegExp.$1这种),也可以在正则本身里引用分组。但只能引用之前出现的分组,即反向引用。
在匹配过程中正则引擎, 给每一个分组都开辟一个空间(缓存区),用来存储每一个分组匹配到的数据。 既然分组可以捕获数据,那么我们就可以使用它们。
提取数据 RegExp.$1 - RegExp.$n
替换数据 replace()

反向引用
例如让我们匹配一个 html 标签 后面的结束标签引用前面放入缓存区的标签。



多个括号嵌套怎么办?
let reg = /((\d)(\d(\d)))\1\2\3\4/g
let str = '1231231233'
第一个字符是数字 例如是 1
第二个字符是数字 例如是 2
第三个字符是数字 例如是 3

使用 str.match(reg)
\1123 ---> 引用的是最外层的大括号
\21 ---> 引用的是最外层内第一个括号
\323 ---> 引用的是最外层第二个大括号
\43 ---> 引用的是最外层第二个大括号内的最后一个小括号

\10 表示什么呢? \10 还是 \10 呢?答案是前者
let reg = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(###) \10+/
let str = '123456789### #########'
console.log(reg.test(str)) // true
console.log(str.match(reg))

如果真要匹配 \10 的话,请使用 (?:\1)0 或者 \1(?:0)。
创建非捕获分组 匹配到的结果不丢进缓存区,也就无法引用。

### 创建非捕获的分组

不在 API 中引用,也不再正则中引用
当在不需要捕获的情况下,使用非捕获分组代替捕获。
其中 ?: 是非捕获元之一,还有两个非捕获元是
?= ?! 这两个还有其它的含义,就匹配位置的。

```javascript
{
let reg = /(\d)/
let str = '123456789'
console.log(str.replace(reg, '$1替换为缓冲区里的内容')) // 1替换为缓冲区里的内容23456789
}
{
let reg = /(?:\d)/
let str = '123456789'
console.log(str.replace(reg, '$1替换为缓冲区里的内容')) // $1替换为缓冲区里的内容23456789 (发现并没有创建捕获)
}

多选分支 |

类似两条交叉路, 多个子模式任选其一
let reg = /expr1|expr2/g
例如 使用子模式 expr1 或者 使用子模式 expr2
值得注意的是: 多选分支是惰性的,

var regex = /good|goodbye/g
var string = ‘goodbye’
console.log(string.match(regex))
// => [“good”]
也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。

贪婪模式:

字符串:let str = “aaa” ;
正则表达式:
/a+/ 这是贪婪模式 正则表达式会匹配全部 3 个字符。
/a+?/ ,这是非贪婪模式, 只匹配第一个字符 a 就足以满足规则。
通过在 重复出现符号 { m, } {m } { m, n } * + ? 之后放置 ?。
该表达式从"贪心"表达式转换为"非贪心"表达式或者最小匹配。

默认是贪婪模式:根据下面的正则 --> 你给我六个我要 5 个,给我 3 个我要三个。
let reg = /\d{2,5}/g;
let str = ‘123 1234 12345 123456’;
console.log(str.match(reg))
[“123”, “1234”, “12345”, “12345”]
这个打印出来的结果,也可以去正则表达式测试网站去测,匹配结果会显示。

非贪婪模式: 根据下面的正则分析 —> 你不管给我几个我最多要两个。

let reg = /\d{2,5}?/g;
let str = ‘123 1234 12345 123456’;
console.log(str.match(reg))
[“12”, “12”, “34”, “12”, “34”, “12”, “34”, “56”]

贪婪模式和全局匹配的区别(不要陷入了思想误区)
用几个例子

定位符 (匹配位置)

一、
对于位置的理解,我们可以理解成空字符。 例如 "hello"字符串等价于如下形式。
“hello” == “” + “h” + “”+ “e” + “” + “l” + “” + “l” + “” + “o” + “” 这些空字符串都是位置。
二、
^ 匹配开头,或者在多行匹配中,匹配行开头。( 多行匹配模式 具有 m 修饰符时 )
$ 匹配结尾,或者在多行匹配中,匹配行结尾。
\b 匹配一个单词边界,具体就是 \w 与\W之间的位置,也包括^ 与 \w 之间的位置,和 \w 与 $的位置。
大小写字母、数字、下划线为可以组成单词的字符,这些字符和其它字符相邻则为单词边界。
\B 匹配一个非单词边界 与\b 就是反面。 例如: 在字符串中所有位置中,刨去 \b, 剩下的就是 \B. 具体来说就是 ^ 与 \W \W 与 $ \w 与 \w \W 与 \W
三、
例子:
一种是匹配字符,一种是匹配位置,这里的\b 就是匹配位置的)。
例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“\b1*”可以匹配“1_23”中的“1*”,但不能匹配“213”中的“1”。

前瞻 exp1(?=exp2)
(?=expr2) 就与 ^(这个就是匹配开头的)一样好理解,就是 expr2 前面的位置。
举个实际的例子:
let reg = /(?=123)/; 匹配位置的正则,位置在哪?在目标对象那里。
let str = ‘123’ 把 123 理解为 " " + 1 + " " + 2 + " " + 3 + " "
我们的这个目标字符串正好最前面有一个位置,并且该位置后面还是 123,匹配上。
匹配上位置 " "。
根据以上的解释,如果要匹配上正则。目标字符串符合 exp2,并且前面的位置符合 exp1,那么则可以匹配上。
注意: 匹配的结果中不会存在 exp2,只有 exp1。

负前瞻 exp1(?!exp2) 匹配不是 exp2 前面的位置
根据以上解释: 如果要想匹配上正则。目标字符串符合 exp1 且后面不是 exp2。
就类似于 ^ 匹配开头了,后面不能是 exp2 模式,就这么简单。

后顾 (?<=exp2)exp1 匹配 exp1 且前面是 exp2。
负后顾 (?<!=exp2)exp1 匹配 exp1 且前面不是 exp2。
这个和上面的都是同理 (?<=expr)就与 $ (这个是匹配结尾的)一样好理解,就是 expr 的后面的位置。

疑惑解答: 对于位置的精确理解
开头的位置前面还有位置,还是开头是同一个位置。那么结尾后面还是结尾的位置,是同一个位置。(开头的开头还是开头,结尾的结尾还是结尾)
但是开头的位置前面肯定是没有任何字符的。
表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。
也等价于:
“hello” == “” + “” + “hello”
也就是说字符之间的位置,可以写成多个。
把位置理解空字符,是对位置非常有效的理解方式

开发中使用正则遇到的问题:

一、js相同的正则对象多次调用test()返回的值却不同的问题:

var reg = /^1[345678][0-9]{9}$/g;
console.log(reg.test(15328044636)); true
console.log(reg.test(15328044636)); false

前提学习:
正则对象的静态属性
RegExp.lastIndex:是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。
注意:
一:该属性只有设置标志 g 才能使用。
二:当方法 exec() 或 test() 再也找不到可以匹配的文本时,它们会自动把 lastIndex 属性重置为 0。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值