正则表达式常用学习

0.简介

regular expression,规则表达式,是一种用来处理字符串的规则,巧妙的正则表达式可以节省很多判断代码.

1.组成

  • 元字符
    • 特殊元字符

\ 转义字符 => 普通字符 <=> 特殊字符
. 点 => 代表除了\n 以外的任意字符(字符字符字符)
^ => 以哪个元字符开始
$ => 以哪个元字符作为结束
\n 任意一个换行符
\d 0-9之间的一个数字
\D 除了0-9之间的任意字符
\w 数字字母下划线中的任意一个字符
\W
\s 一个空白字符 空格 制表符 换页符等
\t 一个制表符 4个空格
\b 单词边界
x | y 或者
[xyz] => x | y | z
[^xy] => 除了 x | y 的任意字符
[a-z]
[^a-z]
(正则中的分组符号)
(?:) => 只匹配不捕获
(?=) => 正向预查
(?!) => 负向预查

  • 量词元字符

* => 0-多次
+ => 1-多次
? => 0 - 1次
{n} => 出现n次
{n,m} => 出现n-m次

  • 普通元字符

/abc/ 匹配abc

  • 修饰符
  1. i ignoreCase
    忽略单词大小写
  2. m multiLine
    进行多行匹配
  3. g global
    全局匹配

2.贪婪性

    let str = 'ab2020nb',
    // 贪婪性:默认情况下,捕获的时候,按照当前正则匹配的最长结果来获取
    reg = /\d+?/g
    console.log(str.match(reg))  
    // 在量词后面设置? 取消捕获时候的贪婪性 按照正则匹配的最短结果来获取
    
    /*
      * 问号的作用
      * 问号左边是非量词元字符:则此时问号本身代表两次元字符出现0-1次
      * 问号左边是量词元字符:取消捕获时候的贪婪性  /\d+?/g
      * (?:) 只匹配不捕获
      * (?=) 正向预查
      * (!=) 负向预查
    */

3.懒惰型

   /*
       * 基于exec的捕获
       * 捕获到的结果是一个数组或者null
       * 第一项:本次捕获到的内容
       * 其余项:对应小分组本次单独捕获的内容
       * 当前捕获内容在字符串中的索引
       * input:原始字符串
       * 每执行一次exec只能捕获到一个符合正则规则的字符串,默认情况下执行多次也只能匹配第一次捕获的结果  => 正则捕获的懒惰型
       * 懒惰产生的原因:每一次匹配的lastIndex值不变 只能通过全局修饰符才能改变这一特性
       * lastIndex : 当前正则匹配下一次匹配的起始索引位置
       *
       * 设置全局匹配符g之后第一次匹配完成之后会自动修改lastIndex的值
       * 此时可以进行循环多次进行匹配
       *
       */
      let str = 'nb13奥利给6666aaaa',
        reg = /\d+/g

4.举例几个简单正则表达式

  • 1).验证手机号
     //  \^abc0$\  只能是abc0

      // 验证手机号
      let tel = /^1\d{10}$/ //1后面十位数字

      let reg1 = /^2.3$/ //=> 只能匹配2中间一个除了换行之外的任意字符 3
      console.log(reg1.test('2.3'))
      console.log(reg1.test('253'))
      console.log(reg1.test('2@3'))
      let reg2 = /^2\.3$/ //=> 只能是2.3
      console.log(reg2.test('2.3'))
      let reg3 = /^\\d$/ // => 匹配   \d  特殊转换为普通
      console.log(reg3.test('\\d'))

      // 直接使用 /^18|69$/  存在很乱的优先级问题
      // /^(18|69)$/  这样不存在优先级混乱问题

      // [] 中括号出现的字符一般都是代表本身的含义 且不包含多位数 只能是一位数
      // /^[@+]/$  出现@ 或者+ 中的一个
      //  /^[\d]$/  \d在中括号中还是0-9数字
      //  /[10-29]/  1 0 - 2 9
  • 2).验证是否为有效数值
     // 验证是否为有效数值
      // 规则分析: 可能出现正负号 只出现一位  [+-]?
      //        一位0-9 多位不能0开头 (\d | [1-9]\d+)
      //        小数部分可能有可能没有 有小数点后面必须有数字 (\.\d+)?
      // 重点重点重点  正则中不能随意添加空格,空格会被识别当做判别中的一位
      let num = /^[+-]?(\d|([1-9]\d+))(\.\d+)$/
      console.log(num.test('+0.1'))
// 更简单
isNaA(parseFloat(num)) === true 
  • 3)验证真实姓名
      /*
       *验证真实姓名
       * 汉字范围 /^[\u4E00-\u9FA5]$/
       * 名字长度2-10
       * 可能有译名  .汉字
       *
       * /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
       *
       */
      let reg = /(.)?/
      reg.test('.')
    1. 验证邮箱
      /*
       *验证邮箱
       *  \w+((-\w+)|(\.\w+))*
       * 开头是字母数字下划线(1-多位)
       * 还可以输 -数字字母下划线 或者.数字字母下划线 *
       * 邮箱的名字由数字字母下划线- . 几部分组成但是.-不能连续出现或者作为开头
       *
       * @[A-Za-z0-9]+
       * @后面数字字母1-多位
       *
       * ((\.|-)[A-Za-z0-9]+)*
       * 对@后面名字的补充
       * 多域名  .com.cn
       * 企业邮箱 zdz@jiazu-qiye.com
       *
       * \.[A-Za-z0-9]+
       * 匹配最后的常规    .com  域名
       */

      let email =
        /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-za-z0-9]+)*\.[A-Za-z0-9]+$/
    1. 身份证验证
  /*
      *身份证
      *规则:
            1.一共18位
            2.最后一位可能是X  代表10
            前六位:省市县
            最后四位:
              最后一位X或者数字
              倒数第二位: 偶数女 奇数 男
              其余两位经过算法算出

              小括号还具有:分组捕获功能,不仅可以把大正则匹配的信息捕获,还可以单独捕获到每个小组的内容
      *
    */
      let id = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/
      console.log(id.exec('37152219900101823X'))

5.以前学习的记录

  let time = '2020-11-6'  // => 2020年11月16日
    let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})/g
    time2 = time.replace(reg,'$1年$2月$3日')
    console.log(time2)

    // 思路:拿reg和time匹配捕获,匹配几次就会把传递的函数执行几次
    //      不仅把方法执行而且replace还会把实参信息传递给函数
    time1 = time.replace(reg,(...arg) => {
      console.log(arg)
      // 参数中第一项为捕获到的完整字符串,其后为每次捕获到的小分组
      // 最后一项为匹配字符串 倒数第二项为第一次匹配的索引
      let [,$1,$2,$3] = arg
      function padZero(str) {
        return ('00' + str).slice(str.length)
      }
      $2 = padZero($3)
      $3 = padZero($2)
      return $1 + '年' + $2 + '月' + $3 
      + '日'
    })
    console.log(time1)

   // 首字母大写  replace不改变原字符串

    let reg1 = /\b([a-zA-Z])[a-zA-Z]+\b/g
    let str1 = 'good good study day day up!'
    // 函数执行6次
    str1 = str1.replace(reg1,(...arg) => {
      // 参数的第一项是匹配的完整分组 good 第二项是匹配到的小分组 g
      console.log(arg)
      let [content,$1] = arg
      $1 = $1.toUpperCase()
      content = content.slice(1)
      return $1 + content
    })
    console.log(str1)
  • 字母出现最大次数
 /*
      * 如何验证一个字符串中那个字符串出现的次数
      *
      *
    */

    let str = 'aaabbbbbbbbccccbcccccd'
    // 去重
    function statastic(str) {
      let obj = {},
        max = 1,
        res = []
        ;[].forEach.call(str, char => {
          if (typeof obj[char] !== 'undefined') {
            obj[char]++
            return
          }
          obj[char] = 1
        })
      for (let key in obj) {
        let item = obj[key]
        item > max ? max = item : null
      }
      for (let key in obj) {
        if (obj[key] === max) {
          res.push(key)
        }
      }
      // 此时res被转换为字符串
      return `maxItem:${res}\nvalue:${max}`
    }
    console.log(statastic(str))


    // 排序
    function statastic1(str) {
      // 将字符串转化为数组排序再转化为字符串
      str = str.split('').sort((a, b) => a.localeCompare(b)).join('')
      // 匹配重复字母
      let reg = /([a-zA-Z])\1+/g
      // 捕获
      let arr = str.match(reg)
      // 把捕获的结果数组根据长度排序 得到一个降序排列的数组里面包含结果数组字符串
      // ["bbbbbbbb", "cccccccc", "aaa"]
      arr.sort((a, b) => b.length - a.length)
      console.log(arr)
      let max = arr[0].length,
        res = [arr[0].substr(0, 1)]
      // 对于可能出现相同次数的字母进行查询
      for (let i = 1; i < arr.length; i++) {
        let item = arr[i]
        if (item.length < max) {
          break
        }
        res.push(item.substr(0, 1))
      }
      return `maxItem:${res}\nvalue:${max}`
    }
    console.log(statastic1(str))

    function statastic3(str) {
      // 字母排序
      str = str.split('').sort((a, b) => a.localeCompare(b)).join('')
      let max = 0,
        res = [],
        flag = false;
        console.log(str)
      for (let i = str.length; i > 0; i--) {
        // 查找出现次数最多的  从最大次数(字符串长度)开始检测
        let reg = new RegExp('([a-zA-Z])\\1{' + (i - 1) + '}', 'g')
        str.replace(reg, (content, $1) => {
          res.push($1)
          max = i 
          flag = true
          // break //不能放在这里 只能放在循环中
        })
        if (flag) {
          break
        }
      }
      return `maxItem:${res}\nvalue:${max}`
    }
    console.log(statastic3(str))
  • 时间字符串格式化
   /*
      * 服务器获取时间字符串格式
      * 2020-11-17 09:00:00
      * 2020/11/17 09:00:00
      * 想要转化为的格式
      * 2020年08月13日
      * 08月13日 09时00分
    */

    function formatTime1(template) {
      // 1.获取字符串中年月日时间
      let timeArr = this.match(/\d+/g)
      template = template || "{0}年{1}月{2}日{3}时{4}分{5}秒"
      template = template.replace(/\{(\d+)\}/g, (content, $1) => {
        let time = timeArr[$1] || '00'
        // 补0
        function padZero(str) {
          return ('00' + str).substr(str.length)
        }
        time = padZero(time)
        return time
      })
      return template
    }
    // 简化代码
    function formatTime(template = "{0}年{1}月{2}日{3}时{4}分{5}秒") {
      let timeArr = this.match(/\d+/g)
      // 直接解构赋值
      return template.replace(/\{(\d+)\}/g, (...[,$1]) => {
        let time = timeArr[$1] || '00'
        return time.length < 2 ? '0' + time : time 
      })
    }

    // 循环扩展原型方法
    ['formatTime'].forEach(item => {
      String.prototype[item] = eval(item)
    })
    let time = '2020-11-17 09:0:00'
    console.log(time.formatTime())
    console.log(time.formatTime('{3}时{4}分{5}秒'))
  • 简单url拆分
    function queryParams(url) {
      let obj = {}
      let reg = /([^?=&#]+)=([^?=&#]+)/g
      url.replace(reg, (...[, $1, $2]) => obj[$1] = $2)
      url.replace(/#([^$=?]+)/, (...[, $1]) => obj.hash = $1)
      return obj
    }
    console.log(queryParams('www.baidu.com?123=456&id=888#box'))
  • 千分符
function millionmeter(num) {
      num += ''
      num = num.split('').reverse().join('')
      for (let i = 3; i < num.length; i += 4) {
        let preV = num.substring(0, i),
          next = num.substring(i)
        num = preV + ',' + next
      }
      return num.split('').reverse().join('')
    }
    let num = 1151781932
    console.log(millionmeter(num))


    function millionmeter1(num) {
      num += ''
      num = num.split('').reverse()
      for (let i = 3; i < num.length; i += 4) {
        num.splice(i,0,',')
      }
      // reverse 不对原数组产生影响
      return num.reverse().join('')
    }

    console.log(millionmeter1(num))

    // 思想:从结尾开始进行前项预查 每三个数字一组,当数字不是三个就进行前一个匹配条件 捕获并且添加逗号 之后剩余所有分组都符合三个一组条件
    function millionmeter2(num) {
      num += ''
      let reg = /\d{1,3}(?=(\d{3})+$)/g
      return num = num.replace(reg,content => content + ',')
      
    }
    console.log(millionmeter2(num))
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • 最近在学PHP,js时需要用到正则表达式,尽管在学Python时学了一些但是不够系统。每次看到繁琐的正则,就浅尝辄...
    zjbao123阅读 468评论 0 2
  • [TOC] 什么是正则表达式? 正则表达式是一组由字母和符号组成的特殊文本, 它可以用来从文本中找出满足你想要的格...
    Selen_Lin阅读 565评论 0 0
  • 什么是正则表达式? 正则表达式是一组由字母和符号组成的特殊文本, 它可以用来从文本中找出满足你想要的格式的句子. ...
    l_genius阅读 574评论 0 0
  • 写在最前面的话 最近在浏览Github Star榜时发现了关于学习正则表达式的介绍,感觉非常不错,可以边看边学边练...
    王诗翔阅读 2,590评论 1 21
  • 以前在程序中登陆或者校验也会经常用正则,但是一般都是去网上找现成的,最近研究了一下,还是发现很博大精深的,而且,熟...
    mymdeep阅读 393评论 0 7