由/(\w+)\s(\w+)/ 而感,一篇道尽js中的正则表达式

在这里插入图片描述

上面的这个正则表达是很简单,但是在谈这个问题之前呢,我还是想聊聊正则表达式,一来呢,增加文章的可读性,二呢,也能帮助读者循序渐进,更好的过渡和理解

正则基础

创建正则表达式的方法

在JavaScript中可以通过两种方式去构造正则表达式。

  • 第一种就是将正则表达式包裹在含在两个正斜杠中
  const regex = /cat/;
  • 第二种就是使用RegExp 构造函数
  const regex = new RegExp('cat');
使用方法
  • 使用方法就很简单了,两者都是调用正则的test函数,将要检测的值【字符串】传入即可
  regex.test('cat');
  regex.test('persian-cat')
  • 不过还有一种更为简单的使用方法,这是正则表达式最简单的类型。能够直接在字符串中找到匹配的类型。
  /cat/.test('cat');
  /cat/.test('persian-cat');
  /cat/.test('woca tmd');
用特殊字符实现更为复杂的功能
任何字符 — .

它是由一个.表示。用来匹配除了换行符以外的任何单个字符串

  const regex = /.og/;
  regex.test('fog');  // true
  regex.test('dog');  // true

通配符是特殊字符之一。如果想要匹配的是一个点 . 字符该怎么办?

转义符 — \

反斜杠 \ 用于将特殊字符的含义切换为普通字符。所以是可以在文本中搜索点 . 字符的,并且这个点不会被解释为特殊字符。

  const regex = /dog./;
  regex.test('dog.');   // true
  regex.test('dog1');   // true
  const regex = /dog\./;
  regex1.test('dog.');  // true
  regex.test('dog1');   // false
字符集 — []

用方括号[]表示。这个模式用来匹配一个字符,该字符可能是括号中的任何字符。

  /[dfl]og/.test('dog'); // true
  /[dfl]og/.test('fog'); // true
  /[dfl]og/.test('log'); // true

需要注意的是字符串内的特殊字符(比如.)不再特殊,因此在这里不需要反斜杠\。我们来看一下其他的一些字符:

  /[A-z].test('abc')/;  // true
  /[A-z].test('Z')/;   // true

请注意,如果用字符集去匹配字符,记得大写字母一定是靠前的。这意味着 /[a-Z]/ 会引发错误

  const pattern = /[a-Z]/;

Uncaught SyntaxError: Invalid regular expression: /[a-Z]/: Range out of order in character class

既然能正向能匹配字符,如果我想反向来呢,比如我匹配字符串不包括含有df两个字符,怎么来处理呢,当然可以,用^,这个家伙在[]表示的就是取反的意思

  /[^df]og/.test('dog'); // false
  /[^df]og/.test('fog'); // false
  /[^df]og/.test('log'); // true

如果你想匹配大小写都有的一个字符串,慎用[A-Za-z],此时最好用不区分大小写的标志i来进行忽略处理

多次重复 — {}

匹配某个表达式出现的确切次数,我们可以用{}来实现,我们来用一个例子来,假如我们匹配一个电话号码格式如: +xx xxx xxx xxx:

  function isPhoneNumber(number){
      return /\+[0-9]{2} [0-9]{3} [0-9]{3} [0-9]{3}/.test(number);
  }
  isPhoneNumber('+12 123 123 123'); // true
  isPhoneNumber('123212'); // false

请注意,我们在此处进行了一些自定义:

  • {x} 完全匹配 x 次出现
  • {x,} 至少匹配 x 次
  • {x, y} 至少匹配 x 次且不超过 y 次
零个或多个重复 — /.*/

带有星号 * 的表达式可以匹配 0 次或更多次。它实际上等效于 {0,}
这样我们可以轻松构造一个可以匹配任意数量字符的模式:/.*/

修饰符
修饰符描述
i执行对大小写不敏感的匹配
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)
m执行多行匹配
忽略大小写 — i
  /dog/i.test('dog'); // true
  new RegExp('dog', 'i').test('DoG');
全局匹配
  var str = 'aa'
  var reg1 = /a/;
  str.match(reg1)  // 结果为:["a", index: 0, input: "aa"]
  var reg2 = /a/g;
  str.match(reg2)  // 结果为:["a", "a"]
  console.log(reg2.lastIndex)    // 0
  alert(reg2.test(str))   // true
  console.log(reg2.lastIndex)   // 1
  alert(reg2.test(str));   // true
  console.log(reg2.lastIndex)  // 2
  alert(reg2.test(str));  // false
  console.log(reg2.lastIndex) // 0
  alert(reg2.test(str));  // true

从上面的例子我们可以看出,我们可以总结出几点:

  • 正常情况下,正则匹配到第一项即暂停
  • 如全局匹配,会一直匹配到最后一项,直到匹配不了为止
  • 正则有个lastIndex属性,这个属性每test(验证)一次都会自动加1,直到匹配不到位置,重新复位,这个不可复位性或叫做不可重入性
  • 每个test后,可以将regex.lastIndex = 0,手动复位

咱们再看一个应用场景啊

  const lorem = 'I_want_to_speak_english';
  lorem.replace('_', ' ');  // 'I want_to_speak_english'
  lorem.replace(/_/g, ' '); // 'I want to speak english'

replace函数自带iteration (迭代),将匹配到的所有对象,全部依次替换为第二个参数内容

多行模式 — m

本来想着在最后再说这个多行模式呢,为了完整性,还是放到修饰符这,正则的每个修饰符和表达符都是串在一起的,你中有我我中有你,很难将具体的一个点完全剖开,单独拎出来讲,既然换行,肯定就是匹配每行里都可以包含的元素,且看示例:

  const pets = `
  dog
  cat
  parrot and other birds
  `;
  /^dog$/m.test(pets); // true
  /^cat$/m.test(pets); // true
  /^parrot$/m.test(pets); // false

我们可以看到,它改变了插入符号和美元符号的含义。在多行模式下,它们代表一行的开头和结尾,而不是整个字符串。同时对于换行符\n同样有效。
示例中parrot是在pets的第三行能匹配到,但是并不是以parrot结尾,故为false

捕获组 — ()

有人疑问了,为啥{}代表多次重复,这个()出来了,代表着捕获分组,通俗一点就是每个括号的内容就是一个捕获分组,比如(\d)\d, 这里的 “(\d)” 这就是一个捕获分组,有多少个()就代表着有多少个分组,这个有什么意义呢,其实就是方便给我们
匹配到的对象进行编号,通过编号呢,同时呢还允许我们对其进行命名,这样我们还能很方便通过$1$2等对捕获组进行一一对应的拿到匹配对象中具体的某个值,比如:

编号命名捕获组匹配内容
0(\d{4})-(\d{2}-(\d\d))2008-12-31
1(\d{4})2008
2(\d{2}-(\d\d))12-31
3(\d\d)31

如果我们对其进行命名,则稍微调整即可

编号命名捕获组匹配内容
0(?<year>\d{4})-(?<date>\d{2}-(?<day>\d\d))2008-12-31
1year(?<year>\d{4})2008
2date(?<date>\d{2}-(\d\d))12-31
3day(?<day>\d\d)31

也可以进行局部的命名,即上面上种方法混合,变成这样

编号命名捕获组匹配内容
0(\d{4})-(?<date>\d{2}-(\d\d))2008-12-31
1(\d{4})2008
2date(?<date>\d{2}-(\d\d))12-31
3(\d\d)31

回头看/(\w+)\s(\w+)/

现在咱们再来看这个/(\w+)\s(\w+)/已经很好理解了,首先呢拆分每个符号具体的意义:

修饰符描述
\w匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’
\s匹配任何空白字符,包括空格、制表符、换页符等等
()捕获组

更多请看

所以呢就一目了然,/(\w+)\s(\w+)/ 匹配的是符合 数字字母 + 空白符 + 数字字母 这中类型的值,其中两个括号内的值是一样的,都是匹配 数字字母,且有且只有两项
咱们看个例子啊:

  var re = /(\w+)\s(\w+)/;
  var str = "zara ali haha hehe";
  var newstr1 = str.replace(re, "$2、$1、$3");
  console.log(RegExp.$1)
  console.log(RegExp.$2)
  console.log(RegExp.$3)
  console.log(newstr1);

大家可以看一下这四个结果是什么

  zara
  ali
  ali、zara、$3 haha hehe

看到这个结果,有的人是不是很惊讶,第三个和第四是怎么一回事,其实很简单,表达是只有两个捕获组,分别是

  • $1 -> ‘zara’
  • $2 -> ‘ali’

根本就没有第三个捕获组,所以$3当然是空了
RegExp的构造函数中调用replace,分别对匹配到的进行替换,将匹配的第一个捕获组替换成$2,同理将匹配的第二个捕获组替换成$1,并和原来的未匹配到的进行拼接,那不就是 ali、zara、 haha hehe 了吗,对!
确实是这样样子的,但是replace方法在将当$n为空时,直接将该$n赋值给空和原字符串进行拼接,所以就得到了期望的ali、zara、$3 haha hehe,这就是正则表达是的奥妙之处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值