曾记得几年前刚接触到VIM
编辑器的时候,对这玩意儿敬而远之。觉得这东西好复杂,我要写个程序得花费这么多时间来搞定编辑器,好累,非常不适合我这种急功近利
型的程序员。几番折腾后看到了耗子叔叔的这篇vim练级攻略。跟着练习了几个星期,加上反复的实战。现在VIM
成为了我唯一的编辑器。受这篇文章的启发决定来整个正则表达式
的练级攻略。同样这里更加关注的是正则的实战部分,即利用它来解决你手头的问题,所见即所得。因为我觉得只有你足够熟悉一个东西你才会有兴趣了解其背后的思想原理。
整个攻略分为4个部分,基础部分包含了一般程序员会用到的百分之80的功能,接下来是只有少数的程序员能在日常工程运用到高级部分的技巧,其次介绍了只有极少数人能够真正掌握正则的超能力(反正我还没有)。最后会讲在js里面运用正则的一些实践。为了方便操作示例主要使用javascript
提供的一些方法来完成测试。
存活下来
顾名思义,会了这些技巧之后就可以利用正则来解决一些日常问题了:)
- 匹配纯文本 @H_301_26@ 找出
- var str = 'Hello my name is LeBron leBron james';
- str.match(/LeBron/);
- //如果正则表达式没有 g 标志,返回和 RegExp.exec(str) 相同的结果
- [ 'LeBron',index: 17,input: 'Hello my name is LeBron leBron james' ]
- var str = 'Hello my name is LeBron leBron james';
- str.match(/LeBron/ig);
- [ 'LeBron','leBron' ]
- 匹配任意字符 @H_301_26@
- var str = 'Hello my name is LeBron leBron james';
- str.match(/.eBron/g);
- [ 'LeBron','leBron' ]
- 匹配特殊字符 @H_301_26@ 正则里面元字符有特殊含义,匹配它本身需要转义
- var str = 'Hello my name is LeBron.james KingJames';
- str.match(/\.james/g);
- [ '.james' ]
- 匹配一组字符 @H_301_26@ 匹配多个字符中的某一个
- var str = 'Hello my name is LeBron leBron james';
- str.match(/[lL]eBron/g);
- [ 'LeBron','leBron' ]
- 利用字符集合区间 @H_301_26@ 常见的有数字和字符区间
- var str = 'a1 a2 b1 b2 abc a9 b7 a5';
- str.match(/a[1-5]/g);
- [ 'a1','a2','a5' ]
- var str = 'a1 a2 b1 b2 abc a9 b7 a5';
- str.match(/a[^1-5]/g);
- [ 'ab','a9' ]
- 常用的元字符 @H_301_26@ 元字符转义匹配
- var str = 'test arr[10]';
- str.match(/arr\[10\]/);
- [ 'arr[10]',index: 5,input: 'test arr[10]' ]
- var str = 'test arr[10]';
- str.match(/arr\[\d\d\]/);
- [ 'arr[10]',input: 'test arr[10]' ]
- var str = 'test arr[10]';
- str.match(/\w\w\w\[\w\w\]/);
- [ 'arr[10]',index: 5,input: 'test arr[10]' ]
- var str = 'test arr[10]';
- str.match(/t\sa/);
- [ 't a',index: 3,input: 'test arr[10]' ]
- 重复匹配 @H_301_26@ 匹配一次或者多次
- var str = 'dasdhld james@163.com hehe@qq.com dasdals.dasd@gmail.com __abc@linchen.com';
- str.match(/\w+@\w+\.\w+/g);
- [ 'james@163.com','hehe@qq.com','dasd@gmail.com','__abc@linchen.com' ]
- str.match(/[\w.]+@[\w.]+\.\w+/g);
- [ 'james@163.com','dasdals.dasd@gmail.com','__abc@linchen.com' ]
- var str = '4/8/03 10-6-2004 2/2/2 01-01-01';
- str.match(/\d{1,2}[-\/]\d{1,2}[-\/]\d{2,4}/g);
- [ '4/8/03','10-6-2004','01-01-01' ]
- var str = '<H1>nihaoH1</H1> <H1>nihao dupH1</H1>';
- str.match(/<[hH]1>.*<\/[hH]1>/g);
- [ '<H1>nihaoH1</H1> <H1>nihao dupH1</H1>' ]
- str.match(/<[hH]1>.*?<\/[hH]1>/g);
- [ '<H1>nihaoH1</H1>','<H1>nihao dupH1</H1>' ]
LeBron
字符串
@H_301_26@
匹配多个结果并且忽略大小写
.
可以表示除了换行符外的任意字符
记住匹配元字符本身需要转义
元字符
[]
用来定义一个字符集和,字符集合的匹配结果是能与该集合里的任意一个成员相匹配的文本。
@H_301_26@
取非匹配
连字符-
是一个特殊字符只在[]里面是元字符,在字符集合外面是普通字符。取非匹配^
也是一个元字符。
@H_301_26@
匹配空白字符
常见的空白符包含换页换行符、制表符等。@H_301_26@
匹配数字
@H_301_26@ 匹配字符(字母数字下划线)
\d
表示数字,\D
表示非数字。
@H_301_26@\w
表示字符,\W
表示非字符。
匹配空白字符
@H_301_26@
\s
表示字符,\S
表示非字符。
特殊进制数字
十六进制以\x为前缀,八进制以\0为前缀。
第三个邮箱地址匹配不够完整
匹配任意次数(*)、匹配0次或者1次(?)用法同
+
。
@H_301_26@
匹配指定次数
@H_301_26@匹配至少重复多少次:
{n,}
防止过渡匹配
更快、更好、更强
- 位置匹配 @H_301_26@ 单词边界
- var str = 'hello he hell';
- str.match(/\bhe\b/);
- [ 'he',index: 6,input: 'hello he hell' ]
- var str = 'hello he hell';
- str.match(/^he/);
- [ 'he',index: 0,input: 'hello he hell' ]
- 使用子表达式 @H_301_26@ 匹配一个IP地址
- var str = 'ip = [120.24.163.198]';
- str.match(/(\d{1,3}\.){3}\d{1,3}/g);
- [ '120.24.163.198' ]
- 回溯引用 @H_301_26@
@H_301_26@
字符串边界
^
定义字符串的起始位置,$
定义字符串的结束位置。
子表达式是一个独立的整体,子表达式必须用括号()括起来。
回溯引用解决了什么问题:
- var str = '<h1>hi label</h1> <h2>h2 label</h2> <h2>h2 error label</h3>';
- str.match(/<[hH][1-9]>.*?<\/[hH][1-9]>/g)
- [ '<h1>hi label</h1>',
- '<h2>h2 label</h2>',
- '<h2>h2 error label</h3>' ]
很明显第三个结果是一个错误的标签,回溯引用就是为了解决这个问题。
- var str = '<h1>hi label</h1> <h2>h2 label</h2> <h2>h2 error label</h3>';
- str.match(/<[hH]([1-9])>.*?<\/[hH]\1>/g)
- [ '<h1>hi label</h1>','<h2>h2 label</h2>' ]
javascript 里面用
\
+数字来引用子表达式。数字由子表达式出现的顺序决定。
- 回溯替换 @H_301_26@
- var str = 'Hello,kingJames@LeBron.com is my email address';
- str.replace(/\w+[\w.]*@[\w.]+\.\w+/g,"hello@world.com");
- 'Hello,hello@world.com is my email address'
javascript 实现正则替换要通过String对象的replace
函数来完成
正则超能力
前后查找
我们需要这样一个模式,它包含的匹配本身不会被返回,而是用于确定正确的匹配位置,它并不是匹配结果的一部分。
- 向前查找(?=) @H_301_26@
- //文本
- Hello my name is LeBron!
- http://www.baidu.com
- https://www.sina.com.cn
- ftp://192.168.0.1
-
- //reg
- grep -P '.+(?=:)' ./x.js
-
- //output
- `http`://www.baidu.com
- `https`://www.sina.com.cn
- `ftp`://192.168.0.1
被匹配到的
:
并没有出现在最终的结果里。javascript
只支持向前查找.
嵌入条件
为什么要嵌入条件?一些正则表达式后面的判断必须基于前面的判断结果来进行。
- 回溯引用条件 @H_301_26@
- var str = '<a href="/home"><img src="/images/home.gif"></a> <img src="/images/aboutme.gif">';
-
- str.match(/(<[aA]%s+[^>]+)?<[iI][mM][gG]\s+[^>]+>(?(1)\s*</[Aa]>)/);
Javascript与正则的结合
javascript
语言提供了以下几个方法实现了正则表达式的处理:
1.exec(RegEx)
Demo:
- var reg = /ab[cx]/g;
- var str = 'abcdefabx';
- var myArr;
- while((myArr = reg.exec(str)) !== null) {
- var msg = 'Found ' + myArr[0] + '.';
- msg += 'Next match starts at ' + reg.lastIndex;
- console.log(msg);
- console.log('myArr:',myArr);
- console.log('reg:',reg);
- console.log('----------------------');
- }
output:
- [luncher@localhost regex]$ node x.js
- Found abc.Next match starts at 3
- myArr: [ 'abc',index: 0,input: 'abcdefabx' ]
- reg: /ab[cx]/g
- ---------------------- Found abx.Next match starts at 9 myArr: [ 'abx',index: 6,input: 'abcdefabx' ] reg: /ab[cx]/g ----------------------
启用分组捕获
- var reg = /(ab)[cx]/g;
- var str = 'abcdefabx';
- var myArr;
- while((myArr = reg.exec(str)) !== null) {
- var msg = 'Found ' + myArr[0] + '.';
- msg += 'Next match starts at ' + reg.lastIndex;
- console.log(msg);
- console.log('myArr:',reg);
- console.log('----------------------');
- }
output
- [luncher@localhost regex]$ node x.js
- Found abc.Next match starts at 3
- myArr: [ 'abc','ab',input: 'abcdefabx' ]
- reg: /(ab)[cx]/g
- ----------------------
- Found abx.Next match starts at 9
- myArr: [ 'abx',input: 'abcdefabx' ]
- reg: /(ab)[cx]/g
- ----------------------
当正则表达式使用 “g” 标志时,可以多次执行 exec 方法来查找同一个字符串中的成功匹配。当你这样做时,查找将从正则表达式的 lastIndex 属性指定的位置开始。reg字面量对象还有其他的一些属性,譬如说查看是否启用了多行匹配、是否忽略大小写、时候全局匹配等。
2.test(RegEx)
test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false。
Demo
- > /https?/.test('https');
- true
-
- > /https?/.test('http');
- true
-
- > /https?/.test('ftp');
- false
3.match(String)
如果正则表达式没有 g 标志,返回和 RegExp.exec(str) 相同的结果。如果正则表达式包含 g 标志,则该方法返回一个包含所有匹配结果的数组。如果没有匹配到,则返回 null。
Demo
- > var str = 'hi my name is leBron James or KingLeBron';
- > str.match(/[lL]eBron\s?/);
- [ 'leBron ',index: 14,input: 'hi my name is leBron James or KingLeBron' ]
- > str.match(/[lL]eBron\s?/g);
- [ 'leBron ','LeBron' ]
4.search(String)
如果匹配成功,则 search() 返回正则表达式在字符串中首次匹配项的索引。否则,返回 -1。
Demo
- [luncher@localhost regex]$ node
- > var str = 'ni hao';
- > str.search(/[ii]/);
- 1
- > str.search(/[xX]/);
- -1
5.split(String)
split() 方法通过把字符串分割成子字符串来把一个 String 对象分割成一个字符串数组。
Demo
- > var str = '{k1: 1,k2: 2,k3: 3}';
- > str.split(/[{}]/);
- [ '','k1: 1,k3: 3','' ]
- > str.split(/[{}]/)[1].split(/,/);
- [ 'k1: 1',' k2: 2',' k3: 3' ]
- >
6.replace(String)
replace() 方法使用一个替换值(replacement)替换掉一个匹配模式(pattern)在原字符串中某些或所有的匹配项,并返回替换后的字符串。这个替换模式可以是字符串或者 RegExp(正则表达式),替换值可以是一个字符串或者一个函数。
Demo1
- > var str ='leBron James';
- > str.replace(/(\w+)\s(\w+)/,"$2,$1");
- 'James,leBron'
- >
$2
代表匹配到的字符串james
,$1
代表匹配到的字符串leBron
。
Demo2
- > var str ='leBron James';
- > str.replace(/(\w+)\s(\w+)/,function(match,p1,p2,offset,string) {
- ... return p1.toUpperCase()+p2.toUpperCase();});
- 'LEBRONJAMES'
参考文献:
1 . Javascript标准库
2. 正则表达式必知必会