# 正则个人笔记(regexp)

# 1. 字符串替换

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div class="content">
    www.zhengchangfu.com
  </div>
  <script>
    const div = document.querySelector('div')
    const con = prompt('请输入要匹配的内容,支持正则')
    const regexp = new RegExp(con, 'g')
    div.innerHTML = div.innerHTML.replace(regexp, search => {
      return `<span style="color:red">${search}</span>`
    })
  </script>
</body>

</html>

# 2. 表单验证

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    span {
      color: red;
      opacity: 0;
      transition: all 0.3s linear;
    }

    span.show {
      opacity: 1;
    }
  </style>
</head>

<body>
  <input type="text">
  <span></span>
  <script>
    const input = document.querySelector('input')
    const span = document.querySelector('span')
    input.addEventListener('keyup', function () {
      // 匹配规则,3-6位英文字母
      const flag = this.value.match(/^[a-z]{3,6}$/i)
      if (!flag) {
        span.innerHTML = '错误'
        span.classList.add('show')
      } else {
        span.classList.remove('show')
      }
    })
  </script>
</body>

</html>

# 3. m 多行匹配修正符

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const str = `
    # 1. node.js 100元 #
    # 2. vue.js 100元 #
    # 3. react.js 100元 #
    # 4. dva 100元 #
    # 55. next.js 100元 #
    `
    // 要求:将1,2,3,5提取成一个对象,类似于{name:'node.js',price:100元}放入一个数组,4不满足,所以排除掉
    // m修饰符可以将多行看成一行对待
    const strArr = str.match(/^\s*#\s*\d+\.\s*[a-z]+\.js.+\s+#$/gm)
    let res = strArr.map(str => {
      // 去除前面多余字符
      str = str.replace(/\s*#\s*\d+\.\s*/, '')
      // 去除后面多余字符
      str = str.replace(/\s*#/, '')
      let [name, price] = str.split(' ')
      return {
        name,
        price
      }
    })
    console.log(res, 'res')
  </script>
</body>

</html>

# 4. 忽略换行符

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const str = `
    chengxiaohui
    zhengchangfu
    `
    const reg = /.+/g
    console.log(str.match(reg))
    // s可以忽略换行符进行匹配
    const reg1 = /.+/gs
    console.log(str.match(reg1))
    // 匹配所有内容
    const reg2 = /[\s\S]+/g
    console.log(str.match(reg2))
  </script>
</body>

</html>

# 5. 原子组替换标签

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    let str = `
    <h1>chengxiaohui</h1>
    <span>children</span>
    <h2>zhengchangfu</h2>
    `
    // 要求:匹配h开头的标签,替换成p标签
    // \1是代表拿到前面一个原子组匹配出来的结果,因为标签是成对的,不可以这样<h1></h2>
    const reg = /<(h[1-6])>([\s\S]*)<\/\1>/gi
    // $1是所有原子组中匹配出来的第1个结果,以此类推,$1-$9
    // console.log(str.match(reg)) // ["<h1>chengxiaohui</h1>", "<h2>zhengchangfu</h2>"]
    // str = str.replace(reg, `<p>$2</p>`)
    // console.log(str)
    // 还可以使用函数的形式
    str = str.replace(reg, function (p0, p1, p2) {
      // console.log(p0) // 匹配到的整体标签
      // console.log(p1) // 原子组的第一个结果
      // console.log(p2) // 原子组的第二个结果
      return `<p>${p2}</p>`
    })
    console.log(str)
  </script>
</body>

</html>

# 6. 链接和内容提取成数组

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const str = `
    <a class='zcf' href='http://www.zhengchangfu.com'>郑常富的网站1</a>
    <a id='cxh' href     ='https://chengxiaohui.com'>程小惠的网站2</a>
    <a class='zcfcxh' href='http://zhengchangfu.com.cn'>郑常富和程小惠的网站3</a>
    `

    // 将a标签中的链接地址和内容提取出来一个对象,放入一个数组中,如:[{link:'http://zhengchangfu.com',title:'郑常富的网站1'}]
    // 思路: 正则匹配链接和内容放入原子组,然后给匹配到的原子组进行重命名
    const reg = /<a.+?href\s*=(['"])(?<link>https?:\/\/(?:www.)?.*?\1)>(?<title>[\s\S]*?)<\/a>/ig
    // console.log(str.match(reg))
    let res = []
    for (let item of str.matchAll(reg)) {
      console.log(item['groups'])
      res.push(item['groups'])
    }
    console.log(res, 'res')
  </script>
</body>

</html>

# 7. 电话号码保护

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const phoneList = [
      '18297523437',
      '12345678901',
      '18297523438',
    ]
    const reg = /(\d{3})\d{4}(\d{4})/
    // 将手机号码中间4位替换成*
    const newList = phoneList.map(item => {
      return item.replace(reg, (v, p1, p2) => {
        return `号码已被我惠哥保护,保护后的号码为:${p1}${'*'.repeat(4)}${p2}`
      })
    })
    console.log(newList, 'newList')


    const reg2 = /(?<=\d{3})\d{4}/   // 如果单纯的替换可以换成断言的方式
    const newList1 = phoneList.map(item => {
      return item.replace(reg2, (v) => {
        return `${'*'.repeat(4)}`
      })
    })
    console.log(newList1, 'newList1')
  </script>
</body>

</html>

# 8. 限制用户敏感字

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    span {
      color: red;
      opacity: 0;
      transition: all 0.4s linear;
    }

    span.show {
      opacity: 1;
    }
  </style>
</head>

<body>
  <input type="text" name='username'>
  <span>不可出现敏感词汇</span>
  <script>
    // 敏感词汇数组
    const sensitiveWords = [
      '操',
      '麻痹',
      '干',
      '傻逼'
    ]
    // 思路:遍历敏感词汇数组,看是否匹配上了任意一个,如果匹配上了,就说明用户输入了敏感词,就提示
    const input = document.querySelector(`[name = username]`)
    const validatorSpan = document.querySelector('span')
    input.addEventListener('keyup', e => {
      const value = e.target.value
      const isExits = sensitiveWords.some(word => value.match(new RegExp(word, 'g')))
      isExits ? validatorSpan.classList.add('show') : validatorSpan.classList.remove('show')
    })
  </script>
</body>

</html>

# 9. 数字转为千分位

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="text" name='money'>
  <script>
    // 先分析转换的规律

    //#region
    // const num1 = 123
    // const num2 = 123.12
    // const formatNum1 = num1.toLocaleString('en-US');
    // const formatNum2 = num2.toLocaleString('en-US');
    // console.log(formatNum1); // 123
    // console.log(formatNum2); // 1,231.12

    // const num3 = 1234
    // const num4 = 1234.12
    // const num5 = 12345
    // const num6 = 12345.12
    // const formatNum3 = num3.toLocaleString('en-US');
    // const formatNum4 = num4.toLocaleString('en-US');
    // const formatNum5 = num5.toLocaleString('en-US');
    // const formatNum6 = num6.toLocaleString('en-US');
    // console.log(formatNum3) // 1,234
    // console.log(formatNum4) // 1,234.12
    // console.log(formatNum5) // 12,345
    // console.log(formatNum6) // 12,345.12

    // const num7 = 123456
    // const num8 = 123456.12
    // const num9 = 1234567
    // const num10 = 1234567.12
    // const formatNum7 = num7.toLocaleString('en-US');
    // const formatNum8 = num8.toLocaleString('en-US');
    // const formatNum9 = num9.toLocaleString('en-US');
    // const formatNum10 = num10.toLocaleString('en-US');
    // console.log(formatNum7) // 123,456
    // console.log(formatNum8) // 123,456.12
    // console.log(formatNum9) // 1,234,567
    // console.log(formatNum10) // 1,234,567.12
    //#endregion

    /*
      规律如下:
       1. 如果小于等于3位连续的数字,不变
       2. 如果大于3位的连续数字会从连续数字的最后一位往前数3位,依次加一个,号
       3. ,号前面最少要有1位数字
    */
    let str = 'zcf1zcf中'
    console.log(str.match(/zcf(?!(\d))/g))
    const input = document.querySelector(`[name=money]`)
    input.addEventListener('keyup', function () {
      const reg = /(?<=\d{3})\d*/
      //   div divaa out
      const antdReg = /\B(?=(\d{3})+(?![0-9]))/g

      console.log(this.value.replace(antdReg, str => {
        console.log(str)
      }))
    })
  </script>
</body>

</html>

# 10. 匹配插值语法

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div class="root">
    <p>{{ name }}</p>
    <p>{{ age }}</p>
    <span>
      <div>{{ sex }} {{hobby}}</div>
    </span>
  </div>
  <script>
    const obj = {
      name: 'zcf',
      age: 2,
      sex: '男',
      hobby: '爱小惠'
    }
    const reg = /\{\{([\s\S]*?)\}\}/gi
    let outerHTML = document.querySelector('.root').outerHTML
    document.querySelector('.root').outerHTML = outerHTML.replace(reg, (p, p1) => {
      p1 = p1.replace(/\s*/g, '')
      return obj[p1]
    })
  </script>
</body>

</html>

# 11. 匹配标签

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div a='b' c="d" f=e>11</div>
  <input a='b' c="d" f=e />
  <script>
    const div = document.querySelector('div').outerHTML
    const input = document.querySelector('input').outerHTML
    const ncname = `[a-zA-Z_][0-9_a-zA-Z]*`; // 标签名
    const qnameCapture = `((?:${ncname}\\:)?${ncname})`; //  用来获取的标签名的 match后的索引为1的
    const startTagOpen = new RegExp(`^<${qnameCapture}`); // 匹配开始标签的
    const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配闭合标签的
    //           aa  =   "  xxx "  | '  xxxx '  | xxx
    const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // a=b  a="b"  a='b'
    const startTagClose = /^\s*(\/?)>/; //     />   <div/>
    const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; // {{aaaaa}}
    // console.log(div.match(new RegExp(ncname)))
    // console.log(input.match(new RegExp(ncname)))
    // console.log(div.match(new RegExp(qnameCapture)))
    // console.log(input.match(new RegExp(qnameCapture)))
    // console.log(div.match(startTagOpen))
    // console.log(input.match(startTagOpen))
    // console.log(div.match(endTag))
    // console.log(input.match(endTag))
    // console.log("   f=e>11</div>".match(attribute))
    // console.log(input.match(attribute))
    console.log(' />'.match(startTagClose))
  </script>
</body>

</html>

# 12. 断言知识点

/*
      重点: 断言只是限制条件,不会参与到匹配结果中去
      ?=   后面是什么
      ?!   后面不是什么
      ?<=  前面是什么
      ?<!  前面不是什么
*/