简介
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
由于正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用 例如,您很可能使用 ? 和 * 通配符来查找硬盘上的文件。? 通配符匹配文件名中的单个字符,而 * 通配符匹配零个或多个字符。像 data?.dat 这样的模式将查找下列文件:
data1.dat data2.dat datax.dat dataN.dat
使用 * 字符代替 ? 字符扩大了找到的文件的数量。data*.dat 匹配下列所有文件:
data.dat data1.dat data2.dat data12.dat datax.dat dataXYZ.dat
尽管这种搜索方法很有用,但它还是有限的。通过理解 * 通配符的工作原理,引入了正则表达式所依赖的概念,但正则表达式功能更强大,而且更加灵活。
语法
正则表达式是由 普通字符(例如字符 a 到 z)以及 特殊字符(称为"元字符")组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
不可打印字符
非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:
字符 | 描述 |
\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。 |
特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如"ls *.txt"中的*,简单的说就是表示任何字符串的意思。如果要查找文件名中有*的文件,则需要对*进行转义,即在其前加一个 反斜杠字符 (\) 。ls \*.txt。
许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符 (\) 放在它们前面。下表列出了正则表达式中的特殊字符:
特殊字符 | 描述 |
$ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。 |
( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 |
* | 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 |
+ | 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 |
. | 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 |
[ | 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。 |
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 |
{ | 标记限定符表达式的开始。要匹配 {,请使用 \{。 |
| | 指明两项之间的一个选择。要匹配 |,请使用 \|。 |
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。
正则表达式的限定符有:
字符 | 描述 |
* | 匹配前面的子表达式零次或多次。例如,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?'。请注意在逗号和两个数之间不能有空格。 |
定位符
定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。
定位符用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。
字符 | 描述 |
^ | 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。 |
$ | 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。 |
\b | 匹配一个字边界,即字与空格间的位置。 |
\B | 非字边界匹配。 |
注意:不能将限定符与定位符一起使用。由于在紧靠换行或者字边界的前面或后面不能有一个以上位置,因此不允许诸如 ^* 之类的表达式。
若要匹配一行文本开始处的文本,请在正则表达式的开始使用 ^ 字符。不要将 ^ 的这种用法与中括号表达式内的用法混淆。
若要匹配一行文本的结束处的文本,请在正则表达式的结束处使用 $ 字符。
选择
用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。但用圆括号会有一个副作用,是相关的匹配会被缓存,此时可用?:放在第一个选项前来消除这种副作用。
其中?:是非捕获元之一,还有两个非捕获元是?=和?!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。
反向引用
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
可以使用非捕获元字符 '?:'、'?=' 或 '?!' 来重写捕获,忽略对相关匹配的保存。
反向引用的最简单的、最有用的应用之一,是提供查找文本中两个相同的相邻单词的匹配项的能力。以下面的句子为例。
Is is the cost of of gasoline going up up?
下面的正则表达式使用单个子表达式来实现这一点:
\b([a-z]+) \1\b
捕获的表达式,正如 [a-z]+ 指定的,包括一个或多个字母。正则表达式的第二部分是对以前捕获的子匹配项的引用,即,单词的第二个匹配项正好由括号表达式匹配。\1 指定第一个子匹配项。字边界元字符确保只检测整个单词。否则,诸如"is issued"或"this is"之类的词组将不能正确地被此表达式识别。
元字符
下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:
字符 | 描述 |
\ | 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。 |
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。 |
* | 匹配前面的子表达式零次或多次。例如,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?'。请注意在逗号和两个数之间不能有空格。 |
? | 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。 |
. | 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 |
(pattern) | 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。 |
(?:pattern) | 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。 |
(?=pattern) | 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
x|y | 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。 |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。 |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。 |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\cx | 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
\D | 匹配一个非数字字符。等价于 [^0-9]。 |
\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。 |
\w | 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 |
\W | 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 |
\xn | 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。 |
\num | 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。 |
\n | 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。 |
\nm | 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。 |
\nml | 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。 |
\un | 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。 |
运算级优先级
正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。
相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:
运算符 | 描述 |
\ | 转义符 |
(), (?:), (?=), [] | 圆括号和方括号 |
*, +, ?, {n}, {n,}, {n,m} | 限定符 |
^, $, \任何元字符、任何字符 | 定位点和序列(即:位置和顺序) |
| | 替换,"或"操作字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food"。若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood"。 |
匹配规则
我们使用grep命令来演示下面的示例,命令如下:
grep -En '^bucket$' test.dat
test.dat里面的内容如下:
Chapter 01 Chapter 0123 Chapter aB Chapter C Chapter 56 Chapter 7778 abChapter 7778 Is is the cost of of gasoline going up up? http://www.w3cschool.cc:80/html/html-tutorial.html once upon a time There once was a man from NewYork Who kept all of this cash in a bucket Who kept all of this cash in a bucket home Who kept all of this cash in a buckets bucket typedef __size_t size_t; typedef __size_t size_t; typedef __size_t size_t; &5 g7 -2 12 66 aaple apple a7M a7Mbcmm aac、abc、acc、adc a1c、a2c、a-c , a#c<wiz_code_mirror>
1
Chapter 01
2
Chapter 0123
3
Chapter aB
4
Chapter C
5
Chapter 56
6
Chapter 7778
7
abChapter 7778
8
Is is the cost of of gasoline going up up?
9
http://www.w3cschool.cc:80/html/html-tutorial.html
10
once upon a time
11
There once was a man from NewYork
12
Who kept all of this cash in a bucket
13
Who kept all of this cash in a bucket home
14
Who kept all of this cash in a buckets
15
bucket
16
typedef __size_tsize_t;
17
typedef __size_tsize_t;
18
typedef __size_tsize_t;
19
&5
20
g7
21
-2
22
12
23
66
24
aaple
25
apple
26
a7M
27
a7Mbcmm
28
aac、abc、acc、adc
29
a1c、a2c、a-c , a#c
基本模式匹配
一切从最基本的开始。模式,是正规表达式最基本的元素,它们是一组描述字符串特征的字符。模式可以很简单,由普通的字符串组成,也可以非常复杂,往往用特殊的字符表示一个范围内的字符、重复出现,或表示上下文。例如:
^once
这个模式包含一个特殊的字符^,表示该模式只匹配那些以once开头的字符串。例如该模式与字符串"once upon a time"匹配,与"There once was a man from NewYork"不匹配。grep执行的结果如下:
10:once upon a time
正如如^符号表示开头一样,$符号用来匹配那些以给定模式结尾的字符串。
bucket$
这个模式与"Who kept all of this cash in a bucket"匹配,与"buckets"不匹配。grep执行结果如下:
12:Who kept all of this cash in a bucket 15:bucket
字符^和$同时使用时,表示精确匹配(字符串与模式一样)。例如:
^bucket$
只匹配字符串"bucket"。 grep执行结果如下:
15:bucket
如果一个模式不包括^和$,那么它与任何包含该模式的字符串匹配。例如:模式
bucket
grep执行结果如下:
12:Who kept all of this cash in a bucket 13:Who kept all of this cash in a bucket home 14:Who kept all of this cash in a buckets 15:bucket
在该模式中的字母(o-n-c-e)是字面的字符,也就是说,他们表示该字母本身,数字也是一样的。其他一些稍微复杂的字符,如标点符号和白字符(空格、制表符等),要用到转义序列。所有的转义序列都用反斜杠(\)打头。制表符的转义序列是:\t。所以如果我们要检测一个字符串是否以制表符开头,可以用这个模式:
^\t
类似的,用\n表示"新行",\r表示回车。其他的特殊符号,可以用在前面加上反斜杠,如反斜杠本身用\\表示,句号.用\.表示,以此类推。
字符簇
在INTERNET的程序中,正规表达式通常用来验证用户的输入。当用户提交一个FORM以后,要判断输入的电话号码、地址、EMAIL地址、信用卡号码等是否有效,用普通的基于字面的字符是不够的。 所以要用一种更自由的描述我们要的模式的办法,它就是 字符簇。要建立一个表示所有元音字符的字符簇,就把所有的元音字符放在一个方括号里:
[AaEeIiOoUu]
这个模式与任何元音字符匹配,但只能表示一个字符。grep的执行结果:
用连字号可以表示一个字符的范围,如
[a-z] //匹配所有的小写字母 [A-Z] //匹配所有的大写字母 [a-zA-Z] //匹配所有的字母 [0-9] //匹配所有的数字 [0-9\.\-] //匹配所有的数字,句号和减号 [ \f\r\t\n] //匹配所有的白字符
同样的,这些也只表示一个字符,这是一个非常重要的。
如果要匹配一个由一个小写字母和一位数字组成的字符串,比如"z2"、"t6"或"g7",但不是"ab2"、"r2d3" 或"b52"的话,用这个模式:
^[a-z][0-9]$^[a-z][0-9]$
尽管[a-z]代表26个字母的范围,但在这里它只能与第一个字符是小写字母的字符串匹配。
前面曾经提到^表示字符串的开头,但它还有另外一个含义。当在一组方括号里使用^是,它表示"非"或"排除"的意思,常常用来剔除某个字符。还用前面的例子,我们要求第一个字符不能是数字:
^[^0-9][0-9]$
这个模式与"&5"、"g7"及"-2"是匹配的,但与"12"、"66"是不匹配的。grep命令如下:
特殊字符"." (点,句号)在正规表达式中用来表示除了'\n'之外的所有字符。所以模式"^.5$"与任何两个字符的、以数字5结尾和以其他非'\n'字符开头的字符串匹配。
^.5$
确定重复出现
到现在为止,你已经知道如何去匹配一个字母或数字,但更多的情况下,可能要匹配一个单词或一组数字。一个单词有若干个字母组成,一组数字有若干个单数组成。跟在字符或字符簇后面的花括号({})用来确定前面的内容的重复出现的次数。
字符簇 | 描述 |
^[a-zA-Z_]$ | 所有的字母和下划线(一个字符) |
^[a-zA-Z]{3}$ | 所有的3个字母的单词 |
^a$ | 字母a |
^a{4}$ | aaaa |
^a{2,4}$ | aa,aaa或aaaa |
^a{1,3}$ | a,aa或aaa |
^a{2,}$ | 包含多于两个a的字符串 |
^a{2,} | 如:aardvark和aaab,但apple不行 |
a{2,} | 如:baad和aaa,但Nantucket不行 |
\t{2} | 两个制表符 |
.{2} | 所有的两个字符 |
我们可以把模式扩展到更多的单词或数字:
所有包含一个以上的字母、数字或下划线的字符串
^[a-zA-Z0-9_]{1,}$ ^[a-zA-Z0-9_]+$
所有的正数
^[1-9]{1,}$ ^[0-9]+$
所有的整数
^\-{0,1}[0-9]{1,}$ ^\-?[0-9]+$
所有的浮点数
^[-]?[0-9]+\.?[0-9]+$ ^\-?[0-9]{1,}\.?[0-9]{1,}$ ^\-?[0-9]*\.?[0-9]+$
示例
简单表达式
正则表达式的最简单形式是在搜索字符串中匹配其本身的单个普通字符。例如,单字符模式,如 A,不论出现在搜索字符串中的何处,它总是匹配字母 A。下面是一些单字符正则表达式模式的示例:
a 7 M
grep执行结果如下:
可以将许多单字符组合起来以形成大的表达式。例如,以下正则表达式组合了单字符表达式:a、7 和 M
a7M
grep执行结果如下:
请注意,没有串联运算符。只须在一个字符后面键入另一个字符。
字符匹配
句点 (.) 匹配字符串中的各种打印或非打印字符,只有一个字符例外。这个例外就是换行符 (\n)。下面的正则表达式匹配 aac、abc、acc、adc 等等,以及 a1c、a2c、a-c 和 a#c:
a.c
中括号表达式
若要创建匹配字符组的一个列表,请在方括号([ 和 ])内放置一个或更多单个字符。当字符括在中括号内时,该列表称为"中括号表达式"。与在任何别的位置一样,普通字符在中括号内表示其本身,即,它在输入文本中匹配一次其本身。大多数特殊字符在中括号表达式内出现时失去它们的意义。不过也有一些例外,如:
- 如果 ] 字符不是第一项,它结束一个列表。若要匹配列表中的 ] 字符,请将它放在第一位,紧跟在开始 [ 后面。
- \ 字符继续作为转义符。若要匹配 \ 字符,请使用 \\。
括在中括号表达式中的字符只匹配处于正则表达式中该位置的单个字符。以下正则表达式匹配 Chapter 1、Chapter 2、Chapter 3、Chapter 4 和 Chapter 5
Chapter [12345]
请注意,单词 Chapter 和后面的空格的位置相对于中括号内的字符是固定的。中括号表达式指定的只是匹配紧跟在单词 Chapter 和空格后面的单个字符位置的字符集。这是第九个字符位置。
若要使用范围代替字符本身来表示匹配字符组,请使用连字符 (-) 将范围中的开始字符和结束字符分开。单个字符的字符值确定范围内的相对顺序。下面的正则表达式包含范围表达式,该范围表达式等效于上面显示的中括号中的列表。
Chapter [1-5]
当以这种方式指定范围时,开始值和结束值两者都包括在范围内。注意,还有一点很重要,按 Unicode 排序顺序,开始值必须在结束值的前面。
若要查找不在列表或范围内的所有字符,请将插入符号 (^) 放在列表的开头。如果插入字符出现在列表中的其他任何位置,则它匹配其本身。下面的正则表达式匹配1、2、3、4 或 5 之外的任何数字和字符:
Chapter [^12345] Chapter [^1-5]
在上面的示例中,表达式在第九个位置匹配 1、2、3、4 或 5 之外的任何数字和字符。这样,例如,Chapter 7 就是一个匹配项,Chapter 9 也是一个匹配项。
中括号表达式的典型用途是指定任何大写或小写字母或任何数字的匹配。下面的表达式指定这样的匹配
[A-Za-z0-9]
替换和分组
替换使用 | 字符来允许在两个或多个替换选项之间进行选择。
^Chapter|Section [1-9][0-9]{0,1}$
您可能认为,上面的表达式匹配出现在行首和行尾、后面跟一个或两个数字的 Chapter 或 Section。很遗憾,上面的正则表达式要么匹配行首的单词 Chapter,要么匹配行尾的单词 Section 及跟在其后的任何数字。如果输入字符串是 Chapter 22,那么上面的表达式只匹配单词 Chapter。如果输入字符串是 Section 33,那么该表达式匹配 Section 33。使用RegexBuddy 4工具如下:
若要使正则表达式更易于控制,可以使用括号来限制替换的范围,即,确保它只应用于两个单词 Chapter 和 Section。但是,括号也用于创建子表达式,并可能捕获它们以供以后使用,这一点在有关反向引用的那一节讲述。通过在上面的正则表达式的适当位置添加括号,就可以使该正则表达式匹配 Chapter 1 或 Section 3。
下面的正则表达式使用括号来组合 Chapter 和 Section,以便表达式正确地起作用:
^(Chapter|Section) [1-9][0-9]{0,1}$
使用RegexBuddy 4工具如下:
其他示例
下面列出一些正则表达式示例:
正则表达式 | 描述 |
\btypedef.+( |\t)size_t; | 在usr/include目录下搜索typedef __SIZE_TYPE__ size_t;的语句 |
#define( |\t)+O_RSYNC | 在usr搜索#define O_RSYNC的语句 |
grep命令的or,and,not操作
OR 操作符
1. 使用 \|
如果不使用grep命令的任何选项,可以通过使用 '\|' 来分割多个pattern,以此实现OR的操作。
grep 'pattern1\|pattern2' filename
2. 使用选项 -E
grep -E 选项可以用来扩展选项为正则表达式。 如果使用了grep 命令的选项-E,则应该使用 | 来分割多个pattern,以此实现OR操作。
grep -E 'pattern1|pattern2' filename
3. 使用 egrep
egrep 命令等同于‘grep -E’。因此,使用egrep (不带任何选项)命令,以此根据分割的多个pattern来实现OR操作。
egrep 'pattern1|pattern2' filename
4. 使用选项 -e
使用grep -e 选项,只能传递一个参数。在单条命令中使用多个 -e 选项,得到多个pattern,以此实现OR操作。
grep -e pattern1 -e pattern2 filename
AND 操作
1. 使用多个grep命令
可以使用多个 grep 命令 ,由管道符分割,以此来实现 AND 语义。
grep -E 'pattern1' filename | grep -E 'pattern2'
NOT操作
1. 使用选项 grep -v
使用 grep -v 可以实现 NOT 操作。 -v 选项用来实现反选匹配的(invert match)。如,可匹配得到除下指定pattern外的所有lines。
grep -v 'pattern1' filename
RegexBuddy工具
Introducing RegexBuddy
RegexBuddy is your perfect companion for working with regular expressions. Easily create regular expressions that match exactly what you want. Clearly understand complex regexes written by others. Instantly compare a regex between multiple (versions of) applications to ensure consistency. Accurately convert regexes intended for another application to your application. Quickly test any regex on sample strings and files, preventing mistakes on actual data. Use the regex in your source code with code snippets automatically adjusted to the particulars of your programming language. Collect and document libraries of regular expressions for future reuse. Integrate RegexBuddy with your favorite searching and editing tools for instant access.
How to Use RegexBuddy
图 1
如图1, 红色标记1和2分别是 regular expression 和regex history, 而标记3有8个tab: Create, Convert, Test, Debug, Use, Library, GREP 和 Forum.
如果想要调整各个面板,点击工具栏里View按钮。里面有Side by Side Layout , 这个是为了你有更大的监视效果。
我们来说一下标记3里面常用的功能:
1) The Create panel explains your regular expression in plain English. Yet, it does maintain a one-on-one relationship with the actual regular expression syntax. This way it helps you learn the actual syntax, rather than being a crutch you'll forever depend upon. As you become more comfortable with regular expressions, you'll start typing in more and more of your regular expressions directly rather than going via the Create panel and its Insert Token menu. But even as an expert, you'll still use the Create panel to help you analyze long regular expressions. Its tree structure is often easier to grasp than a long-winded linear regular expression.
2) The Test panel shows you what your regular expression actually does. It's a sandbox where you can test your regular expression, before mauling actual data.
Building Your First Regular Expression
The Create panel and the Test panel in RegexBuddy are two powerful tools to help you create regular expressions that match exactly what you want. If you have the screen space, it's a good idea to keep both visible in the side by side view.
Enough talk! Let's create our first regular expression to match a date in American mm/dd/yy format, with years from 00 to 99, and optional leading zeros for the day and month. Now read that sentence again. You may not realize it yet, but you'll soon learn through bitter experience that that rather long sentence is the most important step in crafting a regular expression that does exactly what you want. That is: knowing exactly what you want. If you don't know whether leading zeros should be optional or not, or if the year should have 2 or 4 digits, there's no hope for you. Don't launch RegexBuddy until you know what the job is.
Once you know the job, codify it by preparing test data. You can open a file, download a web page, or just type your samples directly into the Test panel. For this example, we'll enter multiple test subjects line by line. So start with choosing the "Line by line" option in the drop-down list on the Test toolbar. Then copy and paste the following lines:
Valid: 1/1/01 01/01/78 12/31/99 Invalid: 0/0/0 0/0/00 1/1/1 12/32/52 19/19/19 1/9/1999 1212/12/12 On 6/2/07 I wrote this On 6/24/13 I edited this
Particularly the invalid examples are important. It's often much harder to "see" which undesired matches a regular expression will produce than it is to see that a regular expression will match everything you want.
Now, let's start crafting our regular expression. Begin with clicking the Clear History button in the History. It looks like a File|New icon. This makes sure we start with a clean slate. To get the same results as explained below, select "C# (.NET 2.0–4.5)" in the list of applications. If you select another application, you may get slightly different results.
The easiest way to create a regular expression, is to have your sample matches ready on the Test panel, and simply proceed from left to right. Regular expressions work with text, character by character. So we'll have to translate what we want, our date format, into a pattern of characters.
First up is the month, which consist of a digit 0, 1, followed by a digit 0 through 9. The first digit is optional if the number is less than 10. Let's try this. Click the Insert Token button on the Create panel, and select Character Class. In the box "literal characters", type "01" (zero one, without the quotes), and click OK. We just created our very first regular expression: [01]. The Test panel immediately highlights all digits 0 and 1. Now, this first token has to be optional. So we click Insert Token again, and select Quantifier (repetition). Set the minimum to zero and the maximum to one. This essentially makes the character class token optional. Choose the "greedy" option, and click OK. RegexBuddy puts a question mark after the character class: [01]?. A question mark in a regular expression indeed makes the preceding token optional. To finish our month number, we insert another character class. Select Insert Token, Character Class and click the Clear button. Under "range of characters", type 0 in the left box, and 9 in the right. Click OK. The regex so far is [01]?[0-9]. The test panel highlights a whole bunch of numbers. (Can you spot our first mistake? More about that later.)
The date separator is easy: a literal slash. Click Insert Token, Literal Text, type a forward slash, and click OK. RegexBuddy appends a forward slash to your regex. Very clever. The highlighting on the Test panel changes dramatically. We've already progressed to the point where years are no longer matched as lonely digits.
Next up is the day. It consists of two numbers. An optional digit between 0 and 3, and a required digit between 0 and 9. We already know how to match a range of digits and how to make one optional. So just type [0-3]?[0-9] at the end of your regex. Adding the date separator while we're at it, we get: [01]?[0-9]/[0-3]?[0-9]/. Things are starting to shape up.
The year consists of two digits ranging from 0 to 9 each. You could just type in [0-9][0-9]. Or, you could type in [0-9] and get some practice inserting a quantifier that repeats the token twice. The result is then: [01]?[0-9]/[0-3]?[0-9]/[0-9]{2}.
All done! Our regex matches what we want. Copy it into the source code, compile, ship to customer, and wait for the bug reports to roll in.
The regular expression indeed matches the dates we want. But it also matches a bunch of stuff we don't want! How important is this? This is yet another thing that must be specified in the requirements. If you're parsing a computer-generated database export that you know will only contain valid dates, you could just use [0-9]{2}/[0-9]{2}/[0-9]{2} to grab all dates. No need to make your regex complicated to filter out 99/99/99, because the database can't store that no-date anyway. But if you're going to process user-provided data, you'd better case your regex in molded stainless steel with a shiny embossed logo.
The first problem is that our month and day parts allow too many numbers, like 0 and 19 for the month, and 0 and 32 for the day. Let's begin with the month. While the first digit is indeed an optional 0 or 1, and the second digit is always between 0 and 9, there's another restriction we didn't put into the regex: if the first digit is 1, then the second digit must be between 0 and 2. And if the first digit is 0 or missing, then the second digit can't be zero. So we essentially have two alternatives for the second digit, depending on what the first digit is. Let's do this.
First, delete the tokens for matching the month from the regex, leaving /[0-3]?[0-9]/[0-9]{2}. Put the cursor at the start of the regex. For the first alternative, we have an optional zero and a digit between 1 and 9. We already know how to do this, so just type: 0?[1-9]. Now we need to tell RegexBuddy we want to add an alternative to what we just typed. This we do by selecting the Alternation item in the Insert Token menu. RegexBuddy will insert a vertical bar, also known as the pipe symbol. Now we type in the second alternative: 1[0-2] matches 10, 11 and 12. Our regex is now 0?[1-9]|1[0-2]/[0-3]?[0-9]/[0-9]{2}.
Unfortunately, that didn't quite go as planned. The Test panel now highlights individual digits all over the place. The Create panel tells us why: the vertical bar alternates the what's to the left of it with everything that's to the right of it. You can see that by clicking on "match this alternative" in the regex tree, and then on "or match this alternative" below. The first alternative is correct, but the second one should stop at the /.
To do this, we need to group the two alternatives for the month together. In the regular expression, select 0?[1-9]|1[0-2]. Do this like you would select text in any text editor. Then click Insert Token, and select Numbered Capturing Group. We could have used a non-capturing group since we're not interested in capturing anything. However, non-capturing groups use a more complicated syntax than numbered capturing groups. You can try them if you want though. It won't make any difference in this example.
Now let's look at the Create panel again: the capturing group's node now sits on the same level in the tree as the two tokens that match the / literally. The two nodes for the alternatives for the date sit nice and cozy below the group node. If you click on them again, you'll see each alternative selects exactly the two alternatives we typed in three paragraphs ago.
We can use the exact same technique for the day of the month. If the first digit is zero or missing, we match 0?[1-9]. If the first digit is a 1 or 2, we match [12][0-9]. If the first digit is a 3, we match 3[01]. Put together in a group with alternation, we match the month with: (0?[1-9]|[12][0-9]|3[01]).
Our overall regex is now (0?[1-9]|1[0-2])/(0?[1-9]|[12][0-9]|3[01])/[0-9]{2}. Looking at it like this, you can see why even regex gurus find RegexBuddy's Create panel helpful. Even though you already know all the syntax used, the regex tree helps to analyze what's going on.
We're almost there. Everything highlighted on the Test panel is now a valid mm/dd/yy date. However, the regex is being sneaky and matching text that looks like a date from the middle of longer strings. There are two ways to go about this. When validating user input, you'll want to check that the input is nothing but a date. For that, we can use start-of-string and end-of-string anchors. Place the cursor at the start of the regex and click Insert Token, Anchors, Beginning of The String. Move the cursor to the end of the regex, and click Insert Token, Anchors, End of The String. Our final regex is \A(0?[1-9]|1[0-2])/(0?[1-9]|[12][0-9]|3[01])/[0-9]{2}\z.
If you wanted to extract the date from "On 6/2/07 I wrote this" (I did!), you can't use the \A and \z anchors. In that case, use Insert Token, Anchors, Word Boundary instead of Beginning or End of The String. A word boundary checks if the match isn't in the middle of a word or number.
So how about that shiny embossed logo? Easy! Click Insert Token, Comment and type "shiny embossed logo". Done!
参考
正则表达式 - 教程
RegexBuddy 4工具的帮助文档
关于grep命令的or,and,not操作的例子