正则[学习笔记一]

286 阅读3分钟

选择符

|这个符号带表选择修释符,也就是 | 左右两侧有一个匹配到就可以。

例子:检测电话是否是上海或北京的坐机

let tel = "010-12345678";
//错误结果:只匹配 | 左右两边任一结果
console.log(tel.match(/010|020\-\d{7,8}/)); 

//正确结果:所以需要放在原子组中使用
console.log(tel.match(/(010|020)\-\d{7,8}/));

匹配字符是否包含 zhangsanlisi

const lzx = "zhangsan";
console.log(/zhangsan|lisi/.test(hd)); //true

字符转义

转义用于改变字符的含义,用来对某个字符有多种语义时的处理。

假如有这样的场景,如果我们想通过正则查找/符号,但是 /在正则中有特殊的意义。如果写成///这会造成解析错误,所以要使用转义语法 /\//来匹配。

const url = "https://www.baidu.com";
console.log(/https:\/\//.test(url)); //true

使用 RegExp 构建正则时在转义上会有些区别,下面是对象与字面量定义正则时区别

let price = 12.23;
//含义1: . 除换行外任何字符 	含义2: . 普通点
//含义1: d 字母 d   	    含义2: \d 数字 0~9
console.log(/\d+\.\d+/.test(price));

//字符串中 \d 与 d 是一样的,所以在 new RegExp 时\d 即为 d
console.log("\d" == "d");

//使用对象定义正则时,可以先把字符串打印一样,结果是字面量一样的定义就对了
console.log("\\d+\\.\\d+");
let reg = new RegExp("\\d+\\.\\d+");
console.log(reg.test(price));

下面是网址检测中转义符使用

let url = "https://www.baidu.com";

console.log(/https?:\/\/\w+\.\w+\.\w+/.test(url));

字符边界

使用字符边界符用于控制匹配内容的开始与结束约定。 边界符 说明 ^ 匹配字符串的开始 $ 匹配字符串的结束,忽略换行符

边界符说明
^匹配字符串的开始
$匹配字符串的结束,忽略换行符

匹配内容必须以www开始

const lzx = "www.baidu.com";
console.log(/^www/.test(lzx)); //true

匹配内容必须以.com结束

const lzx = "www.baidu.com";
console.log(/\.com$/.test(lzx)); //true

检测用户名长度为3~6位,且只能为字母。如果不使用 ^$ 限制将得不到正确结果

<body>
  <input type="text" name="username" />
</body>

<script>
  document
    .querySelector(`[name="username"]`)
    .addEventListener("keyup", function() {
      let res = this.value.match(/^[a-z]{3,6}$/i);
      console.log(res);
      console.log(res ? "正确" : "失败");
    });
</script>

字符列表

元字符说明示例
\d匹配任意一个数字[0-9]
\D与除了数字以外的任何一个字符匹配[^0-9]
\w与任意一个英文字母,数字或下划线匹配[a-zA-Z_]
\W除了字母,数字或下划线外与任何字符匹配[^a-zA-Z_]
\s任意一个空白字符匹配,如空格,制表符\t,换行符\n[\n\f\r\t\v]
\S除了空白符外任意一个字符匹配[^\n\f\r\t\v]
.匹配除换行符外的任意字符

使用体验

匹配任意数字

let lzx = "zhangsan 1998";
console.log(hd.match(/\d/g));

匹配所有电话号码

let lzx = `
	张三:010-99999999,李四:020-88888888
`;
let res = lzx.match(/\d{3}-\d{7,8}/g);
console.log(res);

获取所有用户名

let lzx = `
张三:010-99999999,李四:020-88888888`;
let res = lzx.match(/[^:\d-,]+/g);
console.log(res);

匹配任意非数字

console.log(/\D/.test(2029)); //false

匹配字母数字下划线

let lzx = "zhangsan@";
console.log(lzx.match(/\w/g));

匹配除了字母,数字或下划线外与任何字符匹配


console.log(/\W/.test("@")); //true

匹配与任意一个空白字符匹配

console.log(/\s/.test(" ")); //true
console.log(/\s/.test("\n")); //true

匹配除了空白符外任意一个字符匹配

let lzx = "zhangsan@";
console.log(lzx.match(/\S/g));

如果要匹配点则需要转义

let lzx = `zhangsan@com`;
console.log(/zhangsan.com/i.test(lzx)); //true
console.log(/zhangsan\.com/i.test(lzx)); //false

使用.匹配除换行符外任意字符,下面匹配不到baidu.com 因为有换行符

const url = `
  https://www.baidu.com
  baidu.com
`;
console.log(url.match(/.+/)[0]);

使用/s视为单行模式(忽略换行)时,. 可以匹配所有

let lzx = `
  <span>
    baidu
    BD
  </span>
`;
let res = lzx.match(/<span>.*<\/span>/s);
console.log(res[0]);

正则中空格会按普通字符对待

let tel = `010 - 999999`;
console.log(/\d+-\d+/.test(tel)); //false
console.log(/\d+ - \d+/.test(tel)); //true

所有字符

可以使用 [\s\S][\d\D] 来匹配所有字符

let lzx = `
  <span>
    baidu
    BD
  </span>
`;
let res = lzx.match(/<span>[\s\S]+<\/span>/);
console.log(res[0]);

模式修饰

正则表达式在执行时会按他们的默认执行方式进行,但有时候默认的处理方式总不能满足我们的需求,所以可以使用模式修正符更改默认方式。

修饰符说明
i不区分大小写字母的匹配
g全局搜索所有匹配内容
m视为多行
s视为单行忽略换行符,使用. 可以匹配所有字符
y从 regexp.lastIndex 开始匹配
u正确处理四个字符的 UTF-16 编码

i

将所有baidu.com 统一为小写

let lzx = "baidu.com BAIDU.COM";
lzx = lzx.replace(/baidu\.com/gi, "baidu.com");
console.log(lzx);

g

使用 g 修饰符可以全局操作内容

let lzx = "baidubaidu";
lzx = lzx.replace(/u/, "@");
console.log(lzx); //没有使用 g 修饰符是,只替换了第一个
let lzx = "baidubaidu";
lzx = lzx.replace(/u/g, "@");
console.log(lzx); //使用全局修饰符后替换了全部的 u

m

用于将内容视为多行匹配,主要是对 ^$ 的修饰

将下面是将以 # 数字开始的课程解析为对象结构,学习过后面讲到的原子组可以让代码简单些

let lzx = `
  #1 js,200元 #
  #2 php,300元 #
  #9 houdunren.com # 后盾人
  #3 node.js,180元 #
`;
// [{name:'js',price:'200元'}]
let lessons = lzx.match(/^\s*#\d+\s+.+\s+#$/gm).map(v => {
  v = v.replace(/\s*#\d+\s*/, "").replace(/\s+#/, "");
  [name, price] = v.split(",");
  return { name, price };
});
console.log(JSON.stringify(lessons, null, 2));

u

每个字符都有属性,如 L 属性表示是字母,P 表示标点符号,需要结合 u 模式才有效。其他属性简写可以访问 属性的别名

//使用\p{L}属性匹配字母
let lzx = "zhangsan.学习,做笔记,加油!";
console.log(lzx.match(/\p{L}+/u));

//使用\p{P}属性匹配标点
console.log(lzx.match(/\p{P}+/gu));

字符也有unicode文字系统属性 Script=文字系统,下面是使用 \p{sc=Han} 获取中文字符 han为中文系统,其他语言请查看 文字语言表

let lzx = `
张三:010-99999999,李四:020-88888888`;
let lzx = lzx.match(/\p{sc=Han}+/gu);
console.log(res);

使用 u 模式可以正确处理四个字符的 UTF-16 字节编码

let str = "𝒳𝒴";
console.table(str.match(/[𝒳𝒴]/)); //结果为乱字符"�"

console.table(str.match(/[𝒳𝒴]/u)); //结果正确 "𝒳"

lastIndex

RegExp对象 lastIndex 属性可以返回或者设置正则表达式开始匹配的位置

必须结合 g 修饰符使用
对 exec 方法有效
匹配完成时,lastIndex 会被重置为 0
let lzx = `李四zhangsan张三`;
let reg = /a/g;
reg.lastIndex = 3; // 从索引 3 开始搜索
console.log(reg.exec(lzx));
console.log(reg.lastIndex); // 7

reg = /\p{sc=Han}/gu;
while ((res = reg.exec(lzx))) {
  console.log(res[0]);
}

y

我们来对比使用 yg 模式,使用 g 模式会一直匹配字符串

let lzx = "baidubaidu";
let reg = /u/g;
console.log(reg.exec(lzx));
console.log(reg.lastIndex); // 5
console.log(reg.exec(lzx));
console.log(reg.lastIndex); // 10
console.log(reg.exec(lzx)); // null
console.log(reg.lastIndex); // 0

但使用y 模式后如果从 lastIndex 开始匹配不成功就不继续匹配了

let lzx = "baidubaidu";
let reg = /u/y;
console.log(reg.exec(lzx));
console.log(reg.lastIndex); //5
console.log(reg.exec(lzx)); // null
console.log(reg.lastIndex); // 0

因为使用 y 模式可以在匹配不到时停止匹配,在匹配下面字符中的qq时可以提高匹配效率

let lzx = `学习共勉QQ群:11111111,999999999,88888888`;

let reg = /(\d+),?/y;
reg.lastIndex = 7;
while ((res = reg.exec(lzx))) console.log(res[1]);