5

正则表达式基础

 2 years ago
source link: https://blogread.cn/it/article/7482?f=hot1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

正则表达式基础

浏览:4374次  出处信息

前段时间阅读Underscore源码的时候,踩了不少正则表达式的坑。正则表达式是一项非常实用的技能,可是每次遇到正则相关的问题,我都是回避(总以为正则表达式在网上搜搜就好了。同时,又觉得这玩意儿难,学习成本比较大)。看了司徒正美的一篇关于正则表达式的文章后,慢慢觉得正则并不是想象中那么难,关键是理解正则的核心思想后,多思考、多实践。本篇博文中的正则相关测试主要针对javascript

工具推荐:可视化正则工具

元字符是构建正则表达式的符号(用于连接字母和数字,创建高度描述性的文本模式)。常用的元字符有:( [ { \ ^ $ | ) ? * + .

如果要在正则中匹配这些元字符,需要加转义符\

// 匹配`?`var reg = /\?/;    reg.test('?123'); // true

来自MSDN的元字符的完整列表以及它们在正则表达式上下文中的行为:

regex table

预定义的一些特殊字符:

  • \t:制表符

  • \v:垂直制表符

  • \n:换行符

  • \r:回车符

  • \f:换页符

  • \a:alert字符

  • \e:escape字符

  • \0:空字符

简单类

原则上正则的一个字符对应一个字符,我们可以用[]把它们括起来,让[]这个整体对应一个字符。如:

/js/.test('js');  // true    /[js]/.test('j'); // true// 结果返回一个匹配数组:["bat", "Cat", "fAt", "bat", "faT", "cat"]// 注意:如果正则中没有加入参数g,则返回第一个匹配console.log("a bat ,a Cat,a fAt bat ,a faT cat".match(/[bcf]at/gi));

负向类

[]中首字符设为^,表示匹配不能为括号里面的字符。如:

/[^abc]/.test("a"); // true   /[^abc]/.test("js"); //true

范围类

有时匹配的东西过多且类型又相同,全部输入太麻烦。可以利用[],同类字符之间加一个-即可。如:

/[a-z]/.test('x'); //true/[0-9]/.test('3'); //true

组合类

用[]匹配不同类型的单个字符。如:

/[a-z0-9]/.test('b'); // true   /[a-g0-5]/.test('z'); // false    /[^7-9]/.test("6"); // false

预定义类

预定义的一些常用字符类,本质上是[]的马甲:

  • .:匹配除了换行和回车之外的任意字符,相当于[^\n\r]

  • \d:匹配数字字符,相当于[0-9]

  • \D:匹配非数字字符,相当于[^0-9]

  • \s:匹配空白字符,相当于[ \t\n\x0B\f\r]

  • \S:匹配非空白字符,相当于[^ \t\n\x0B\f\r]

  • \w:匹配单词字符(所有的字母),相当于[a-zA-Z_0-9]

  • \W:匹配非单词字符,相当于[^a-zA-Z_0-9]

/\d/.test('3'); // true   /\S/.test(' '); // false    /\w/.test('_'); // true    /./.test('骏'); // true

由于元字符与特殊字符或字符类或者它们的组合([])甚至它们的马甲(预定义类)都是一对一进行匹配。如果我们要匹配“正则表达式基础xxx”,最简单都要/..../。因此我们需要一个简单的操作,来处理这数量关系。

简单量词

  • ?:软性量词 出现零次或一次

  • *:软性量词 出现零次或多次(任意次)

  • +:软性量词 出现一次或多次(至少一次)

  • {n}:硬性量词 对应固定n次

  • {n,m}:软性量词 至少出现n次但不超过m次

  • {n,}:软性量词 至少出现n次(+的升级版)

/...../.test('正则表达式'); // true// /[\u4e00-\u9fa5]/用于匹配单个汉字    /[\u4e00-\u9fa5]{5}/.test('正则表达式');  // true    /\d+/.test('123456');  // true    /[js]{2}/.test('abc');  // false

贪婪量词、非贪婪量词、支配性量词

人都是贪婪的,正则也是如此。只要在合法的情况下,正则会尽量地多去匹配字符,这就叫做贪心模式。如果我们希望正则尽量少地匹配字符,只要在表示数字的符号后面加上一个?(即:问号加在量词的后边,则表示非贪婪模式)。组成如下的形式:{n,}?, *?, +?, ??, {m,n}?。上面两种模式都有个不断尝试的过程,而支配性量词却只尝试一次,如果不成功就算了(javascript不支持该模式!)

// 贪婪模式    alert(/c{1,}/.exec('cccccccccc')); // cccccccccc// 非贪婪模式    alert(/c{1,}?/.exec('cccccccccc')); // c

到目前为止,我们只能一个字符的匹配。虽然量词的出现,能帮助我们处理一排密紧密相连的同类型字符,但这是不够的。下面该轮到小括号()出场了,中括号[]表示范围内选择,大括号{}表示重复次数,小括号允许我们重复多个字符。

//分组+量词    /(dog){2}/.test("dogdog"); // true//分组+范围    alert("baddad".match(/([bd]ad)*/)); //baddad,dad//分组+分组    alert("mon and dad".match(/(mon( and dad)?)/)); //mon and dad,mon and dad, and dad// ja是整个正则匹配的内容,j是第一个括号子正则表达式匹配的内容,a是第二个括号子正则表达式匹配的内容    alert(/(\w)(\w)/.exec('javascript')); // ja,j,a

反向引用标识正则表达式中的匹配组捕获的子字符串。每个反向引用都由一个编号或名称来标识,并通过“\编号”表示法在正则规则中进行引用

var color = "#990000";  /#(\d+)/.test(color);  alert(RegExp.$1); // 990000// '\1'就是一个反向引用,它表示的是第一个括号内的子正则表达式匹配的内容  alert(/(dog)\1/.test("dogdog")); //truevar num = "1234 5678";  var newNum = num.replace(/(\d{4}) (\d{4})/,"$2 $1");  alert(newNum); // 5678 1234

元字符|相当于正则表达式中的或,有一个候选项满足,就停止其它候选项匹配

var reg = /red|black|yellow/;    reg.test('red'); // true    reg.test('yellow'); // true    /^z|o.+/.exec('online'); // online    /^z|o.+/.exec('zhongguo'); // z    /(\d{6})|(abc*)/.test('abccc'); // true    /(\d{6})|(abc*)/.test('123456'); // true

非捕获性分组

并不是所有分组都能创建反向引用,有一种特别的分组称之为非捕获性分组,它不会创建反向引用,即可以避免保存括号内的匹配结果。反之,就是捕获性分组。要创建一个非捕获性分组,只要在分组的左括号的后面紧跟一个问号与冒号就行了((?:pattern))

// 捕获性分组   /^(b|c)\1/.exec('bbs.byr.cn'); // bb,b// 非捕获性分组    /^(?:b|c)\1/.exec('bbs.byr.cn'); // null    /^(?:b|c)/.exec('bbs.byr.cn'); // b// 移除所有标签,只留下innerTextvar html = "<p><a href='http://chenjunxyf.me/'>风影博客</a><em>chenjun</em></p>";    var text = html.replace(/<(?:.|\s)*?>/g,"");    console.log(text); //  风影博客chenjun

注意:javascript不存在命名分组

正向前瞻

形式:(?=pattern)所谓正向前瞻,意思就是:要匹配的字符串,后面必须紧跟着pattern

   /chenjun(?=33)/.test('chenjun33'); // true    alert(/chenjun(?=33)/.exec('chenjun33')); // chenjun    /chenjun(?=33)/.test('chenjun233'); // false    alert(/chenjun(?=33)/.exec('chenjun233')); // null

负向前瞻

形式(?!pattern)和?=恰好相反,要求字符串的后面不能紧跟着某个pattern

   /chenjun(?!css)/.test('chenjuncss'); // false    alert(/chenjun(?!css)/.exec('chenjuncss')); // null    /chenjun(?!css)/.test('chenjun233'); // true    alert(/chenjun(?!css)/.exec('chenjun233')); // chenjun// 移除hr以外的所有标签,只留下innerTextvar html = "<p><a href='http://chenjunxyf.me/'>风影博客</a></p><hr/><p><em>chenjun</em></p>";    var text = html.replace(/<(?!hr)(?:.|\s)*?>/gi,"");    console.log(text); //  风影博客<hr/>chenjun

要与字符串合用的特殊字符,前面基本都用到了!

  • \b:单词边界([a-zA-Z_0-9]之外的字符)

  • \B:非单词边界

// 利用正则实现首字母大写var a = 'chenjun';    String.prototype.capitalize = function(){      returnthis.replace(/^\w/, function(s){        return s.toUpperCase();      });    };    console.log(a.capitalize()); // Chenjun// 单词提取var str = "12w-eefd&efrew";    alert(str.match(/\b\w+\b/g)); //12w,eefd,efrew

regex mind map

建议继续学习:

QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK