正则[学习笔记二]

105 阅读4分钟

原子表

在一组字符中匹配某个元字符,在正则表达式中通过元字符表来完成,就是放到[] (方括号)中。

使用语法

原子表说明
[]只匹配其中的一个原子
[^]只匹配"除了"其中字符的任意一个原子
[0-9]匹配0-9任何一个数字
[a-z]匹配小写a-z任何一个字母
[A-Z]匹配大写A-Z任何一个字母

实例操作

使用[]匹配其中任意字符即成功,下例中匹配ia任何一个字符,而不会当成一个整体来对待

const url = "baidu.com";
console.log(/ia/.test(url)); //false
console.log(/[ia]/.test(url)); //true

日期的匹配

let tel = "2022-02-23";
console.log(tel.match(/\d{4}([-\/])\d{2}\1\d{2}/));

获取0~3间的任意数字

const num = "2";
console.log(/[0-3]/.test(num)); //true

匹配a~f间的任意字符

const lzx = "e";
console.log(/[a-f]/.test(lzx)); //true

顺序为升序否则将报错

const num = "2";
console.log(/[3-0]/.test(num)); //SyntaxError

字母也要升序否则也报错

const lzx = "baidu.com";
console.log(/[f-a]/.test(lzx)); //SyntaxError

获取所有用户名

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

原子表中有些正则字符不需要转义,如果转义也是没问题的,可以理解为在原子表中. 就是小数点

let str = "(baidu.com)+";
console.table(str.match(/[().+]/g));

//使用转义也没有问题
console.table(str.match(/[\(\)\.\+]/g));

可以使用 [\s\S] 或 [\d\D]匹配到所有字符包括换行符

const reg = /[\s\S]+/g;

下面是使用原子表知识删除所有标题

<body>
  <p>学习</p>
  <h1>baidu.com</h1>
  <h2>BAIDU.com</h2>
</body>
<script>
  const body = document.body;
  const reg = /<(h[1-6])>[\s\S]*<\/\1>*/g;
  let content = body.innerHTML.replace(reg, "");
  document.body.innerHTML = content;
</script>

原子组

如果一次要匹配多个原子,可以通过原子组完成
原子组与原子表的差别在于原子组一次匹配多个原子,而原子表则是匹配任意一个字符
元字符组用 () 包裹

下面使用原子组匹配 h1 标签,如果想匹配 h2 只需要把前面原子组改为 h2 即可。

const lzx = `<h1>baidu.com</h1>`;
console.log(/<(h1)>.+<\/\1>/.test(lzx)); //true

基本使用

没有添加 g 模式修正符时只匹配到第一个,匹配到的信息包含以下数据

变量说明
0匹配到的完整内容
1,2....匹配到的原子组
index原字符串中的位置
input原字符串
groups命名分组

在match中使用原子组匹配,会将每个组数据返回到结果中

0 为匹配到的完成内容
1/2 等 为原子级内容
index 匹配的开始位置
input 原始数据
groups 组别名
let lzx = "baidu.com";
console.log(lzx.match(/bai(du)\.(com)/)); 
//["baidu.com", "du", "com", index: 0, input: "baidu.com", groups: undefined]

下面使用原子组匹配标题元素

let lzx = `
  <h1>zhangsan</h1>
  <span>张三</span>
  <h2>lzx</h2>
`;

console.table(lzx.match(/<(h[1-6])[\s\S]*<\/\1>/g));

检测 0~100 的数值,使用 parseInt 将数值转为10进制

console.log(/^(\d{1,2}|100)$/.test(parseInt(09, 10)));

邮箱匹配

下面使用原子组匹配邮箱

let lzx = "2891990670@qq.com";
let reg = /^[\w\-]+@[\w\-]+\.(com|org|cn|cc|net)$/i;
console.dir(lzx.match(reg));

如果邮箱是以下格式 houdunren@hd.com.cn 上面规则将无效,需要定义以下方式

let lzx = `admin@liuzhongxin.com.cn`;
let reg = /^[\w-]+@([\w-]+\.)+(org|com|cc|cn)$/;
console.log(lzx.match(reg));

引用分组

\n 在匹配时引用原子组, $n 指在替换时使用匹配的组数据。下面将标签替换为p标签

let lzx = `
  <h1>baidu</h1>
  <span>zhangsan</span>
  <h2>lzx</h2>
`;

let reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi;
console.log(lzx.replace(reg, `<p>$2</p>`));

如果只希望组参与匹配,并不希望返回到结果中,使用 (?:) 处理。下面是获取所有域名的示例

let lzx = `
  https://www.zhangsan.com
  http://ZHANGSAN.com
  https://LZX.com
`;

let reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|org|cn))/gi;
while ((v = reg.exec(lzx))) {
  console.dir(v);
}

分组别名

如果希望返回的组数据更清晰,可以为原子组编号,结果将保存在返回的 groups字段中

let lzx = "<h1>zhangsan.com</h1>";
console.dir(lzx.match(/<(?<tag>h[1-6])[\s\S]*<\/\1>/));

组别名使用 ?<>形式定义,下面将标签替换为p标签

let lzx = `
  <h1>zhangsan</h1>
  <span>张三</span>
  <h2>LZX</h2>
`;
let reg = /<(?<tag>h[1-6])>(?<con>[\s\S]*)<\/\1>/gi;
console.log(lz.replace(reg, `<p>$<con></p>`));

获取链接与网站名称组成数组集合

<body>
  <a href="https://www.baidu.com">百度</a>
  <a href="https://www.zhangsan.com">zhangsan</a>
  <a href="https://www.sina.com.cn">新浪</a>
</body>

<script>
  let body = document.body.innerHTML;
  let reg = /<a\s*.+?(?<link>https?:\/\/(\w+\.)+(com|org|cc|cn)).*>(?<title>.+)<\/a>/gi;
  const links = [];
  for (const iterator of body.matchAll(reg)) {
    links.push(iterator["groups"]);
  }
  console.log(links);
</script>

禁止贪婪

正则表达式在进行重复匹配时,默认是贪婪匹配模式,也就是说会尽量匹配更多内容,但是有的时候我们并不希望他匹配更多内容,这时可以通过?进行修饰来禁止重复匹配

使用说明
*?重复任意次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复
??重复0次或1次,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复
{n,}?重复n次以上,但尽可能少重复

下面是禁止贪婪的语法例子

let str = "aaa";
console.log(str.match(/a+/)); //aaa
console.log(str.match(/a+?/)); //a
console.log(str.match(/a{2,3}?/)); //aa
console.log(str.match(/a{2,}?/)); //aa

将所有 span 更换为 h4 并描红,并在内容前加上 lzx-

<body>
  <main>
    <span>baidu</span>
    <span>baidu.com</span>
    <span>lzx.com</span>
  </main>
</body>
<script>
  const main = document.querySelector("main");
  const reg = /<span>([\s\S]+?)<\/span>/gi;
  main.innerHTML = main.innerHTML.replace(reg, (v, p1) => {
    console.log(p1);
    return `<h4 style="color:red">lzx-${p1}</h4>`;
  });
</script>

下面是使用禁止贪婪查找页面中的标题元素

<body>
  <h1>
    baidu.com
  </h1>
  <h2>zhangsan.com</h2>
  <h3></H3>
  <H1></H1>
</body>

<script>
  let body = document.body.innerHTML;
  let reg = /<(h[1-6])>[\s\S]*?<\/\1>/gi;
  console.table(body.match(reg));
</script>

matchAll

在新浏览器中支持使用 matchAll 操作,并返回迭代对象

需要添加 g 修饰符
let str = "lzx";
let reg = /[a-z]/ig;
for (const iterator of str.matchAll(reg)) {
  console.log(iterator);
}

在原型定义 matchAll方法,用于在旧浏览器中工作,不需要添加 g 模式运行

String.prototype.matchAll = function(reg) {
  let res = this.match(reg);
  if (res) {
    let str = this.replace(res[0], "^".repeat(res[0].length));
    let match = str.matchAll(reg) || [];
    return [res, ...match];
  }
};
let str = "lzx";
console.dir(str.matchAll(/(U)/i));

exec

使用 g 模式修正符并结合 exec 循环操作可以获取结果和匹配细节

<body>
  <h1>baidu.com</h1>
  <h2>zhangsan.com</h2>
  <h1>lzx</h1>
</body>
<script>
  function search(string, reg) {
    const matchs = [];
    while ((data = reg.exec( string))) {
      matchs.push(data);
    }
    return matchs;
  }
  console.log(search(document.body.innerHTML, /<(h[1-6])>[\s\S]+?<\/\1>/gi));
</script>

使用上面定义的函数来检索字符串中的网址

let lzx = `https://baidu.com  
https://www.sina.com.cn
https://www.baidu.com
`;

let res = search(lzx, /https?:\/\/(\w+\.)?(\w+\.)+(com|cn)/gi);
console.dir(res);