正则表达式基础
前段时间阅读Underscore源码的时候,踩了不少正则表达式的坑。正则表达式是一项非常实用的技能,可是每次遇到正则相关的问题,我都是回避(总以为正则表达式在网上搜搜就好了。同时,又觉得这玩意儿难,学习成本比较大)。看了司徒正美的一篇关于正则表达式的文章后,慢慢觉得正则并不是想象中那么难,关键是理解正则的核心思想后,多思考、多实践。本篇博文中的正则相关测试主要针对javascript!
工具推荐:可视化正则工具
元字符
元字符是构建正则表达式的符号(用于连接字母和数字,创建高度描述性的文本模式)。常用的元字符有:( [ { \ ^ $ | ) ? * + .
如果要在正则中匹配这些元字符,需要加转义符\
// 匹配`?`var reg = /\?/; reg.test('?123'); // true
来自MSDN的元字符的完整列表以及它们在正则表达式上下文中的行为:
特殊字符
预定义的一些特殊字符:
\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
思维导图
参考
建议继续学习:
- 统计最近用过的linux命令 (阅读:5245)
- grep 正则表达式选项要记得转义 (阅读:5103)
- 正则表达式的与或非 (阅读:4594)
- 学习Grep,Sed中的正则 (阅读:3914)
- URL正则表达式 (阅读:3483)
- 正则表达式简要入门 (阅读:3374)
- PHP 正则里面的两个重要技巧 (阅读:3375)
- 正则表达式简介及使用 (阅读:3194)
- 正则转义符汇总 (阅读:3203)
- 正则表达式傻瓜书 第二章:元字符 (阅读:3087)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:chenjun 来源: 风影博客
- 标签: 正则
- 发布时间:2015-06-01 10:03:59
- [43] IOS安全–浅谈关于IOS加固的几种方法
- [43] 如何拿下简短的域名
- [42] Oracle MTS模式下 进程地址与会话信
- [42] 图书馆的世界纪录
- [41] 界面设计速成
- [39] 【社会化设计】自我(self)部分――欢迎区
- [39] android 开发入门
- [37] 读书笔记-壹百度:百度十年千倍的29条法则
- [36] 视觉调整-设计师 vs. 逻辑
- [33] Go Reflect 性能