进阶教程 5. 正则基础

183 阅读3分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

余生再无倾城色,一草一木皆相思

一、 定义及应用场景

正则:(RegExp:Regular Expression)

专门用来处理字符串的规则,这个处理包含两方面的:

  1. 正则匹配,判断某一个字符串是否符合规则
  2. 正则捕获,把符合规则的字符串捕获到;
  • 学习正则学习编写一个正则:
let reg = /\d/img; // 字面量方式创建
let reg0 = new RegExp('\d+', 'img'); // 实例的方式创建

二、正则的构成:元字符和修饰符

2.1 元字符

  • 特殊元字符
    • \d 匹配0-9中的任意一位数字
    • \D 匹配除了0-9数字以外的任意字符
    • \w 匹配数字、字母、_ 中的任意一个字符串
    • \s 匹配一个空白符(空格、\t制表符)
    • \b 匹配单词的边界 'zhu-feng' z的左侧,u的右侧,f左侧,g的右侧都是边界
    • \n 匹配一个换行符
    • . 不是小数点,是匹配除 \n 以外的任意字符串
    • \ 转义符,将普通的字符串转义成特殊的元字符,例如\d 就表示 0 -9 之间的字符串;还可以将特殊的元字符转义成普通元字符。如 . 不再表示除 \n 以外的任意字符,而是表示普通小数点
    • ^ (读作caret符)表示以某个元字符开头
    • $ 表示以某个元字符结尾
    • x | y 表示x或者y中的任意一个
    • [xyz] 表示x/y/z 中的任意一个
    • [a-z] a-z 中的任意一个字母
    • [0-9] 0-9中的任意一个数字
    • [^a-z] 除了a-z以外的任意字符
    • () 正则分组
    • (?:) 当前分组值匹配不捕获
    • (?=) 正向预查
    • (?!) 负向预查
  • 量词元字符:表示出现次数的元字符
      • 出现 0次到多次
    • ? 出现0次到1次
    • {n} 出现n次
    • {n, m} 出现n次到m次
    • {n,} 至少出现n次
  • 普通元字符:只要在正则中出现,(基于字面量的方式创建),除了有特殊元字符和量词元字符以外,其余的都是普通元字符。
  • 修饰符:
    • i: ignorecase 忽略大小写
    • m: multiline 多行匹配
    • g: global 全局匹配

2.2 示例:

RegExp.prototype.test() 方法

let reg2 = /\D/;
console.log(reg2.test('2')); // false
console.log(reg2.test('a')); // true
console.log(reg2.test('_')); // true

let reg3 = /\w/;
console.log(reg3.test('1')); // true
console.log(reg3.test('a')); // true
console.log(reg3.test('_')); // true
console.log(reg3.test('A')); // true

let reg4 = /\n/;
console.log(reg4.test(`
`)); // true 模板字符串中输入了一个回车

let reg5 = /\s/;
console.log(reg5.test('    ')); //true
console.log(reg5.test('   ')); // true

let reg6 = /\b/;
console.log(reg6.test('zhu-feng'));

let reg7 = /./;
console.log(reg7.test('我')); // true
console.log(reg7.test('x')); // true
console.log(reg7.test('12')); // true
console.log(reg7.test('\n')); // false

let reg8 = /^\d/;
console.log(reg8.test('1abc')); // true
console.log(reg8.test('x23c')); // false

let reg9 = /\d$/;
console.log(reg9.test('hell9')); // true
console.log(reg9.test('123hello')); // false

let reg10 = /^\d$/; // 匹配一个数字,这个数字既是开头也是结尾;就是说这个字符串中只能有一个数字
console.log(reg10.test('9')); // true
console.log(reg10.test('99')); // false
console.log(reg10.test('abc1')); // false
console.log(reg10.test('1xyz')); // false

let reg11 = /a|b/;
console.log(reg11.test('a')); // true
console.log(reg11.test('b')); // true
console.log(reg11.test('ab')); // true

let reg12 = /[a-z]/;
console.log(reg12.test('a')); // true
console.log(reg12.test('z')); // true
console.log(reg12.test('xyz')); // true
console.log(reg12.test('A')); // false

let reg13 = /[^0-9]/;
console.log(reg13.test('8')); // false
console.log(reg13.test('ABC')); // true
console.log(reg13.test('\n')); // true
console.log(reg13.test('_')); // true

let reg14 = /\d*/;
console.log(reg14.test('abc')); // true
console.log(reg.test('12133bade')); // true

let reg15 = /\d?/;
console.log(reg15.test('dsjf8lkk')); // true

let reg16 = /\d{3}/;
console.log(reg16.test('666')); // true
console.log(reg16.test('22')); // false
console.log(reg16.test('111')); // true

三、方括号的细节问题

3.1 方括号中的元字符

[] 正则中的方括号:表示方括号中的元字符中的任意一个

特殊元字符:在正则中有特殊含义的字符串如 . ,在正则中表示匹配除 \n 之外的任意其他字符,原义是 小数点

  1. 在方括号中的部分特殊元字符代表的是该字符的原义,不再具有特殊含义 如: [.] [?] [*] [+]
let reg = /./;
let reg1 = /[.]/; // 方括号中的 . 表示普通小数点
console.log(reg.test('abc')); // true
console.log(reg.test('.')); // true
console.log(reg1.test('abc')); // false
console.log(reg1.test('.')); // true
let reg2 = /[?]/; // 方括号中的?表示问号,不是两次元字符
console.log(reg2.test('?'));

let reg3 = /[*]/; // 方括号中的*表示星号,不是量词源字符串
console.log(reg.test('*')); // true

let reg4 = /[+]/; // 方括号中的+表示加号,不是量词元字符
console.log(reg4.test('+')); // true

3.2 方括号不能识别多位数

let reg5x = /^183$/; // 只能匹配183
let reg5 = /^[183]$/; // 代表1或者8或者3中的任意一个数字

console.log(reg5.test('1')); // true
console.log(reg5.test('8')); // true
console.log(reg5.test('3')); // true
console.log(reg5.test('18')); // false
console.log(reg5.test('13')); // false
console.log(reg5.test('183')); // false

3.3 中括号限制范围 [0-9] [a-z] [A-Z]

let reg6 = /^[23-68]$/; // 这个正则表示匹配 2 或 3-6 或 8
console.log(reg6.test('23')); // false
console.log(reg6.test('34')); // false
console.log(reg6.test('67')); // false
console.log(reg6.test('2')); // true
console.log(reg6.test('4')); // true
console.log(reg6.test('9')); // false

? 思考:为什么会有这种情况?因为中括号中连续出现的多位数不是表示一个多位数,而是表示多个一位数字;

  • ? 思考:此时真的需要一个数字范围的话怎么办?

我们可以把这个范围拆分,如23-68, 然后再两位数分别表示这些段 23 - 29 2[3-9] 30-59 [3-5][0-9] 60-68 6[0-8]

let reg7 = /^(2[3-9]|[3-5]\d|6[0-8])$/;
console.log(reg7.test('22')); // false
console.log(reg7.test('23')); // true
console.log(reg7.test('34')); // true
console.log(reg7.test('67')); // true
console.log(reg7.test('2')); // false
console.log(reg7.test('4')); // false
console.log(reg7.test('9')); // false

四、正则捕获

4.1 正则的捕获:把正则匹配到内容捕获到

RegExp.prototype.exec() 方法

let str = 'hello2019 qq2019 qq9021';
let reg = /qq\d+/;
let execs = reg.exec(str);
console.log(execs);
/*
* [
*   "qq2019", 捕获到的内容
*   index: 10, 捕获的起始索引位置
*   input: "hello2019 qq2019 qq9021", 原始字符串
 *   groups: undefined 命名匹配
 * ]
*
* */
let reg2 = /xxxx/;
console.log(reg2.exec(str)); // null
// 如果捕获不到不到会返回null

4.2 正则捕获的懒惰性

先来看下面一段示例:

console.log(reg.exec(str)); 
// ["qq2019", index: 10, input: "hello2019 qq2019 qq9021", groups: undefined]
console.log(reg.exec(str));
// ["qq2019", index: 10, input: "hello2019 qq2019 qq9021", groups: undefined]
  • 正则的懒惰性

如果不做特殊处理,正则每次捕获的时候都是从索引0开始,捕获到第一个符合条件的就停止捕获,后面有多少都不去捕获。

  • 破除懒惰性的解决方案:在正则后加修饰符g

  • 示例:

let reg3 = /qq\d+/g;
console.log(reg3.exec(str)); // ["qq2019", index: 10, input: "hello2019 qq2019 qq9021", groups: undefined]

console.log(reg3.exec(str)); // ["qq9021", index: 22, input: "hello2019 qq2019 qq9021", groups: undefined]

console.log(reg3.exec(str)); // null

console.log(reg3.exec(str)); // ["qq2019", index: 10, input: "hello2019 qq2019 qq9021", groups: undefined]

console.log(reg3.exec(str)); // ["qq9021", index: 22, input: "hello2019 qq2019 qq9021", groups: undefined]
console.log(reg3.exec(str)); // null

思考? 为啥捕获到最后一次会是null呢?

这些我们下一篇继续讨论!


纤云弄,飞星传恨,银汉迢迢暗度。

金风玉露一相,便胜却人间无数。