正则手记_本人自学实践的一部分笔记+个人粗浅的理解
注:正则表达式,不是程序员的专属工具,我等非程序员也可以拿来提高工作效率。以下内容主要为读书笔记,加上一些自己的理解,通过配图,应该有助于理解。
正文:
正则,中文全称:“正则表达式”;英文全称:Regular Expressions,简称:Regex。
定义:简单说,它就是一个字符串,是一种特殊形式的文本模式。
优点:具有超强结构描述能力的形式化表达方法。
怎么个超强?
谁需要用正则表达式:程序员,如果一个程序员(本人不是),不懂正则表达式,效率就会大打折扣。
我不是程序员,正则对我有用吗?答案是:YES!
可以这样说,不会正则的程序员不是合格的程序员;不会正则的文本处理者也不大可能是一个高效的文本工作者。
“我不是程序员,工作的主要内容也不是大规模的文本处理,平时工作就用一下excel,还需要正则吗?”答案还是:YES!
既然正则这么好用,
那还等什么?!开始学!!
首先,学习正则的书籍有哪些?
《正则表达式必知必会》(Ben Forta,翻译:杨涛等)_入门级,菜鸟的选择;
《正则指引》(作者:余晟)_提升级,概念梳理的非常好!内容实在!
《精通正则表达式》(作者:Jeffrey E.F. Friedl;翻译:余晟)_精进级,必读!非程序员,至少需要阅读前4章,最好吃透前6章;
《正则表达式经典实例》(Jan Goyvaerts、Steven Levitban;翻译:郭耀,迟骋 审校:余晟)_“快餐级”。可以看作是学习正则后的速查手册。
Windows系统下常见的支持正则表达式的软件(实际上有很多,不一一举):
Notepad++:免费;
Emeditor:收费,网上容易找到;
Editpad:专业版收费,新版网上不容易找到;此为本人最爱。可以用它的lite版,免费的;
作者:Jan Goyvaerts,他公司还有如下优秀软件:
Regexbuddy、Powergrep、Acetext等,其中,Regexbuddy是学习正则表达式的绝佳帮手!
上面的软件,正则功能非常强大,但是没有像Word那样的文字处理功能。那有没有像word那样具备文字处理能力,又支持正则功能的软件?
有!就是libreoffice!它的文字处理以及电子表格都是支持正则表达式的,可谓两全其美。它还有其他优势:开元免费而且不带广告,还是多国语言的,还可以安装到U盘,随身携带使用。实在太“帅”了!
上面讲的是基本的概念、正则相关的书籍和可能用到的软件。下面上“干货”。
正则基本内容:
1.元字符(12个)
2.字符组(匹配单个字符)
3.排除型字符组
4.量词:?、+、*、{m,n}及变体
5.多选结构
6.分组及反向引用
7.断言(精确匹配)
8.环视(位置匹配)
9.回溯及正则应用原理(重点和难点)
--------------------------割一下-----------------------------------
元字符(12个)
括号类:(、 )、 [、 { (4个)
量词类:?、+、 * 、. (4个)
其他类:^、$、 \、 | (4个)
知识点:1.元字符是构建正则引擎(Regex Engine)的重要部件;可以这样说,没有元字符,就没有正则表达式;完整的正则表达式就是两部分构成:元字符+普通文本字符;2.元字符本身具有特殊含义;3.元字符本身的匹配需要进行转义(escape)__在元字符前加斜杠\,比如:\(,或者\.或者\\等等;注意:这些元字符,如果放入[ ]中,它们就不再是元字符,而是普通的字符。
字符组(匹配单个字符)
常见的字符组(字符之间是“或”的关系)及简写(注意大小写):
[ \f\n\r\t\v]=\s,方括号里的第一字符是空格\f是换页符,\n是换行符,\r是回车符,\t是制表符(Tab符),\v是垂直制表符;
[0-9]=\d:匹配从0-9的单个数字字符,书写顺序只能是0-9,不能是9-0,而且必须放[ ]括号内;比如,匹配10位长度的数字,可以写成[0-9]{10},等价于\d{10};{10}是量词限制,后面讲;
[a-z]或者[A-Z]:前者匹配从a到z的小写字母字符,后者匹配从A到Z的大写字母字符,同样要注意顺序,不能倒过来写;
匹配单个中文字符:[\u4e00-\u9fff]。匹配多个中文字符,用量词。
而且,如果不需要区别大小写时,可以用修饰符(?i)来打开“不区分大小写”功能,用(?-i)来关闭“不区分大小写功能”,也就是打开区分大小写功能。Q:gr[ae]y可以匹配的单词?A:可以匹配gray和grey。
简写:常用的简写有\d,\w,\s。
\d和\D互补:\d匹配单个数字字符,那\D就不能匹配数字字符;
\w和\W互补:\w匹配可以单个数字,字符(大小写),下划线,中文字符等,\W就不能匹配它们;
\s和\S互补:\s匹配单个空格,\S就不能匹配空格,而是匹配单个非空格字符。
排除型字符组(排除单个字符)
[^…]:表示“匹配一个未列出的字符(match a character that’s not listed)”而非“不要匹配列出的字符(Don’t match what is listed)”。这两句话的区别在于:
前者强调:一个字符组,即使是排除型字符组,也需要匹配一个字符,匹配一个除了“排除型字符组中所有字符”以外的字符。
后者暗示:这里不出现任何字符也可以。
量词:?、+、*、{m,n}及变体
首先要明确:量词只作用于前面的元素。常见量词范围如下(取自余晟老师的正则指引,非商用!):
匹配优先量词、忽略优先量词
匹配优先量词:上表中的?、+、*、{m,n}及其变体都是匹配优先的;
忽略优先量词:??、+?、*?是忽略优先,就是在?、+、*后加?后,匹配优先量词就成了忽略优先量词。
匹配优先、忽略优先在后面的回溯及匹配原理部分,会显得十分重要。
多选结构
多选结构额样式:(...|...|…),括号内竖线|分割多个子表达式(多选分支)。
匹配:优先选择最左的匹配结果,所以要注意多选结构的排列顺序。
如下图:
注意事项:
多选结构的级别很低(效率问题),能用字符组解决的问题,最好不要用多选结构;
尽量避免多选结构中出现重复匹配,比如:(中国人|中国)
分组及反向引用
分组:使用括号(...)来分组,这样的括号交捕获型括号;
反向引用:就是把捕获分组匹配出来的文本重复使用;格式\1、\2等,有的软件不支持\1、\2,但是一般支持$1、$2这样的格式;
分组的两种可能:顺序分组、嵌套
顺序分组:从左到右按数字排列就好,比如正则表达式(regex1)(regex1)分组后,使用\1会引用regex1捕获的匹配,而\2会引用regex2捕获的匹配;
嵌套:无论如何嵌套,分组的编号都是根据开括号出现的顺序来计数的,规则如下(取自余晟老师的《正则指引》,非商用!):
非捕获型分组:
想分组又不捕获(方便阅读、提高效率),怎么办?用(?:...)来实现;
具有现实意义的例子:公司员工邮箱由名字和邮箱地址组成,格式如下:
yb.l<yb.l@abc-group.com>
现要将他们的名字和邮箱地址分开,用正则是最好的解决方法。有人说,我可以一个一个拷贝,粘贴,这个当然可以,一个两个你可以这样整,一百个,一千个,你得来回折腾多少次?而用正则,两次拷贝、粘贴就搞定。
详见后面的截图。。。
断言(精确匹配)
常见的几种断言:
^:匹配行开头。比如:^[aeiou]\w+表示以字母a、e、i、o、u开头的单词,bag没有被匹配到。
$:匹配行结尾.比如:\w+[aeiou]$表示以字母a、e、i、o、u结尾的单词,只有idea可以被匹配到。
\b:单词边界,
比如:\cat\只能匹配到cat这个单词,包含cat的无法匹配。
环视:四种环视结构,它只匹配位置,不匹配字符,相当于一个条件限制。
一个简单的方法就是:把自己想象成被匹配到的字符,然后“你站在那里向右(顺序环视)或者向左(逆序环视)看,是需要满足什么或排除什么”,这里的“什么”是regex匹配的内容。
1.顺序肯定环视(?=regex):意思是从左向右“看”,后面需要出现满足括号内正则表达式(regex)要求的匹配;
比如下面的例子:
2.顺序否定环视(?!regex):意思是从左向右“看”,后面不能出现括号内正则表达式(regex)要求的匹配;
如下面的例子
3.逆向肯定环视(?<=regex):意思是从右向左“看”,前面需要出现满足括号内正则表达式(regex)要求的匹配;
比如下面的例子:
4.逆向否定环视(?<!regex):意思是从右向左“看”,前面不能出现满足括号内正则表达式(regex)要求的匹配;
回溯及正则应用原理(重点和难点)
原理举例:
文本:hot tonic tonight
正则:to(nite|knight|night)
首先,把正则中的每一个字符引擎想象成一个放大镜。然后是用这个放大镜去比对待匹配文本的字符。
然后,从最左边开始,正则引擎t无法匹配文本中的h,引擎在文本中继续前进,来到文本中的字母o,不匹配。继续到文本中的字母t,匹配。然后正则引擎来到o,它无法跟文本中hot的t(前面已经被匹配)后面的空格完成匹配。
显然,本轮匹配失败。
再来,这次轮到tonite引擎了。如上面的过程,toni匹配后,正则引擎的t无法匹配文本的c,匹配失败。
。。。
如此反复,正则引擎的所有选项会尝试去逐一匹配文本,直到正则引擎tonight完全匹配到文本的tonight,匹配成功!
其实,正则表达式是靠正则引擎驱动的,而正则引擎可以分为如下几类:
传统型NFA引擎:功能强大,支持回溯和反向引用;
POSIXNFA引擎:符合标准;
DFA引擎:不一定符合POSIX,速度快,没有回溯和反向引用功能。
NFA和DFA的比较:
1.DFA不支持忽略优先量词(懒惰量词),NFA则支持;
2.DFA不支持捕获型括号、反向引用和回溯,NFA则支持;(NFA最重要的部分:回溯<backtracking>)
3.DFA是文本主导的(text-directed),而NFA是正则主导的(regex directed);4.DFA匹配速度比NFA要快。
一般地,我们使用的软件绝大多数支持传统型NFA,回溯和反向引用的功能非常吸引人。
NFA引擎的测试:
1.被匹配字符:nfa not
2.正则引擎(正则表达式):nfa|nfa not
3.如果仅能匹配到nfa,就是传统型NFA;
4.如果匹配到nfanot,就是POSIXNFA或者DFA。
我们这里只关注传统型NFA。
NFA引擎最重要的性质:
1.它会依次处理各个子表达式或组成元素;
2.遇到需要在两个可能成功的可能中进行选择的时候,它会选择其一,同时记住另一个,以备稍后可能的需要;
3.需要做出选择的情形:量词(决定是否尝试另一次匹配)、多选结构(决定选择哪个多选分支,留下哪个稍后尝试);
回溯的两个要点:
1.如果需要在“进行尝试”和“跳过尝试”之间选择:对于匹配优先量词(贪婪量词),引擎会优先选择“进行尝试”,而对于忽略优先量词(懒惰量词),会选择“跳过尝试”。
2.LIFO(后进先出)原则。
所谓回溯,就是在匹配的位置,正则引擎为了满足其他匹配而不得不进行的字符释放,顺序是从右往左回退,所以叫“回溯”,我叫它“回吐”,把前面“吃到的”字符“吐”一部分出来。
下面用这张图(取自《精通正则表达式》,非商用)开始回溯的讲解之旅:
文本:The name "McDonald’s" is said "makudonarudo" in Japanese
正则表达式:".*"
上面的过程是这样的:
首先匹配到位置A的双引号,然后点(.)可以匹配任意字符,后面的量词(*)告诉点(.)需要尽可能多的匹配,所以它一下子匹配到了末尾的位置B;
此时,正则引擎来到了上面正则表达式中的红色双引号("),它也要被匹配。怎么办,前面的.*已经把正则引擎推到了位置B,现在为了匹配红色双引号,需要从位置B往后(左)退,一直退到上图中带有红色圈的双引号位置,整个匹配成功。如下图:
这时有人问了:我只想匹配双引号内的字符或字符串,怎么办?
答:使用忽略优先量词(又叫懒惰匹配)或者排除型的优先匹配(又叫贪婪匹配)。
同样的例子,如下,注意正则表达式的写法。
懒惰匹配过程:
Step1 首先匹配".*?"中最左边的双引号;
Step2 然后匹配.*?,因为是懒惰匹配,它直接跳过;
Step3引擎的控制权交给后面的红色双引号"。而此时文本中引擎的位置还停留在第一个双引号后面的字母M的位置,显然,M和正则引擎后面的红色双引号无法匹配,怎么办?
Step4 此时正则引擎控制权交还给点.,而点.可以匹配字母M,然后选择,因为是懒惰匹配*?,又跳过,来到红色的双引号位置,此时又无法匹配,于是重复以上步骤,直到双引号内的内容被全部匹配完成;
Step5 整个过程,引擎的控制权在来回交替。
例外一种方法:排除型的优先匹配
其他使用环视来匹配的例子:
...<B>Billionsand <B>Zillions</B>of suns…, 这里<B>Zillions</B>为期望匹配内容。
下面的方法显然无法实现:
怎么办?可以尝试环视。
具体的操作如下,需要仔细琢磨才能理解。
工作中和excel联手的几个常见应用
1.分离邮箱地址中的人名和地址,然后在单独拷贝到excel中,第一列为人名,第二列就是邮箱地址了。
提取人名(图2.因为窗口大小限制,人名没有完全显示,后同):
邮箱地址一次性提取如下:
不管多少人名,邮箱,两次拷贝、粘贴搞定!
数据提取、合并
提取的例子:黄色部分是从第3列分离出来的。
正则参考如下:
合并的例子:
写在最后:
正则,一个高效工具,用电脑的人都应该学学;同时记住:实现同一个匹配,正则的写法不止一种;
再次推荐:《精通正则表达式》。
祝好!