几个月学习了一下正则表达式,说来惭愧,当时只是一个劲地看,没有做总结,现在有点忘了。所以今天来总结一下。
一.正则表达式的定义
在计算机科学中,是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。在很多文本编辑器或其他工具里,正则表达式通常被用来检索和/或替换那些符合某个模式的文本内容。许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen。
对于经常跟程序打交道的人们,正则表达式是个好工具哦。
最普遍的使用是我们在自己电脑的硬盘上搜索文件,比如:《正则表达?》或者《正则*》,其中?表示匹配文件名中的单个字符,而*表示匹配1个或者多个字符。
那么我们还能使用正则表达式来做些什么事情呢?
1.测试字符中的某个模式。比如可以对一个输入的字符串进行测试,看它是否存在一个电话号码模式或者一个邮箱模式。这叫做数据有效性验证。这经常出现在注册时填写邮箱地址的程序验证中。
2.替换文本。可以使用一个正则表达式来标志具有某种特征的字符串,然后使用它来删除或者替换一个文本中的这个字符串。程序员经常遇到这种替换的情况吧,不过好像一般不使用正则表达式。
3.根据模式匹配从字符串中提取一个子字符串。可以用来在文本或者输入字段中查找特点文字。
二.正则表达式的语法
一个正则表达式就是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。
一.优先顺序
在构造正则表达式之后,就可以象数学表达式一样来求值,也就是说,可以从左至右并按照一个优先级顺序来求值。
下表从最高优先级到最低优先级列出各种正则表达式操作符的优先级顺序:
操作符 |
描述 |
\ |
转义符 |
(),(?:),(?=),[] |
圆括号和方括号 |
*,+,?,{n},{n,},{n,m} |
限定符 |
^,$,\anymetacharacter |
r 位置和顺序 |
| |
“或”操作 |
二.分类
1.普通字符
通字符由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符,所有数字,所有标点符号以及一些符号。简单的正则表达式是一个单独的普通字符,可以匹配所搜索字符串中的该字符本身。例如:字符模式 'A' 可以匹配所搜索字符串中任何位置出现的字母 'A'。
也可以将多个单字符组合在一起得到一个较大的表达式。例如,通过组合单字符表达式 'a'、'7'以及 'M' 所创建出来的一个表达式"a7M".
2.特殊字符
不少元字符在试图对其进行匹配时需要进行特殊的处理。要匹配这些特殊字符,必须首先将这些字符转义,也就是在前面使用一个反斜杠 (\)。下表给出了这些特殊字符及其含义:
特殊字符 |
说明 |
$ |
匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。 |
() |
标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 |
* |
匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 |
+ |
匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 |
。 |
匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 |
[ |
标记一个中括号表达式的开始。要匹配 [,请使用 \[。 |
? |
匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 |
\ |
将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹 配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。 |
^ |
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。 要匹配 ^ 字符本身,请使用 \^。 |
{ |
标记限定符表达式的开始。要匹配 {,请使用 \{。 |
| |
指明两项之间的一个选择。要匹配 |,请使用 \|。 |
3.非打印字符
不少很有用的非打印字符,偶尔必须使用。下表显示了用来表示这些非打印字符的转义序列:
字符 |
含义 |
\CX |
匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\f |
匹配一个换页符。等价于 \x0c 和 \cL。 |
\n |
匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r |
匹配一个回车符。等价于 \x0d 和 \cM。 |
\s |
匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S |
匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
\t |
匹配一个制表符。等价于 \x09 和 \cI。 |
\v |
匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
句点 (.) 匹配一个字符串中任何单个的打印或非打印字符,除了换行符 (\n) 之外。 比如"abc","aac","amc"等等,可以用"a.c" 来进行匹配。
果试图匹配一个包含文件名的字符串,其中句点 (.) 是输入字符串的一部分,则可以在正则表达式中的句点前面加上一个反斜杠 (\) 字符来实现这一要求。比如"filename.exe"可以用"filename\.exe"来进行匹配。
4.括号表达式
以在一个方括号 ([ 和 ]) 中放入一个或多个单字符,来创建一个待匹配的列表。如果字符被放入括号中括起来,则该列表称为括号表达式。括号内和其他任何地方一样,普通字符代表其本身,也就是说,它们匹配输入文字中出现的一处自己。大多数特殊字符在位于括号表达式中时都将失去其含义。不过也有例外:
(1)']' 字符如果不是第一项,则将结束一个列表。要在列表中匹配 ']' 字符,请将其放在第一项,紧跟在开始的 '[' 后面。
(2)'\' 仍然作为转义符。要匹配 '\' 字符,请使用 '\\'。
括号表达式中所包含的字符只匹配该括号表达式在正则表达式中所处位置的一个单字符。比如:
"Number [12345]" 可以匹配'Number 1'、'Number 2'、'Number 3'、'Number 4' 以及 'Number 5'。
注意单词 'Number' 及后面的空格与括号内的字符的位置关系是固定的。因此,括号表达式只用来指定满足紧跟在单词 'Number' 和一个空格之后的单字符位置的字符集合。这里是第八个字符位置。
如果希望使用范围而不是字符本身来表示待匹配的字符,则可以使用连字符将该范围的开始和结束字符分开。每个字符的字符值将决定其在一个范围内的相对顺序。比如:
"Number [1-5]" 可以匹配'Number 1'、'Number 2'、'Number 3'、'Number 4' 以及 'Number 5'。
也就是说"Number [12345]"等价于"Number [1-5]"。
(如果以这种方式指定范围,则开始和结束值都包括在该范围内。有一点特别需要注意的是,在 Unicode 排序中起始值一定要在结束值之前。)
如果想在括号表达式中包括连字符,则必须使用下述方法之一:
a. 使用反斜杠将其转义: [\-]
b. 将连字符放在括号列表的开始和结束位置。下面的表达式能匹配所有的小写字母和连字符: [-a-z][a-z-]
c. 创建一个范围,其中开始字符的值小于连字符,而结束字符的值等于或大于连字符。下面两个正则表达式都满足这一要求: [!--][!-~]
同样,通过在列表开始处放置一个插入符(^),就可以查找所有不在列表或范围中的字符。如果该插入符出现在列表的其他位置,则匹配其本身,没有任何特殊含义。
如:“Number [^123]”以及"Number [^1-3]"匹配第8个位置除1,2,3之外的任何数字字符。
括号表达式的典型用法是指定对任何大写或小写字母字符或任何数字的匹配,如"[A-Za-z0-9]"。
5.限定符
有时候不知道要匹配多少字符。为了能适应这种不确定性,正则表达式支持限定符的概念。这些限定符可以指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。如下:
字符 |
描述 |
* |
匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。 |
+ |
匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。 + 等价于 {1,}。 |
? |
匹配前面的子表达式零次或一次。例如, "do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。 ? 等 价于 {0,1}。 |
{n} |
n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹 配 "food" 中的两个 o。 |
{n,} |
n 是一个非负整数。至少匹配 n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。 |
{n,m} |
m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如, "o{1,3}" 将 匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 |
例子:
a."Number [1-9][0-9]*"匹配 第1-999个,即2位或3位的数字;
b.”Number [0-9]{1,2}“匹配 第0-99个,即不少于1位,但不超过2位的数字;
c.”Number [1-9][0-9]?“匹配 第1-99个,即1或2位的数字。等价于"Number [1-9][0-9]{0,1}";
'*'、'+' 和 '?' 限定符都称之为贪婪的,也就是说,他们尽可能多地匹配文字。有时这根本就不是所希望发生的情况。有时则正好希望最小匹配。
比如:有一个句子是 <H1>Number 1-ABC</H1>,
贪婪匹配是:"<.*>",匹配最左边的"<"到最右边的">"。
非贪婪匹配是:"<.*?>",匹配最开始的<H1>。
通过在 '*'、 '+' 或 '?' 限定符后放置 '?',该表达式就从贪婪匹配转为了非贪婪或最小匹配。
6.定位符
定位符可以将一个正则表达式固定在一行的开始或结束。也可以创建只在单词内或只在单词的开始或结尾处出现的正则表达式。下表包含了正则表达式及其含义的列表:
字符 |
描述 |
^ |
匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。 |
$ |
匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。 |
\b |
匹配一个单词边界,也就是指单词和空格间的位置。 |
\B |
匹配非单词边界。 |
不能对定位符使用限定符。因为在一个换行符或者单词边界的前面或后面不会有连续多个位置,因此诸如'^*'的表达
式是不允许的。要匹配一行文字开始位置的文字,请在正则表达式的开始处使用 '^' 字符。不要把 '^' 的这个语法与括号表达式中的语法弄混。它们的语法根本不同。
要匹配一行文字结束位置的文字,请在正则表达式的结束处使用 '$' 字符。如"Chapter[1-9][0-9]{0,1}$"只匹配章节,而不会交叉匹配到其他内容。
配单词边界有少许不同,但却给正则表达式增加了一个非常重要的功能。单词边界就是单词和空格之间的位置。非单词边界就是其他任何位置。
如:单词“Number”可以用"\bNum"(如果它位于要匹配的字符串的开始,则将查找位于单词开头处的匹配) 或者 "ber\b"(如果它位于改字符串的末尾,则查找位于单词结束处的匹配)来匹配。
注:"\Bapt"只会匹配"Chapter",而不会匹配"aptitude"。
7.选择与编组
选择允许使用 '|' 字符来在两个或多个候选项中进行选择。
但是如果我们这样写:"^Chapter|Section [1-9][0-9]{0,1}$"
你或许以为它会匹配位于一行的开始和结束位置,且后跟一个或两个数字的 'Chapter' 或 'Section':
但是实际上它要么匹配位于一行开始处的单词 'Chapter 1',要么匹配一行结束处的后跟任何数字的 'Section 1'。这不是我们想要的目的。
那么我们可以使用圆括号来限制选择的范围。上面的例子我们可以这样写:"^(Chapter|Section) [1-9][0-9]{0,1}$"。
'Chapter|Section' 两边放置圆括号建立了适当的编组,但也导致两个待匹配单词之一都被捕获供今后使用。有时捕获一个子匹配是所希望的,有时则是不希望的。除非真的是需要捕获子匹配,否则请不要使用。由于不需要花时间和内存来存储那些子匹配,这种正则表达式的效率将更高。
以在正则表达式模式圆括号内部的前面使用 '?:'来防止存储该匹配供今后使用:"^(?:Chapter|Section) [1-9][0-9]{0,1}$"。
除了 '?:' 元字符,还有两个非捕获元字符用于称之为预查的匹配。一个为正向预查,用 ?= 表示, 在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串。一个为负向预查,用 '?!' 表示,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。如"Windows(?=95|98|NT)"可以匹配Windows95,Windows98,WindowsNT。
(这个正向预查和负向预查,我也看不大懂,以后再研究研究。)
7.向后引用
正则表达式一个最重要的特性就是将匹配成功的模式的某部分进行存储供以后使用这一能力。请回想一下,对一个正则表达式模式或部分模式两边添加圆括号将导致这部分表达式存储到一个临时缓冲区中。可以使用非捕获元字符 '?:','?=',or '?!' 来忽略对这部分正则表达式的保存。
捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从1开始,连续编号直至最大99个子表达式。每个缓冲区都可以使用 '\n' 访问,其中n为一个标识特定缓冲区的一位或两位十进制数。
后引用一个最简单,最有用的应用是提供了确定文字中连续出现两个相同单词的位置的能力。如句子:Is is the cat there going up up? 它显存在单词多次重复的问题。如果能有一种方法无需查找每个单词的重复现象就能修改该句子就好了
"\b([a-z]+) \1\b"这个正则表达式可以实现这个功能。
上面的子表达式就是圆括号之间的每一项。所捕获的表达式包括一个或多个字母字符,即由
'[a-z]+' 所指定的。该正则表达式的第二部分是对前面所捕获的子匹配的引用,也就是由附加表达式所匹配的第二次出现的单词。'\1'用来指定第一个子匹配。单词边界元字符确保只检测单独的单词。如果不这样,则诸如 "is issued" 或 "this is" 这样的短语都会被该表达式不正确地识别。
以上是前一阵学习正则表达式的一份总结,如有不当之处,大家多多指出。