Skip to content

匹配

作者:Yuan Tang
更新于:5 个月前
字数统计:997 字
阅读时长:4 分钟

重复匹配

基本使用

如果要重复匹配一些内容时我们要使用重复匹配修饰符,包括以下几种。

符号说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复 n 次
{n,}重复 n 次或更多次
{n,m}重复 n 到 m 次

因为正则最小单位是元字符,而我们很少只匹配一个元字符如 a、b 所以基本上重复匹配在每条正则语句中都是必用到的内容。

默认情况下重复选项对单个字符进行重复匹配,即不是贪婪匹配

js
const fn = 'fnddd'
console.log(fn.match(/fnd+/i)) // fnddd

使用原子组后则对整个组重复匹配

js
const fn = 'fnddd'
console.log(fn.match(/(fn)+/i)) // fn

下面是验证坐机号的正则

js
const fn = '010-12345678'
console.log(/0\d{2,3}-\d{7,8}/.exec(fn))

验证用户名只能为 3~8 位的字母或数字,并以字母开始

html
<body>
  <input type="js" name="username" />
</body>
<script>
  let input = document.querySelector(`[name="username"]`);
  input.addEventListener("keyup", e => {
    const value = e.target.value;
    let state = /^[a-z][\w]{2,7}$/i.test(value);
    console.log(
      state ? "正确!" : "用户名只能为3~8位的字母或数字,并以字母开始"
    );
  });
</script>

验证密码必须包含大写字母并在 5~10 位之间

html
<body>
<input type="js" name="password" />
</body>
<script>
let input = document.querySelector(`[name="password"]`);
input.addEventListener("keyup", e => {
  const value = e.target.value.trim();
  const regs = [/^[a-zA-Z0-9]{5,10}$/, /[A-Z]/];
  let state = regs.every(v => v.test(value));
  console.log(state ? "正确!" : "密码必须包含大写字母并在5~10位之间");
});
</script>

禁止贪婪

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

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

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

js
const 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 并描红,并在内容前加上 tydumpling-

html
<body>
  <main>
    <span>houdunwang</span>
    <span>fndd.com</span>
    <span>tydumpling.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">tydumpling-${p1}</h4>`;
  });
</script>

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

html
<body>
  <h1>
    tydumpling.com
  </h1>
  <h2>fndd.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>

全局匹配

问题分析

下面是使用match 全局获取页面中标签内容,但并不会返回匹配细节

html
<body>
  <h1>tydumpling.com</h1>
  <h2>fndd.com</h2>
  <h1>tydumpling</h1>
</body>

<script>
  function elem(tag) {
    const reg = new RegExp("<(" + tag + ")>.+?<\.\\1>", "g");
    return document.body.innerHTML.match(reg);
  }
  console.table(elem("h1"));
</script>

matchAll

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

需要添加 g 修饰符

js
const str = 'tydumpling'
const reg = /[a-z]/ig
for (const iterator of str.matchAll(reg))
  console.log(iterator)

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

js
String.prototype.matchAll = function (reg) {
  const res = this.match(reg)
  if (res) {
    const str = this.replace(res[0], '^'.repeat(res[0].length))
    const match = str.matchAll(reg) || []
    return [res, ...match]
  }
}
const str = 'tydumpling'
console.dir(str.matchAll(/(o)/i))

exec

使用 g 模式修正符并结合 exec 循环操作可以获取结果和匹配细节。注意:如果不添加 g 全局匹配,会造成死循环。因为代码 /u/i.exec('daoudaou') 会一直匹配第三项,不会往下匹配了。

html
<body>
  <h1>tydumpling.com</h1>
  <h2>fndd.com</h2>
  <h1>tydumpling</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>

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

js
const fn = `https://fndd.com
https://www.sina.com.cn
https://www.tydumpling.com`

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

Contributors

Yuan Tang