一时半刻谈正则(上篇)

前端之家收集整理的这篇文章主要介绍了一时半刻谈正则(上篇)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一时半刻谈正则(上篇)

前言

  • 如果你正在学习一项技术,可是总会有”学的时候觉得挺简单,但是用的时候总是想不起来“这种感受,那么我敢肯定。你还是没有学会!因为:”真的学会了,就真的不会忘“。
  • 如果你之前没有听说过正则表达式(Regular Expression),请不要想着看完这篇博客自己就可以成为正则大牛了,因为我也不是。同时也不要急,跟着我的思路一步一步来,相信肯定会有非常大的收获。
  • 学 习正则表达式,入门不难,看一些例子,试着模仿模仿就可以粗通,并且在工作中解决不少问题。往往大部分学习者就此止步,并得意的对自己说:"正则不过如 此"。还有人总是抱着一副现学现用的态度,觉得应该用着的时候再学习。在下觉得”艺多不压身“。你比别人多一项技术,那么在遇到问题的时候,解决思路就可 能和别人不同。
  • 博客题目叫做一时半刻谈正则,请不要想当然认为一时半刻自己就能完全理解。学习是个渐进的过程,对于正则表达式这门"艺术",请怀着敬畏的心态与兴奋的心情,来一同探讨它。
  • 欢迎转载,但请声明作者以及来源。

-------------------------------------------------------------------------------------------------------------

说说本文目标

半刻让你明白正则表达式是什么?用处在哪儿。一小时让你能够"会用"正则表达式。(我不相信5分钟快速入门,10分钟基本掌握之类的超级快餐式教程,一小时,是我自己能接受的时间,恕我愚钝)。

-----------------------------------------------------------------------------------------------------------------------------------

如何使用教程以及其余参考书目

纸上得来终觉,浅绝知此事要躬行”。请相信这句话吧。

-------------------------------------------------------------------------------------------------------------

正则表达式是什么?

  1. 来点儿有料的正则表达式具有伟大技术发明的一切优点,它简单、优美、功能强大、妙用无穷,对于很多工作来讲,正则表达式简直就是灵丹妙药能成百上千倍的提高工作效率。CSDN创始人蒋涛先生早年在从事软件开发工作的时候就体验过它的巨大威力。
  2. 普及点儿知识:正则表达式处理的对象只是字符串,没错,可以抽象的说是一个字符对象序列。然而,当今计算机体系本质的数据结构不就是这样的对象序列么?我们围绕计算机所做的大多数工作,都可以归结为对这样的序列的操作。(如果你不晓得"字符"或者"字符串"这两个词的概念,恕我无能为力了)
  3. 入门正则表达式就是一种描述字符串结构模式的形式化表示方法。听起来有点儿拗口?我换一种说法,正则表达式用来处理"符合某种抽象模式"的字符串。你可以理解为它是一定义这种模式的语法,也可以理解为就是一个描述着这种模式的字符串。还是有点儿模糊?上例子:
    • 我想从一句话中找到一个电话号码 例如:“我的电话号码是010-12345678,你的电话呢?”“My phone number is 010-12345678”.对于我们人类来说可以直接分辨出来,然而对于计算机而言呢?下面我定义了一个正则表达式:\d\d\d-\d\d\d\d\d\d\d\d用这个式子来过滤上述字符串,可以很轻松的匹配电话号码。不过这个式子实在是太麻烦了,还可以这样:\d{3}-\d{8}这个看起来要简单多了,当然电话号码形式多样,此式子只是作为简单的例子,你完全不必理会它是什么意思。
    • 假设你想从一个HTML页面中抽取出其中包含的所有超链接。一个HTML页面中包含许多元素,各种标签链接可能分布在各个地方,怎么办? 没关系,一个HTML链接基本形式是这样的<a href="http://www.baidu.com">百度一下</a>为 了从HTML页面中寻找和这个式子类似的字符串,我们需要描述一下这种类型字符串共有的特点,我们发现它有<a>标签 有http://www.baidu.com这样的地址还有引号,href等这样的字符。好了,先不管它是什么形式了。总之我们可以用一个正则表达式来描 述这种类型,描述超链接的特点:<a\s+([^>]+)>(.*?)</a>看不懂这些符号不要紧,只要记住,这个式子描述了在HTML页面中超链接的一般形式,用这个式子,就可以很轻易的找到HTML页面中符合这个式子描述的特点的字符串了。
  • 正则表达式是强大、便捷高效的文本处理工具。正则表达式本身加上如同一门袖珍编程语言的通用模式表示法,赋予使用者描述和分析文本的能力。配合上特定的工具提供的额外支持,或者编程语言的支持,正则表达式能够添加删除、分离、插入、和修正各种类型的文本和数据。
  • 如下,借助于Perl,从指定字符串中提取链接并打印主机名、地址、端口号等(读者先不必理会代码含义):
while($html =~ m{<a\s+([^>]+)>(.*?)</a>}ig){
	$link = $1;
	$name = $2;
	if($link =~ m{^http://([^/:]+)(:(\d+))?(/.*)?$}i){
		$host = $1;
		$port = $3 || 80;
		$path = $4 || "/"
		print "HOST:$host\n";
		print "PORT:$port\n";
		print "PATH:$path\n"
	
	}else{
		print "非法HTTP URL!";
	}
	print "NAME:$name\n";
}

----------------------------------------------------------------------------------------------------------------------------------

用什么工具编写并测试我的正则表达式?

  1. 看 过了上面我简单举出的例子后,如果你不觉得正则表达式"长的很奇怪",很难写的话。要么你是个天才,要么我是个傻子。正则表达式的语法很令人头疼,对于初 学者而言,看到这样的式子,往往觉得如刻在龟甲上的神秘密码一般,对于经常使用正则表达式的人来说,也是如此。没有固定的语法形式约束你必须怎么写,只有 基本的规则。所以找到一款用于测试正则表达式的工具实在是太有必要了。如果你看到了这里觉得工具很好找并且都适用的话。我会告诉你,正则表达式在几十年的发展过程中形成了不同的流派,各种编程语言,工具对于正则表达式的支持也是各有千秋。(强烈建议真正想学技术的朋友们果断的去尝试Linux吧。在我国IT行业人群受windows影响太久了,结果就导致人人变懒。Linux下实在是有太多好用的工具。)有很多在线测试的网站,这是个不错的选择,当然也可以搜罗一些简单实用的工具。我是从egrep起步的(Linux),而后自己根据不同语言的对正则支持的特性,采用边写代码边测试的形式。也用过许多工具,可以参考下正则工作室。关于使用何种工具,就不推荐了,因为各有所好。最近自己一直在使用自己开发的基于Java平台的一款正则分析工具。(因为还处于开发测试阶段,所以没有附上程序,之后会将源码以及程序发布的)有图有真相:

------------------------------------------------------------------------------------------------------------

正式开始

  1. 元字符

完整的正则表达式由两种字符组成。一种被称为"元字符",比如在上述例子中出现的^,*等,另一种就是普通字符,比如匹配HTML超链接时的<a>。与编程语言做类比的话元字符就相当于关键字,普通字符就相当于程序中其余的部分。当然正则中的元字符也可以在某种情况下视为普通字符,在后面我会重点说到。

首先认识两个元字符:脱字符 ^与美元符号$,这两个元字符非常容易理解。在检查一行文本时^代表一行的开始,$代表一行的结束。例如整则表达式 dog可以匹配一行文本中任意位置的dog这个词。但是如果我用 ^dog的话,就只能匹配行首的dog。^dog匹配的是以d作为一行文本的第一个字符,而后紧挨着是一个o,在往后就是一个g。请按照这种基于字符的匹配方式来思考,^dog完全可以理解为“匹配以dog开头的行”。两种思维都正确,但是第一种更为符合正则匹配原理如果我用 ^dog$ 的话,就只能匹配一行文本只含有一个单词 dog 的行了。你或许会有疑问,脱字符以及美元符号并没有匹配字符啊?对,请记住,这两个字符只是匹配一个位置,而不是具体的字符。暂且先记住这两个字符值匹配行首和行尾这两个位置。到这里我想你肯定已经了解这两个字符了。那么思考一个问题:正则表达式dog$会匹配什么? ^$又能匹配什么呢?(dog$只匹配行尾字符是先是一个d然后是o然后是g,即以dog结尾的行;^$匹配一个空行)

继续认识一个字符:点号.点号用来匹配任意字符。(这种说法只是通常情况下,有时点号并不能匹配换行符,不过现在你没必要研究如此深刻)例如对于这个单词 separate,我总是记不住应该是separate还是seperate还是separete,当我需要在文本中查找这个词的时候我可以这样写 sep...te这个时候不管这个词中间是什么,都可以匹配。当然也会匹配到 sepxyzte,sep123te等无意义的单词。稍后你会看到有更好的解决方案。再次说明:如果我们需要在表达式中使用一个“匹配任意字符”的占位符,点号就非常方便。

好了,元字符入门就到这里。告诉你这三个字符,其实是想说在以下表格中我列出的元字符,如果讲的话也会以上两种形式来讲。认识更多常用的元字符:

现在我们说说正则表达式里的单词是什么意思:就是不少于一个的连续的\w。另外对中文/汉字的特殊处理具体情况请查看所使用的开发工具以及编程语言对此的说明。元字符的个数远不止此,我就不一一列举出来了,因为重点是教会大家会用。

---------------------------------------------------------------------------------------------------------------------------------

2.量词以及可选项

如果要想匹配多个字符,或者多个重复类似字符怎么办? 比方说验证你的QQ号,QQ号可能从5位到10位。你完全可以用\d\d\d\d\d 来验证5位的QQ号,但是如果是10位呢?\d\d\d\d\d\d\d\d\d\d 实在是麻烦了些。放心吧,正则表达式肯定有非常好的解决方案。

认识一个元字符星号: *,它表示出现任意多次,就是[0,∞)用法是这样的,例如 \w*此表达式表示匹配任意多\w可以匹配的字符,\d*可以匹配任意多次数字,再例如 .*这两个符号的组合就可以匹配任意多字符。量词一般放在某个字符或元字符后面,修饰其容许出现的次数

下面看加号: +,它表示出现只少一次,就是[1,∞)用法和 * 一样,放在被修饰字符后面。

如果我想指定某个范围或者某个特定的次数怎么办? 当然可以了,你可以使用{min,max}来修饰范围,例如QQ号 \d{5,10}此表达式就能够匹配5-10位的数字组成的字符串。是不是非常容易? 还可以这样\d{8}说明\d只能重复八次。\d{5,}说明\d至少重复5次以上。{min,max}你可以称之为区间量词

最后在认识一个元字符:? 问号表示出现0次或者1次。所以?又被称之为可选项。举个例子,匹配整数。整数包括正整数、负整数以及0。我们可以这样写表达式-?\d+能够明白这个式子的含义么?对于 -123,123,-5,6等这样的数字都可以匹配。因为整数前面的负号-被?修饰,是可有可无的。当然这个式子还能够匹配000000这样的式子。随着学习的深入,这样的问题都是可以解决的。

到目前为止,基本的量词我想你已经明白了。现在总结如下:

-----------------------------------------------------------------------------------------------------------

3.字符组

回想我在介绍点号的时候的separate,我只是用sep...te来进行匹配。这可能会匹配到很多无意义的单词。我只是希望在三个点号处出现e、a、 r这三个字符。要是有一种语法,能够让我们在某处列出期望匹配的字符就好了。想必肯定是有。没错,正则表达式结构体:[ ]的存在就是为了解决这个问题,通常它连同被它包围的所有字符这个整体被称之为字符组。为了解决我的问题,表达式可以这么写sep[ae]r[ae]te你只需要在[ ]里列出期望匹配的字符就行。字符组中的每个字符之间是或的关系。[a] 匹配一个a, [b] 匹配一个b, [ab]匹配a或者b 。在字符组中可以列举任意多个字符例如[1234567] [abcdefghijk] [abcd1234,.@#%!$]等。在匹配HTML标签<H1><H2><H3>...<H6>时可以这么写:<[Hh][123456]> 当然还有更简便的方式就是 <[Hh][1-6]>,这也是我接下来需要说明的重点。在字符组内部,可以通过连字符-来表示范围,例如[a-z]表示26个英文字母,[0-9]表示10个数字[a-zA-z0-9_]基本相当于\w。但是如果是这样

[-az]或者[-a-z]的话表示什么呢? [-az]能够匹配-、a、z三个字符中任何一个;[-a-z]能够匹配-,以及26个小写英文字母。

!!!请注意只 有在字符组内部连字符- 才是元字符,否则它就只是能匹配连字符的普通字符。其实在字符组内部它也不一定就是元字符,如果连字符出现在字符组开头,它表示的就是一个普通字符,而不 是一个范围。同样的道理,问号?和点号. 通常作为元字符有其特殊的含义,但是在字符组内部他们就是普通字符。例如[0-9A-Z_!.?] 里面,这个式子能匹配 0~9,A~Z以及下划线_、叹号! 、和问号?。不妨把字符组看做独立的微型语言,在字符组内部和外部,对于元字符的规定是不同的。

接下来认识一个基于字符组的新名词:排除型字符组。用[^ ]来代替[ ]这个字符组就会匹配任何未列出的字符。例如[^0-9]它会匹配任意一个不是数字的字符。在[^ ]里面列出的是不希望匹配的字符,与[ ]正好相反。你肯定注意到了^不就是我提过的了匹配行首的元字符么?没错,但是在字符组内部,它的含义就变了。关于排除型字符组,需要注意的是:排除型字符组表示“匹配一个未列出的字符”而不是“不要匹配列出的字符”。这两种说法看起来一样。但是还是有非常细微的差别的,请看例子:对于几个单词 :

Iraq

Iraqa

Qantas

miqra

qintar

zaqqum%

如果我使用 q[^u] 来 进行匹配的话 Iraq Qantas 这两个词都是匹配不到的。因为 Qantas无法匹配的原因是Q是大写的。而Iraq的例子比较令人迷惑。正则表达式要求q之后紧跟一个不是u的字符,但是并不是说不跟任何字符。抱歉我 没有说明 Iraq后有一个换行符,我们假设没有。请务必记住:一个字符组,即便是排除型字符组也要匹配一个字符。

----------------------------------------------------------------------------------------------------------

4.多选结构

在 上一节字符组部分,你肯定学会了用字符组的方式来列出候选字符。但是事实情况是,字符组所在位置只能匹配一个字符。如果我想要匹配多种情况,那就不得不使 用许多字符组,这实在是不够便捷。一项发明做成什么样才能够称得上是伟大呢?正则表达式被称之为伟大是却有原因的。来学习另一种结构。叫做多选结构。其中最重要的字符,相信大家都很熟悉,就是 |这个竖线它的含义就是 。依靠它,我们能把不同的自表达式组合成为一个总的表达式,而这个总表达式又能够匹配任意的子表达式。例如 Bob Robert是两个子表达式 。但是Bob|Robert 就是一个复合的表达式,此表达式可以匹配 Bob或者Robert。在这样的组合中子表达式我们称之为“多选分支”,顾名思义,就是具有多种候选方式的其中的一个分支.。

来看一个例子,对于英文单词 prepare,我也分不清应该是 prepera 还是就是prepare。我想如果在一篇文档中匹配这个词,到现在你应该有可以有不止一种解决方案了。例如prep[ae]r[ae]或者prep[are]{3} (这个是非常不够精确的) 我们用多选结构试试:prepera|prepare 这样也是非常好的,当然这个式子还可以继续精简。往后你会知道还可以这样写 prep(era|are) 这样的式子我会在后面用一整节进行说明。

一个字符组只能匹配目标文本中单个字符,而每个多选结构自身都可能是完整的正则表达式,都可以匹配任意长度的文本,这两者都非常有用。同样,在一个包含多选结构的表达式中使用脱字符^和美元符号时也需要小心。我会在下面进行说明,你只需要跟着我的思路往下走。

使用多选结构注意事项多选结构中多选分支的先后顺序会在某些情况下对正则匹配结果产生影响。这个某些情况,我只能说涉及到正则引擎这个词。就是正则表达式匹配原理。一般情况下,Java,Perl,.NET,PHP,Python,Ruby,Emacs,vim等 都会因为多选分支排列顺序不同对匹配结果产生影响。因为这些平台、语言使用的正则引擎类型是基本相同的。对于现在的你,没有必要理解很深刻。上述平台的正 则引擎在遇到多选分支时是按照从左至右的顺序进行匹配的,因为多选结构表示或的关系,遇到一个能够匹配成功的,此多选结构就被标记为匹配成功。例如 ^\d{10}|\d{5}$ 对于0123456789这个序列,它会匹配所有数字。然而对于 ^\d{5}|\d{10}$ 而言,它只会匹配01234这5个数字。所以多选分支的顺序会对结果产生一定影响。此刻你只需要知道并有印象即可,如果想要深入理解原理,我强烈建议你花点儿时间认真学习一下。不要只看我的教程。

-----------------------------------------------------------------------------------------------------------

5.括号以及反向引用

又是一个难啃的骨头!在上一节我写了这么个式子 prep(era|are),其中括号是什么意思呢?在正则表达式的王国里,括号()有着非常重要的作用。猜测括号都会有哪些作用呢?通常情况下,我们会把括号括起来的部分当做一个整体,例如四则运算2*(3+4),我们会把3+4的结果视为一个整体。一个括号内部就是一个基本的单元。同样的,在正则表达式里,括号的作用也是如此。括号有两种基本用途:限制多选分支的范围;将若干字符组合为一个单元,受问号或者星号之类量词的作用。

首先看限制多选分支的范围。例如prep(era|are),括号里面的内容会被视作一个独立的单元。假如我们去掉括号的话就变成了prepera|are那么这个表达式表示的是匹配prepera或者are这两个单词了。在这个例子中括号的作用就是限制多选分支的范围。为了使你更明白,我再列举几个例子:Chin(a|ese)这个表达式匹配China或者Chinese; th(at|ey|ere),它匹配that或者they或者there;来个复杂点儿的,sep(a|e)r(a|e)te (看起来是不是和sep[ae]r[ae]挺像?仔细思考不一样的地方)。不知你是否已经明白了括号的这一项用途?如果还不太理解,就请把括号当做分组,一个括号就是一个组。这一组字符在外界看来就是一个独立的单元。其实括号的第三个用途就是分组并记住组里面的内容

再来看括号的基本用途二。 我们在学习量词时知道了用星号*容许被修饰元字符出现任意多次,用加号+说明至少出现一次,区间量词限定出现范围等。但是这些量词修饰的只是单个字符。如 果我想修饰一个单词,或者一个句子怎么办?这个也可以用括号来解决。例如(hello){3} 能够匹配一行文本中连续出现三个hello的部分,也就是说对于hello world hello hello hellohellohello这个句子,它会匹配最后连续的三个hello。

学到了这里,我想你肯定觉得我给出的例子都太简单了。那么我来给个问题。匹配24小时制的所有时间,例如 08:00am 23:30pm等。想想这个问题,用你已经学到的知识可以怎么解决

解决问题首先要分析问题,解决正则表达式问题首先要分析待匹配的字符串的结构特点。先用\d\d:\d\d[ap]m 或者[0-9][0-9]:[0-9][0-9](am|pm)看是否能解决?通常情况下这个表达式就可以解决了。但是此式子能够匹配到 25:99am这样毫无意义的式子。我们下一步需要的是限定一下范围。对于分钟而言,只能是00~59首先看这样是否可行呢?[0-5][0-9] 用来匹配分钟,这是可以的,还可以这样[0-5]\d,也还可以(0|1|2|3|4|5)\d,也还可以[012345]\d不过后两个是在不怎么好看。分钟解决了,请你思考小时部分应该如何解决呢? 我们知道最大只能是23最小是00,这个问题有点儿复杂。不过也是可以很容易解决的。我先把解决方案写出来:(0\d|1\d|2[0-3]):[0-5]\d[ap]m这个式子是否可行呢?

我们还可以提出问题,假如容许出现 9:15am这样的形式呢? 就是小时部分可以是单个字符,这完全是合法的时间表示。想想,小时部分既能出现一个字符也可以是两个字符,用什么方法可以解决((0?[0-9]|1[0-9]|2[0-3]):[0-5][0-9])[ap]m看这个式子是不是很晕?但是它确实是可以工作的,且不说效率问题。因为正则表达式效率问题内容很深。打造高效的正则表达式是一项非常困难的任务。好在现在只是让你学会用它。好了,24小时这个例子就到这里。

留一个问题如何匹配IP地址? 例如 10.186.144.100, 255.255.255.0, 100.86.92.55,请你试着解决这个问题。相信会有非常大的收获的。(\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字(\d{1,3}\.){3} 匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。不幸的是,它也将匹配999.999.999.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址(([01]?\d\d?|2[0-4]\d|25[0-5])\.){3}([01]?\d\d?|2[0-4]\d|25[0-5])。理解这个表达式的关键是理解[01]?\d\d?|2[0-4]\d|25[0-5],这里我就不细说了,你自己应该能分析得出来它的意义。

学到这里,你会发现,正则表达式非常灵活,对于同一个问题可能会有非常多的不同解决方式,写出来的表达式也是各有千秋。记住一个准则就可以了:使用正则表达式往往需要在待匹配文本和选取正则表达式之间做出衡量。首先你先要了解带匹配的数据,然后根据数据形式,设计合适的正则表达式。没有最好,只有适合。

我们来学习括号的第三种用途:分组与反向引用。 在许多开发平台中正则表达式中的括号能够记住它所匹配到的内容。先来解决一个问题。如何寻找一句话中重复出现的单词? 比方说 How do you do? 或者 I can because i think i can.我们将带着这个问题来学习括号的这部分知识。如果我们确切的可以知道重复的单词是什么,比方说是do那么我们就可以简单的这么写:do.*do不过它会匹配到 do .... doing 这样的部分。同时也会有其他五花八门的匹配结果。这显然不是我们所希望的。这样匹配的前提还是,我们知道可能重复的单词是什么,假如我们不知道重复的单词是什么呢?穷举所有可能出现的重复单词显然是不可能完成的任务。如果我们先匹配一个单词,接下来检查“后面的单词是否和它一样”,这就好办多了。这就是反向引用的作用。如果你的正则表达式支持反向引用,我们就可以借助于它来完成这个任务。

首先我们定义这样一个匹配单词的式子:[a-zA-Z]+ 然后用括号将它括起来,作为一个分组也就是一个单元([a-zA-Z]+)然后这个单元后可以有多个其余字符。([a-zA-Z]+).*为了检验重复,我们要这样写([a-zA-Z]+).*\1,你肯定看到了\1。没错\1的意思就是第一个分组。同理\2表示第二个分组,\3表示第三个分组。前面的括号用于分组并记住其所匹配到的内容,后面通过\1等来引用前面分组的内容,这就是反向引用。那么分组是怎么划分的呢? 括号是允许嵌套的,例如:([a-z])([0-9])\1\2这个你可以理解\1引用的是([a-z]),\2引用的是([0-9]),那么([a-z]([0-9])(A-Z))(#|@)这样的式子呢?(虽然找个式子意义不大)。记住括号是按照开括号 ( 从左至右出现的顺序进行的,分组编号是根据开括号的顺序编排的。在([a-z]([0-9])(A-Z))(#|@)个式子中\1引用过的是([a-z]([0-9])(A-Z)),\2引用的是([0-9]),\3引用的是(A-Z),\4引用的是(#|@)。明白了么?

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<name> )(或者把尖括号换成单引号' 也行:(?'name' ), 这样就把此括号的组名指定为name了。要反向引用这个分组捕获内容,你可以使用\k<name>即可。


到这里我们学习了字符组,多选结构以及括号的作用和使用方法,累了么?先看过下面的重要总结。然后再休息一下,马上进入下半部分。剩下的部分相对于前面这些来说可能稍微难以理解一些。不过坚持一下,你离会用正则表达式不远了。

  • 在 字符组内部,元字符的定义规则(及它们的意义)和字符组外部是不一样的。例如在字符组外部点号是元字符,但是在字符组内部点号则不是如此。相反,连字符只 有在字符组内部(普遍情况下)才是元字符,否则就不是。脱字符在字符组外部只表示一个意思,在字符组内部紧挨着 [ 时表示排除型,其它情况下又表示别的意思。
  • 不要混淆多选结构和字符组。字 符组[abc]和多选结构(a|b|c)固然表示同一个意思,但是这个例子中的相似性并不能够推广开来。无论列出的字符有多少 字符组只能匹配一个字符。相反,多选结构可以匹配任意长度的文本,每个多选结构可能匹配的文本都是独立的。不过多选结构没有像字符组那样的排除功能
  • 排除型字符组是表示所有未列出的字符的字符组的简便方法。因此 [^x] 的意思不是说"只有当这个位置不是x是才能匹配"而是“匹配一个不等于x的字符”。差别很细微,但是很重要。

猜你在找的正则表达式相关文章