原文章地址:
EZLippi/practical-programming-booksgithub.com推荐几个正则表达式编辑器
- Debuggex :https://www.debuggex.com/
- PyRegex:http://www.pyregex.com/
- Regexper:http://www.regexper.com/
正则表达式是一种查找以及字符串替换操作。正则表达式在文本编辑器中广泛使用,比如正则表达式被用于:
- 检查文本中是否含有指定的特征词
- 找出文中匹配特征词的位置
- 从文本中提取信息,比如:字符串的子串
- 修改文本
与文本编辑器相似,几乎所有的高级编程语言都支持正则表达式。在这样的语境下,“文本”也就是一个字符串,可以执行的操作都是类似的。一些编程语言(比如Perl,JavaScript)会检查正则表达式的语法。
正则表达式是什么?
正则表达式只是一个字符串。没有长度限制,但是,这样的正则表达式长度往往较短。如下所示是一些正则表达式的例子:
I had a S+ day today[A-Za-z0-9-_]{3,16}dddd-dd-ddv(d+)(.d+)*TotalMessages="(.*?)"<[^<>]>
这些字符串实际上都是微型计算机程序。正则表达式的语法,实际上是一种轻量级、简洁、适用于特定领域的编程语言。记住这一点,那么你就很容易理解下面的事情:
- 每一个正则表达式,都可以分解为一个指令序列,比如“先找到这样的字符,再找到那样的字符,再从中找到一个字符。。。”
- 每一个正则表达式都有输入(文本)和输出(匹配规则的输出,有时是修改后的文本)
- 正则表达式有可能出现语法错误——不是所有的字符串都是正则表达式
- 正则表达式语法很有个性,也可以说很恐怖
- 有时可以通过编译,使得正则表达式执行更快
在实现中,正则表达式还有其他的特点。本文将重点讨论正则表达式的核心语法,在几乎所有的正则表达式中都可以见到这些规则。
特别提示:正则表达式与文件通配语法无关,比如 *.xml
正则表达式的基础语法
字符
正则表达式中包含了一系列的字符,这些字符只能匹配它们本身。有一些被称为“元字符”的特殊字符,可以匹配一些特殊规则。
如下所示的例子中,我用红色标出了元字符。
I had a S+ day today[A-Za-z0-9-_]{3,16}dddd-dd-ddv(d+)(.d+)*TotalMessages="(.*?)"<[^<>]*>
大部分的字符,包括所有的字母和数字字符,是普通字符。也就意味着,它们只能匹配它们自己,如下所示的正则表达式:
cat
意味着,只能匹配一个字符串,以“c”开头,然后是字符“a”,紧跟着是字符“t”的字符串。
到目前为止,正则表达式的功能类似于
String.indexOf()strpos()
注意:不做特殊说明,正则表达式中是区分大小写的。但是,几乎所有正则表达式的实现,都会提供一个Flag用来控制是否区分大小写。
catcotczt这样的字符串,甚至可以找出c.t这样的组合,但是不能找到ct或者是coot这样的字符串。
- 正则表达式c[aeiou]t,表示可以匹配的字符串是”以c开头,接着是aeiou中的任何一个字符,最后以t结尾”。在文本的实际应用中,这样的正则表达式可以匹配:cat,cet,cit,cot,cut五种字符串。
- 正则表达式[0123456789]表示匹配任意一个整数。
- 正则表达式[a]表示匹配单字符a。
包含忽略字符的例子
[][]
在字符类中,字符的重复和出现顺序并不重要。[dabaaabcc]与[abc]是相同的
重要提示:字符类中和字符类外的规则有时不同,一些字符在字符类中是元字符,在字符类外是普通字符。一些字符正好相反。还有一些字符在字符类中和字符类外都是元字符,这要视情况而定!
比如,.表示匹配任意一个字符,而[.]表示匹配一个全角句号。这不是一回事!
字符类的范围
在字符集中,你可以通过使用短横线来表示匹配字母或数字的范围。
- [b-f]与[b,c,d,e,f]相同,都是匹配一个字符”b”或”c”或”d”或”e”或”f”
- [A-Z]与[ABCDEFGHIJKLMNOPQRSTUVWXYZ]相同,都是匹配任意一个大写字母。
- [1-9]与[123456789]相同,都是匹配任意一个非零数字。
[aeiou][aeiou][aeiou][aeiou][aeiou][aeiou]euouaeeuouaes同样的,恐怖的正则表达式[bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz]sulphhydryls
- [0-9.,]表明匹配一个数字,或者一个全角句号,或者一个逗号
- [0-9a-fA-F]意味着匹配一个十六进制数
- [a-zA-Z0-9-]意味着匹配一个字母、数字或者一个短横线
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
- [^a]表示匹配任何不是“a”的字符
- [^a-zA-Z0-9]表示匹配任何不是字母也不是数字的字符
- [^abc]匹配一个为“^”或者a或者b或者c的字符
- [^^]表示匹配任何不为“^”的字符
练习
在字典中,找到一个不满足“在e之前有i,但是没有c”的例子。答案
cie和[^c]ei都要可以找到很多这样的例子,比如ancient,science,viel,weigh
转义字符类
d这个正则表达式与[0-9]作用相同,都是匹配任何一个数字。(要匹配d,应该使用正则表达式d)
w与[0-9A-Za-z]相同,都表示匹配一个数字或字母字符
s意味着匹配一个空字符(空格,制表符,回车或者换行)
另外
- D与[^0-9]相同,表示匹配一个非数字字符。
- W与[^0-9A-Za-z]相同,表示匹配一个非数字同时不是字母的字符。
- S表示匹配一个非空字符。
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]dddd-dd-dd
- 正则表达式a{1}与a意思相同,都表示匹配字母a
- a{3}表示匹配字符串“aaa”
- a{0}表示匹配空字符串。从这个正则表达式本身来看,它毫无意义。如果你对任何文本执行这样的正则表达式,你可以定位到搜索的起始位置,即使文本为空。
- a{2}表示匹配字符串“a{2}”
- 在字符类中,大括号没有特殊含义。[{}]表示匹配一个左边的大括号,或者一个右边的大括号
练习
简化下面的正则表达式
z.......zdddd-dd-dd[aeiou][aeiou][aeiou][aeiou][aeiou][aeiou][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz]
答案
z.{7}zd{4}-d{2}-d{2}[aeiou]{6}[bcdfghjklmnpqrstvwxyz]{10}
注意:重复字符是没有记忆性的,比如[abc]{2}表示先匹配”a或者b或者c”,再匹配”a或者b或者c”,与匹配”aa或者ab或者ac或者ba或者bb或者bc或者ca或者cb或者cc“一样。[abc]{2}并不能表示匹配”aa或者bb或者cc“
指定重复次数范围
重复次数是可以指定范围的
- x{4,4}与x{4}相同
- colou{0,1}r表示匹配colour或者color
- a{3,5}表示匹配aaaaa或者aaaa或者aaa
I had an aaaaawful day会匹配单词aaaaawful中的aaaaa,而不会匹配其中的aaa。
- a{1,}表示匹配一个或一个以上的连续字符a。依然是匹配最长字符串。当找到第一个a之后,正则表达式会尝试匹配尽量多个的连续字母a。
- .{0,}表示匹配任意内容。无论你输入的文本是什么,即使是一个空字符串,这个正则表达式都会成功匹配全文并返回结果。
".{0,}""[^"]{0,}"
- ?*+ 表示匹配字符串”?*+”
- [?*+]表示匹配一个问号,或者一个*号,或者一个加号
练习
简化下列的正则表达式:
".{0,}""[^"]{0,}"x?x?x?y*y*z+z+z+z+
答案
".*""[^"]*"x{0,3}y*z{4,
w+W+w+w+W+w+W+w+w+W+w+W+w+W+w+W+w+W+w+
非贪婪匹配
正则表达式 “.*” 表示匹配双引号,之后是任意内容,之后再匹配一个双引号。注意,其中匹配任意内容也可以是双引号。通常情况下,这并不是很有用。通过在句尾加上一个问号,可以使得字符串重复不再匹配最长字符。
- d{4,5}?表示匹配dddd或者ddddd。也就是和d{4}一样
- colou??r与colou{0,1}r相同,表示找到color或者colour。这与colou?r一样。
- “.*?”表示先匹配一个双引号,然后匹配最少的字符,然后是一个双引号,与上面两个例子不同,这很有用。
选择匹配
你可以使用|来分隔可以匹配的不同选择:
- cat|dog表示匹配”cat”或者”dog”
- red|blue|以及red||blue以及|red|blue都表示匹配red或者blue或者一个空字符串
- a|b|c与[abc]相同
- cat|dog||表示匹配”cat”或者”dog”或者一个分隔符”|“
- [cat|dog]表示匹配a或者c或者d或者g或者o或者t或者一个分隔符“|”
练习
简化下列正则表达式:
s|t|u|v|waa|ab|ba|bb[abc]|[^abc][^ab]|[^bc][ab][ab][ab]?[ab]?
答案
[s-w][ab]{2}.[^b][ab]{2,4}
[1-9]|[12][0-9]|3[01]
- 通过使用 Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day 匹配一周中的某一天
- (w*)ility 与 w*ility 相同。都是匹配一个由”ility”结尾的单词。稍后我们会讲解,为何第一种方法更加有用。
- ()表示匹配一对括号。
- [()]表示匹配任意一个左括号或者一个右括号
(.*).*([^()]*)[^()]*
- (red|blue)表示匹配red或者blue或者是一个空字符串
- abc()def与abcdef相同
你也可以在分组的基础上使用重复:
- (red|blue)?与(red|blue|)相同
- w+(s+w+)表示匹配一个或多个由空格分隔的单词
w+W+w+W+w+w+W+w+W+w+W+w+W+w+W+w+w+(W+w+){2}w+(W+w+){5}
- b表示匹配一个单词分隔符
- bwwwb表示匹配一个三字母单词
- aba表示匹配两个a中间有一个单词分隔符。这个正则表达式永远不会有匹配的字符,无论输入怎样的文本。
单词分隔符本身并不是字符。它们的宽度为0。下列正则表达式的作用不同
(bcat)b(bcatb)b(cat)bb(catb)
练习
在词典中找到最长的单词。答案
在尝试之后发现,b.{45,}b可以在字典中找到最长单词
换行符
一篇文本中可以有一行或多行,行与行之间由换行符分隔,比如:
- Line一行文字
- Line break换行符
- Line一行文字
- Line break换行符
- …
- Line break换行符
- Line一行文字
注意,所有的文本都是以一行结束的,而不是以换行符结束。但是,任意一行都可能为空,包括最后一行。
行的起始位置,是在换行符和下一行首字符之间的空间。考虑到单词分隔符,文本的起始位置也可以当做是首行位置。
最后一行是最后一行的尾字符和换行符之间的空间。考虑到单词分隔符,文本的结束也可以认为是行的结束。
那么新的格式表示如下:
- Start-of-line, line, end-of-line
- Line break
- Start-of-line, line, end-of-line
- Line break
- …
- Line break
- Start-of-line, line, end-of-line
基于上述概念:
^.*&表示匹配全文内容,因为行的开始符号也是一个字符,"."会匹配这个符号。找到单独的一行,可以使用
与字符分隔符一样,换行符也不是字符。它们宽度为0.如下所示的正则表达式作用不同:
(^cat)$(^cat$)^(cat)$^(cat$)
练习
使用正则表达式在《时间机器》中找到最长的一行。答案
使用正则表达式^.{73,}$可以匹配长度为73的一行
文本分界
在很多的正则表达式实现中,将^和$作为文本的开始符号和结束符号。
还有一些实现中,用A和z作为文本的开始和结束符号。
捕捉和替换
从这里开始,正则表达式真正体现出了它的强大。
捕获组
你已经知道了使用括号可以匹配一组符号。使用括号也可以捕获子串。假设正则表达式是一个小型计算机程序,那么捕获子串就是它输出的一部分。
正则表达式(w*)ility表示匹配以ility结尾的词。第一个被捕获的部分是由w*控制的。比如,输入的文本内容中有单词accessibility,那么首先被捕获的部分是accessib。如果输入的文本中有单独的ility,则首先被捕获的是一个空字符串。
你可能会有很多的捕获字符串,它们可能靠得很近。捕获组从左向右编号。也就是只需要对左括号计数。
假设有这样的正则表达式:(w+) had a ((w+) w+)
输入的内容是:I had a nice day
- 捕获组1:I
- 捕获组2:nice day
- 捕获组3:nice
I had a nice day
- 如果输入文本为a,捕获组1为空。
- 如果输入文本为ad,捕获组为d
- 如果输入文本为avocado,捕获组1为v。但是捕获组0表示整个单词avocado.
替换
假如你使用了一个正则表达式去匹配字符串,你可以描述另外一个字符串来替换其中的匹配字符。用来替换的字符串称为替换表达式。它的功能类似于
- 常规的Replace会话
- Java中的String.replace()函数
- PHP的str_replace()函数
- 等等
练习
将《时间机器》中所有的元音字母替换为r。答案
使用正则表达式[aeiou]以及[AEIOU],对应的替换字符串分别为r,R.
但是,你可以在替换表达式中引用捕获组。这是在替换表达式中,你可以唯一操作的地方。这也是非常有效的,因为这样你就不用重构你找到的字符串。
假设你正在尝试将美国风格的日期表示MM/DD/YY替换为ISO 8601日期表示YYYY-MM-DD
- 从正则表达式(dd)/(dd)/(dd)开始。注意,这其中有三个捕获组:月份,日期和两位的年份。
203-1-22005-03-04
在替换表达式中,你可以多次使用捕获组
- 对于双元音,正则表达式为([aeiou]),替换表达式为ll
- 在替换表达式中不能使用反斜杠。比如,你在计算机程序中希望使用字符串中使用部分文本。那么,你必须在每个双引号或者反斜杠之前加上反斜杠。
- 你的正则表达式可以是(["])。捕获组1是双引号或者反斜杠
- 你的替换表达式应该是l
1:2papacocob(.{6,})1bchiquichiqui(.{7,})1匹配countercountermeasurecountercountermeasures
"[^"]*"[]String re = "s";String re = "[ trn]";
在其他的编程语言中,正则表达式是由特殊标明的,比如使用/。下面是JavaScript的例子:
var regExp = /d/;var regExp = /[[]]/;var regExp = /s/;var regExp = /[ trn]/;var regExp = /https?:///;
PatternSyntaxException1234 5678 8765 4321 网站拒绝接收。因为它使用了正则表达式d{16}。正则表达式应该考虑到用户输入的空格和短横线。D*(dD*){16}
- 名字中不含空格
- 名字中没有连接符号
- 名字中只会使用ASCII码字符
- 名字中出现的字都在特殊字符集中
- 名字至少要有M个字的长度
- 名字不会超过N个字的长度
- 人们只有一个名
- 人们只有一个中间名
- 人们只有一个姓(最后三条是从英语的人名考虑)
电子邮件地址
不要使用正则表达式验证邮箱地址的正确性。
首先,这样的验证很难是精确的。电子邮件地址是可以用正则表达式验证的,但是表达式会非常的长并且复杂。
短的正则表达式会导致错误。(你知道吗?电子邮箱地址中会有一些注释)
第二,即使一个电子邮件地址可以成功匹配正则表达式,也不代表这个邮箱实际存在。邮箱的唯一验证方法,是发送验证邮件。
注意
在严格的应用场景中,不要使用正则表达式来解析HTML或者XML。解析HTML或者XML:
- 使用简单的正则表达式不能完成
- 总体来说非常困难
- 已经有其他的方法解决
找到一个已经有的解析库来完成这个工作
这就是55分钟的全部内容
总结:
abcd1234.[abc][a-z]dws.d 表示w[0-9A-Za-z_]s [^abc]DWS
{4}{3,16}{1,}?*+?*+(Septem|Octo|Novem|Decem)berb^$Az123
.[]{}?*+|()^$[]-^
致谢
正则表达式非常常用而且非常有用。每个人在编辑文本或是编写程序时都必须了解怎样使用正则表达式。练习
选择正则表达式的某种实现,阅读相关文档。我保证,你会学到更多。