正则表达式30分钟入门教程

目录

跳过目录

  1. 本文目标
  2. 如何使用本教程
  3. 正则表达式到底是什么东西?
  4. 入门
  5. 测试正则表达式
  6. 元字符
  7. 字符转义
  8. 重复
  9. 字符类
  10. 分枝条件
  11. 反义
  12. 分组
  13. 后向引用
  14. 零宽断言
  15. 负向零宽断言
  16. 注释
  17. 贪婪与懒惰
  18. 处理选项
  19. 平衡组/递归匹配
  20. 还有些什么东西没提到
  21. 联系作者
  22. 网上的资源及本文参考文献
  23. 更新纪录

本文目标

30分钟内让你明白正则表达式是什么,并对它有一些基本的了解,让你可以在自己的程序或网页里使用它。

如何使用本教程

最重要的是——请给我30分钟,如果你没有使用正则表达式的经验,请不要试图在30内入门——除非你是超人 :)

别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并没有你想像中的那么困难。当然,如果你看完了这篇教程之后,发现自己明白了很多,却又几乎什么都记不得,那也是很正常的——我认为,没接触过正则表达式的人在看完这篇教程后,能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理,以后你还需要多练习,多使用,才能熟练掌握正则表达式。

除了作为入门教程之外,本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说,这个目标还是完成得不错的——你看,我自己也没能把所有的东西记下来,不是吗?

清除格式 文本格式约定:专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 对其进行匹配的源字符串 对正则表达式或其中一部分的说明

隐藏边注 本文右边有一些注释,主要是用来提供一些相关信息,或者给没有程序员背景的读者解释一些基本概念,通常可以忽略。

正则表达式到底是什么东西?

字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-123456780376-7654321)。

入门

学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。

假设你在一篇英文小说里查找hi,你可以使用正则表达式hi

这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。

不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b

\b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置

如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w

假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b

这里,.是另一个元字符,匹配除了换行符以外的任意字符*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词

换行符就是’\n’,ASCII编码为10(十六进制0x0A)的字符。

如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:

0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。

为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}

这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)

测试正则表达式

如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。

不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是微软 .Net Framework 2.0下正则表达式的行为,所以,我向你介绍一个.Net下的工具Regex

Tester。首先你确保已经安装了.Net Framework 2.0,然后下载Regex Tester。这是个绿色软件,下载完后打开压缩包,直接运行RegexTester.exe就可以了。

下面是Regex Tester运行时的截图:

Regex Tester运行时的截图

元字符

现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.正则表达式里还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等\w匹配字母或数字或下划线或汉字等

对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的,其它环境下的具体情况请查看相关文档。

下面来看看更多的例子:

\ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)

好吧,现在我们说说正则表达式里的单词是什么意思吧:就是不少于一个的连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大 :)

\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次

\b\w{6}\b 匹配刚好6个字符的单词

表1.常用的元字符
代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

正则表达式引擎通常会提供一个“测试指定的字符串是否匹配一个正则表达式”的方法,如JavaScript里的RegExp.test()方法或.NET里的Regex.IsMatch()方法。这里的匹配是指是字符串里有没有符合表达式规则的部分。如果不使用^$的话,对于\d{5,12}而言,使用这样的方法就只能保证字符串里包含5到12连续位数字,而不是整个字符串就是5到12位数字。

元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$

这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。

因为使用了^$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。

和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^$的意义就变成了匹配行的开始处和结束处

字符转义

如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.\*。当然,要查找\本身,你也得用\\.

例如:deerchao\.net匹配deerchao.netC:\\Windows匹配C:\Windows

重复

你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):

表2.常用的限定符
代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

下面是一些使用重复的例子:

Windows\d+匹配Windows后面跟1个或更多数字

^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)

字符类

要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?

很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母[.?!]匹配标点符号(.或?或!)

我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。

下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}

“(”和“)”也是元字符,后面的分组节里会提到,所以在这里需要使用转义

这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)-空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。

分枝条件

不幸的是,刚才那个表达式也能匹配010)12345678(022-87654321这样的“不正确”的格式。要解决这个问题,我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子:

0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。

\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

分组

我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。

(\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。

IP地址中每个数字都不能大于255,大家千万不要被《24》第三季的编剧给忽悠了……

不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义

表3.常用的反义代码
代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

例子:\S+匹配不包含空白符的字符串

<a[^>]+>匹配用尖括号括起来的以a开头的字符串

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

呃……其实,组号分配还不像我刚说得那么简单:

  • 分组0对应整个正则表达式
  • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
  • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例:

\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成也行:(?’Word’\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b

使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

表4.常用分组语法
分类 代码/语法 说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里
(?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释 (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。“我为什么会想要这样做?”——好问题,你觉得为什么呢?

零宽断言

地球人,是不是觉得这些术语名称太复杂,太难记了?我也有同感。知道有这么一种东西就行了,它叫什么,随它去吧!人若无名,便可专心练剑;物若无名,便可随意取舍……

接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧:

断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配singdanc

(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890

下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)

负向零宽断言

前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词–它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:

\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b

零宽度负预测先行断言(?!exp)断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词

同理,我们可以用(?<!exp),零宽度负回顾后发断言断言此位置的前面不能匹配表达式exp(?<![a-z])\d{7}匹配前面不是小写字母的七位数字

请详细分析表达式(?<=<(\w+)>).*(?=<\/\1>),这个表达式最能表现零宽断言的真正用途。

一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容(?<=<(\w+)>)指定了这样的前缀被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。

注释

小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)

要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:

      (?<=    # 断言要匹配的文本的前缀

      <(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)

      )       # 前缀结束

      .*      # 匹配任意文本

      (?=     # 断言要匹配的文本的后缀

      <\/\1>  # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签

      )       # 后缀结束

贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)ab(第四到第五个字符)

为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。

表5.懒惰限定符
代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

处理选项

在C#中,你可以使用Regex(String, RegexOptions)构造函数来设置正则表达式的处理选项。如:Regex regex = new Regex(@”\ba\w{6}\b”, RegexOptions.IgnoreCase);

上面介绍了几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。下面是.Net中常用的正则表达式选项:

表6.常用的处理选项
名称 说明
IgnoreCase(忽略大小写) 匹配时不区分大小写。
Multiline(多行模式) 更改^$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)
Singleline(单行模式) 更改.的含义,使它与每一个字符匹配(包括换行符\n)。
IgnorePatternWhitespace(忽略空白) 忽略表达式中的非转义空白并启用由#标记的注释。
ExplicitCapture(显式捕获) 仅捕获已被显式命名的组。

一个经常被问到的问题是:是不是只能同时使用多行模式和单行模式中的一种?答案是:不是。这两个选项之间没有任何关系,除了它们的名字比较相似(以至于让人感到疑惑)以外。

平衡组/递归匹配

这里介绍的平衡组语法是由.Net Framework支持的;其它语言/库不一定支持这种功能,或者支持此功能但需要使用不同的语法。

有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用\(.+\)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?

为了避免(\(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa <bbb> <bbb> aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来?

这里需要用到以下的语法构造:

  • (?’group’) 把捕获的内容命名为group,并压入堆栈(Stack)
  • (?’-group’) 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
  • (?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
  • (?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

如果你不是一个程序员(或者你自称程序员但是不知道堆栈是什么东西),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个”group”,第二个就是从黑板上擦掉一个”group”,第三个就是看黑板上写的还有没有”group”,如果有就继续匹配yes部分,否则就匹配no部分。

我们需要做的是每碰到了左括号,就在压入一个”Open”,每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。

<                         #最外层的左括号

    [^<>]*                #最外层的左括号后面的不是括号的内容

    (

        (

            (?'Open'<)    #碰到了左括号,在黑板上写一个"Open"

            [^<>]*       #匹配左括号后面的不是括号的内容

        )+

        (

            (?'-Open'>)   #碰到了右括号,擦掉一个"Open"

            [^<>]*        #匹配右括号后面不是括号的内容

        )+

    )*

    (?(Open)(?!))         #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果还有,则匹配失败

>                         #最外层的右括号

平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配嵌套的<div>标签<div[^>]*>[^<>]*(((?’Open’<div[^>]*>)[^<>]*)+((?’-Open’</div>)[^<>]*)+)*(?(Open)(?!))</div>.

还有些什么东西没提到

上边已经描述了构造正则表达式的大量元素,但是还有很多没有提到的东西。下面是一些未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们–当你需要用到它们的时候。如果你安装了MSDN

Library,你也可以在里面找到.net下正则表达式详细的文档。

这里的介绍很简略,如果你需要更详细的信息,而又没有在电脑上安装MSDN Library,可以查看关于正则表达式语言元素的MSDN在线文档

表7.尚未详细讨论的语法
代码/语法 说明
\a 报警字符(打印它的效果是电脑嘀一声)
\b 通常是单词分界位置,但如果在字符类里使用代表退格
\t 制表符,Tab
\r 回车
\v 竖向制表符
\f 换页符
\n 换行符
\e Escape
\0nn ASCII代码中八进制代码为nn的字符
\xnn ASCII代码中十六进制代码为nn的字符
\unnnn Unicode代码中十六进制代码为nnnn的字符
\cN ASCII控制字符。比如\cC代表Ctrl+C
\A 字符串开头(类似^,但不受处理多行选项的影响)
\Z 字符串结尾或行尾(不受处理多行选项的影响)
\z 字符串结尾(类似$,但不受处理多行选项的影响)
\G 当前搜索的开头
\p{name} Unicode中命名为name的字符类,例如\p{IsGreek}
(?>exp) 贪婪子表达式
(?<x>-<y>exp) 平衡组
(?im-nsx:exp) 在子表达式exp中改变处理选项
(?im-nsx) 为表达式后面的部分改变处理选项
(?(exp)yes|no) 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no
(?(exp)yes) 同上,只是使用空表达式作为no
(?(name)yes|no) 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no
(?(name)yes) 同上,只是使用空表达式作为no
发表在 编程相关 | 留下评论

HTML 5 参考手册

HTML 5

通过制定如何处理所有 HTML 元素以及如何从错误中恢复的精确规则,HTML 5 改进了互操作性,并减少了开发成本。

HTML 5 中的新特性包括了嵌入音频、视频和图形的功能,客户端数据存储,以及交互式文档。

HTML 5 还包含了新的元素,比如:<nav>, <header>, <footer> 以及 <figure> 等等。

HTML 5 工作组包括:AOL, Apple, Google, IBM, Microsoft, Mozilla, Nokia, Opera, 以及数百个其他的供应商。

注释:HTML 5 还没有成为 W3C 正式的推荐标准。

如需阅读更多有关 W3C HTML 5 活动的内容,请阅读我们的 W3C 教程

按字母顺序排列的标签列表

  • 4: 指示在 HTML 4.01 中定义了该元素
  • 5: 指示在 HTML 5 中定义了该元素
标签 描述 4 5
<!–…–> 定义注释。 4 5
<!DOCTYPE> 定义文档类型。 4 5
<a> 定义超链接。 4 5
<abbr> 定义缩写。 4 5
<acronym> HTML 5 中不支持。定义首字母缩写。 4
<address> 定义地址元素。 4 5
<applet> HTML 5 中不支持。定义 applet。 4
<area> 定义图像映射中的区域。 4 5
<article> 定义 article。 5
<aside> 定义页面内容之外的内容。 5
<audio> 定义声音内容。 5
<b> 定义粗体文本。 4 5
<base> 定义页面中所有链接的基准 URL。 4 5
<basefont> HTML 5 中不支持。请使用 CSS 代替。 4
<bdo> 定义文本显示的方向。 4 5
<big> HTML 5 中不支持。定义大号文本。 4
<blockquote> 定义长的引用。 4 5
<body> 定义 body 元素。 4 5
<br> 插入换行符。 4 5
<button> 定义按钮。 4 5
<canvas> 定义图形。 5
<caption> 定义表格标题。 4 5
<center> HTML 5 中不支持。定义居中的文本。 4
<cite> 定义引用。 4 5
<code> 定义计算机代码文本。 4 5
<col> 定义表格列的属性。 4 5
<colgroup> 定义表格列的分组。 4 5
<command> 定义命令按钮。 5
<datalist> 定义下拉列表。 5
<dd> 定义定义的描述。 4 5
<del> 定义删除文本。 4 5
<details> 定义元素的细节。 5
<dfn> 定义定义项目。 4 5
<dir> HTML 5 中不支持。定义目录列表。 4
<div> 定义文档中的一个部分。 4 5
<dl> 定义定义列表。 4 5
<dt> 定义定义的项目。 4 5
<em> 定义强调文本。 4 5
<embed> 定义外部交互内容或插件。 5
<fieldset> 定义 fieldset。 4 5
<figcaption> 定义 figure 元素的标题。 5
<figure> 定义媒介内容的分组,以及它们的标题。 5
<font> HTML 5 中不支持。 4
<footer> 定义 section 或 page 的页脚。 5
<form> 定义表单。 4 5
<frame> HTML 5 中不支持。定义子窗口(框架)。 4
<frameset> HTML 5 中不支持。定义框架的集。 4
<h1> to <h6> 定义标题 1 到标题 6。 4 5
<head> 定义关于文档的信息。 4 5
<header> 定义 section 或 page 的页眉。 5
<hgroup> 定义有关文档中的 section 的信息。 5
<hr> 定义水平线。 4 5
<html> 定义 html 文档。 4 5
<i> 定义斜体文本。 4 5
<iframe> 定义行内的子窗口(框架)。 4 5
<img> 定义图像。 4 5
<input> 定义输入域。 4 5
<ins> 定义插入文本。 4 5
<keygen> 定义生成密钥。 5
<isindex> HTML 5 中不支持。定义单行的输入域。 4
<kbd> 定义键盘文本。 4 5
<label> 定义表单控件的标注。 4 5
<legend> 定义 fieldset 中的标题。 4 5
<li> 定义列表的项目。 4 5
<link> 定义资源引用。 4 5
<map> 定义图像映射。 4 5
<mark> 定义有记号的文本。 5
<menu> 定义菜单列表。 4 5
<meta> 定义元信息。 4 5
<meter> 定义预定义范围内的度量。 5
<nav> 定义导航链接。 5
<noframes> HTML 5 中不支持。定义 noframe 部分。 4
<noscript> 定义 noscript 部分。 4 5
<object> 定义嵌入对象。 4 5
<ol> 定义有序列表。 4 5
<optgroup> 定义选项组。 4 5
<option> 定义下拉列表中的选项。 4 5
<output> 定义输出的一些类型。 5
<p> 定义段落。 4 5
<param> 为对象定义参数。 4 5
<pre> 定义预格式化文本。 4 5
<progress> 定义任何类型的任务的进度。 5
<q> 定义短的引用。 4 5
<rp> 定义若浏览器不支持 ruby 元素显示的内容。 5
<rt> 定义 ruby 注释的解释。 5
<ruby> 定义 ruby 注释。 5
<s> HTML 5 中不支持。定义加删除线的文本。 4
<samp> 定义样本计算机代码。 4 5
<script> 定义脚本。 4 5
<section> 定义 section。 5
<select> 定义可选列表。 4 5
<small> 定义小号文本。 4 5
<source> 定义媒介源。 5
<span> 定义文档中的 section。 4 5
<strike> HTML 5 中不支持。定义加删除线的文本。 4
<strong> 定义强调文本。 4 5
<style> 定义样式定义。 4 5
<sub> 定义下标文本。 4 5
<summary> 定义 details 元素的标题。 5
<sup> 定义上标文本。 4 5
<table> 定义表格。 4 5
<tbody> 定义表格的主体。 4 5
<td> 定义表格单元。 4 5
<textarea> 定义 textarea。 4 5
<tfoot> 定义表格的脚注。 4 5
<th> 定义表头。 4 5
<thead> 定义表头。 4 5
<time> 定义日期/时间。 5
<title> 定义文档的标题。 4 5
<tr> 定义表格行。 4 5
<tt> HTML 5 中不支持。定义打字机文本。 4
<u> HTML 5 中不支持。定义下划线文本。 4
<ul> 定义无序列表。 4 5
<var> 定义变量。 4 5
<video> 定义视频。 5
<xmp> HTML 5 中不支持。定义预格式文本。 4
发表在 html5 | 留下评论

HTML 5 事件属性

标准事件属性

HTML 4 增加了通过事件触发浏览器中行为的能力,比如当用户点击某个元素时启动一段 JavaScript。

如果需要学习更多有关使用这些事件进行编程的内容,请学习我们的 JavaScript 教程 和 DHTML 教程

下面的表格列出了可插入 HTML 5 元素中以定义事件行为的标准事件属性。

Window 事件属性

window 对象触发的事件。

适用于 <body> 标签:

属性 描述
onafterprint script 在打印文档之后运行脚本
onbeforeprint script 在文档打印之前运行脚本
onbeforeonload script 在文档加载之前运行脚本
onblur script 当窗口失去焦点时运行脚本
onerror script 当错误发生时运行脚本
onfocus script 当窗口获得焦点时运行脚本
onhaschange script 当文档改变时运行脚本
onload script 当文档加载时运行脚本
onmessage script 当触发消息时运行脚本
onoffline script 当文档离线时运行脚本
ononline script 当文档上线时运行脚本
onpagehide script 当窗口隐藏时运行脚本
onpageshow script 当窗口可见时运行脚本
onpopstate script 当窗口历史记录改变时运行脚本
onredo script 当文档执行再执行操作(redo)时运行脚本
onresize script 当调整窗口大小时运行脚本
onstorage script 当文档加载加载时运行脚本
onundo script 当 Web Storage 区域更新时(存储空间中的数据发生变化时)
onunload script 当用户离开文档时运行脚本

表单事件

由 HTML 表单内部的动作触发的事件。

适用于所有 HTML 5 元素,不过最常用于表单元素中:

属性 描述
onblur script 当元素失去焦点时运行脚本
onchange script 当元素改变时运行脚本
oncontextmenu script 当触发上下文菜单时运行脚本
onfocus script 当元素获得焦点时运行脚本
onformchange script 当表单改变时运行脚本
onforminput script 当表单获得用户输入时运行脚本
oninput script 当元素获得用户输入时运行脚本
oninvalid script 当元素无效时运行脚本
onreset script 当表单重置时运行脚本。HTML 5 不支持。
onselect script 当选取元素时运行脚本
onsubmit script 当提交表单时运行脚本

键盘事件

由键盘触发的事件。

适用于所有 HTML 5 元素:

属性 描述
onkeydown script 当按下按键时运行脚本
onkeypress script 当按下并松开按键时运行脚本
onkeyup script 当松开按键时运行脚本

鼠标事件

由鼠标货相似的用户动作触发的事件。

适用于所有 HTML 5 元素:

属性 描述
onclick script 当单击鼠标时运行脚本
ondblclick script 当双击鼠标时运行脚本
ondrag script 当拖动元素时运行脚本
ondragend script 当拖动操作结束时运行脚本
ondragenter script 当元素被拖动至有效的拖放目标时运行脚本
ondragleave script 当元素离开有效拖放目标时运行脚本
ondragover script 当元素被拖动至有效拖放目标上方时运行脚本
ondragstart script 当拖动操作开始时运行脚本
ondrop script 当被拖动元素正在被拖放时运行脚本
onmousedown script 当按下鼠标按钮时运行脚本
onmousemove script 当鼠标指针移动时运行脚本
onmouseout script 当鼠标指针移出元素时运行脚本
onmouseover script 当鼠标指针移至元素之上时运行脚本
onmouseup script 当松开鼠标按钮时运行脚本
onmousewheel script 当转动鼠标滚轮时运行脚本
onscroll script 当滚动元素滚动元素的滚动条时运行脚本

媒介事件

由视频、图像以及音频等媒介触发的事件。

适用于所有 HTML 5 元素,不过在媒介元素(诸如 audio、embed、img、object 以及 video)中最常用:

属性 描述
onabort script 当发生中指事件时运行脚本
oncanplay script 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本
oncanplaythrough script 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本
ondurationchange script 当媒介长度改变时运行脚本
onemptied script 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本
onended script 当媒介已抵达结尾时运行脚本
onerror script 当在元素加载期间发生错误时运行脚本
onloadeddata script 当加载媒介数据时运行脚本
onloadedmetadata script 当媒介元素的持续时间以及其他媒介数据已加载时运行脚本
onloadstart script 当浏览器开始加载媒介数据时运行脚本
onpause script 当媒介数据暂停时运行脚本
onplay script 当媒介数据将要开始播放时运行脚本
onplaying script 当媒介数据已开始播放时运行脚本
onprogress script 当浏览器正在取媒介数据时运行脚本
onratechange script 当媒介数据的播放速率改变时运行脚本
onreadystatechange script 当就绪状态(ready-state)改变时运行脚本
onseeked script 当媒介元素的定位属性 [1] 不再为真且定位已结束时运行脚本
onseeking script 当媒介元素的定位属性为真且定位已开始时运行脚本
onstalled script 当取回媒介数据过程中(延迟)存在错误时运行脚本
onsuspend script 当浏览器已在取媒介数据但在取回整个媒介文件之前停止时运行脚本
ontimeupdate script 当媒介改变其播放位置时运行脚本
onvolumechange script 当媒介改变音量亦或当音量被设置为静音时运行脚本
onwaiting script 当媒介已停止播放但打算继续播放时运行脚本

[1]:定位属性的英文译文是:seeking attribute。

 

发表在 html5 | 留下评论

HTML 5 标准属性

所有 HTML 5 标签均支持下面列出的属性,仅有少数例外。

HTML 5 标准属性

NEW:HTML 5 中新的标准属性。

注释:HTML 4.01 不再支持 accesskey 属性:

属性 描述
accesskey character 规定访问元素的键盘快捷键
class classname 规定元素的类名(用于规定样式表中的类)。
contenteditable
  • true
  • false
规定是否允许用户编辑内容。
contextmenu menu_id 规定元素的上下文菜单。
data-yourvalue value 创作者定义的属性。

HTML 文档的创作者可以定义他们自己的属性。

必须以 “data-” 开头。

dir
  • ltr
  • rtl
规定元素中内容的文本方向。
draggable
  • true
  • false
  • auto
规定是否允许用户拖动元素。
hidden hidden 规定该元素是无关的。被隐藏的元素不会显示。
id id 规定元素的唯一 ID。
item
  • empty
  • url
用于组合元素。
itemprop
  • url
  • group value
用于组合项目。
lang language_code 规定元素中内容的语言代码。语言代码参考手册
spellcheck
  • true
  • false
规定是否必须对元素进行拼写或语法检查。
style style_definition 规定元素的行内样式。
subject id 规定元素对应的项目。
tabindex number 规定元素的 tab 键控制次序。
title text 规定有关元素的额外信息。

 

发表在 html5 | 留下评论

HTML 5 <abbr> 标签

定义和用法

表示一个缩写形式,比如 “Inc.”、”etc.”。通过对缩写词语进行标记,您就能够为浏览器、拼写检查程序、翻译系统以及搜索引擎分度器提供有用的信息。

<abbr> 标签最初是在 HTML 4.0 中引入的,表示它所包含的文本是一个更长的单词或短语的缩写形式。

浏览器可能会根据这个信息改变对这些文本的显示方式,或者用其他文本代替。也许当前流行的浏览器对 <abbr> 标签中包含的文本还没有任何处理方式,但是我们无法预测将来的版本将会如何实现该标签。

实例

The <abbr title="People's Republic of China">PRC</abbr> was founded in 1948.

输出:

etc.

亲自试一试

HTML 4.01 与 HTML 5 之间的差异

NONE

提示和注释:

注释:在某些浏览器中,当您把鼠标移至缩略词语上时,title 可用于展示表达的完整版本。

标准属性

<abbr> 标签支持 HTML 5 中的标准属性

事件属性

<abbr> 标签支持 HTML 5 中的事件属性

 

发表在 html5 | 留下评论

HTML 5 <embed>标签

定义和用法

<embed> 标签定义嵌入的内容,比如插件。

实例

<embed src="helloworld.swf" />

亲自试一试

HTML 4.01 与 HTML 5 之间的差异

<embed> 标签是 HTML 5 中的新标签。

提示和注释

注释:<embed> 标签必须有 src 属性。

提示:您无法在开始标签和结束标签之间写文本,来说明旧式的浏览器不支持该标签,就像 <audio> 和 <video>。

属性

属性 描述
height pixels 设置嵌入内容的高度。
src url 嵌入内容的 URL。
type type 定义嵌入内容的类型。
width pixels 设置嵌入内容的宽度。

标准属性

<embed> 标签支持 HTML 5 中的标准属性

事件属性

<embed> 标签支持 HTML 5 中的事件属性

 

发表在 html5 | 留下评论

HTML 5
标签

定义和用法

<figcaption> 标签定义 figure 元素的标题。

“figcaption” 元素应该被置于 “figure” 元素的第一个或最后一个子元素的位置。

实例

文档中的一个小节,描述 PRC:

<figure>
  <figcaption>PRC</figcaption>
  <p>The People's Republic of China was born in 1949...</p>
</figure>

TIY

HTML 4.01 与 HTML 5 之间的差异

<figcaption> 标签是 HTML 5 中的新标签。

提示和注释

提示:”figcaption” 元素应该被置于 “figure” 元素的第一个或最后一个子元素的位置。

标准属性

<figcaption> 标签支持 HTML 5 中的标准属性

事件属性

<figcaption> 标签支持 HTML 5 中的事件属性

发表在 html5 | 留下评论

HTML 5
标签

定义和用法

<figure> 标签用于对元素进行组合。

实例

文档中的一个小节,描述 PRC:

<figure>
  <h1>PRC</h1>
  <p>The People's Republic of China was born in 1949...</p>
</figure>

TIY

HTML 4.01 与 HTML 5 之间的差异

<figure> 标签是 HTML 5 中的新标签。

提示和注释

提示:请使用 <figcaption> 元素为元素组添加标题。

标准属性

<figure> 标签支持 HTML 5 中的标准属性

事件属性

<figure> 标签支持 HTML 5 中的事件属性

 

发表在 html5 | 留下评论

HTML 5 标签

定义和用法

<font> 标签规定文本的字体外观、字体尺寸和字体颜色。不要使用该元素,请使用 CSS 向元素添加样式。

HTML 4.01 与 HTML 5 之间的差异

在 HTML 4.01 中,不赞成使用该元素。

在 HTML 5 中,不支持该元素,但是允许由所见即所得的编辑器来插入该元素 。

在 HTML 5 中,仅支持 style 属性。

 

发表在 html5 | 留下评论

HTML 5

定义和用法

<footer> 标签定义 section 或 document 的页脚。

在典型情况下,该元素会包含创作者的姓名、文档的创作日期以及/或者联系信息。

实例

文档的页脚:

<footer>This document was written in 2010</footer>

亲自试一试

HTML 4.01 与 HTML 5 之间的差异

<footer> 标签是 HTML 5 中的新标签。

提示和注释

提示:假如您使用 footer 来插入联系信息,应该在 footer 元素内使用 <address> 元素。

标准属性

<footer> 标签支持 HTML 5 中的标准属性

事件属性

<footer> 标签支持 HTML 5 中的事件属性

发表在 html5 | 留下评论