随笔-143  评论-68  文章-0  trackbacks-0

1.什么是正则表达式


简单的说,正则表达式是一种可以用于文字模式匹配和替换的强有力的工具。是由一系列普通字符和特殊字符组成的能明确描述文本字符串的文字匹配模式。

正则表达式并非一门专用语言,但也可以看作是一种语言,它可以让用户通过使用一系列普通字符和特殊字符构建能明确描述文本字符串的匹配模式。除了简单描述这些模式之外,正则表达式解释引擎通常可用于遍历匹配,并使用模式作为分隔符来将字符串解析为子字符串,或以智能方式替换文本或重新设置文本格式。正则表达式为解决与文本处理有关的许多常见任务提供了有效而简捷的方式。

正则表达式具有两种标准:

·       基本的正则表达式(BRE – Basic Regular Expressions)

·       扩展的正则表达式(ERE – Extended Regular Expressions)。

ERE包括BRE功能和另外其它的概念。

 

正则表达式目前有两种解释引擎:

·       基于字符驱动(text-directed engine)

·       基于正则表达式驱动(regex-directed engine)

Jeffery Friedl把它们称作DFA和NFA解释引擎。

 

约定:

为了描述起来方便,在本文中做一些约定:

1.     本文所举例的所有表达时都是基于NFA解释引擎的。

2.     正则表达式,也就是匹配模式,会简写为Regex。

3.     Regex的匹配目标,也就是目标字符串,会简写为String。

4.     匹配结果用会用黄色底色标识。

5.     用1\+1=2 括起来的表示这是一个regex。

6.     举例会用以下格式:

 

test
This is a test
会匹配test,testcase等


  
2.正则表达式的起源
正则表达式的“祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。

1956 年, 一位叫 Stephen Kleene 的美国数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。

随后,发现可以将这一工作应用于使用Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson是Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的qed 编辑器。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。具有完整语法的正则表达式使用在字符的格式匹配方面上,后来被应用到熔融信息技术领域。自从那时起,正则表达式经过几个时期的发展,现在的标准已经被ISO(国际标准组织)批准和被Open Group组织认定。

哇哈哈...不可天下人负我!!!
穷!机子天天蓝屏......
 

 
3. 正则表达式使用祥解
最简单的正则表达式相信大家都已熟悉并且经常使用,那就是文字字符串。特定的字符串可通过文字本身加以描述;像 test这样的Regex模式可精确匹配输入的字符串”test”,但是它也可以匹配this is a testcase,这就不是我们想要得结果。

当然,使用正则表达式匹配等于它自身的精确字符串是没有价值的实现,不能体现正则表达式的真正作用。但是,假如要查找的不是test,而是所有以字母 t 开头的单词,或所有4个字母的单词,那该怎么办?这超出了文字字符串的合理范围。所以我们才需要深入地研究正则表达式。

3.1基本语法
虽然正则表达式并非一门专用语言,但它也有一些特殊的规定,也可以称之为基本语法。

正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。该模式描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。

可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式。

3.1.1普通字符
由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符,所有数字,所有标点符号以及一些符号。

3.1.2非打印字符
非打印字符也是普通字符,单独列出来便于参考。

哇哈哈...不可天下人负我!!!
穷!机子天天蓝屏......
 
非打印字符参考(有错大家矫正,JS手册都有)
\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。

Regex中可以使用非打印字符。\t会匹配一个tab字符(ASC||),\r 会匹配一个回车(0x0D),\n 会匹配一个换行符(0x0A)。应该注意的是:Windows使用\r\n表示一行的结束,而UNIX使用\n 。

同样,我们可以在Regex中使用16进制的ASCⅡ码或者ANSI标准码。在拉丁语中,版权符号的代码是0xA9,所以我们也可以这样来匹配版权符号 \xA9 。另外一个匹配tab的写法是:\x09 。但是注意,第一位的“0”必须去掉。


特殊字符
特殊字符也叫做元字符,保留字符(Metacharactor),在Regex中表示特殊的意义,大部分的意思在不同的上下文中的意义是不同的,这里只列出最普遍的意义。

特殊字符共有11个:
$
匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。

( )
标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。

*
匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。

+
匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。

.
匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。

[
标记一个中括号表达式的开始。要匹配 [,请使用 \[。

?
匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。

\
将下一个字符标记为或特殊字符、或原义字符、或反向引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。

^
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。

{
标记限定符表达式的开始。要匹配 {,请使用 \{。

|
指明两项之间的一个选择。要匹配 |,请使用 \|。

在元字符前加 \ 转义符,可以把特殊字符当作普通字符来使用。

比如:如要要匹配 1+1=2 ,正确的正则表达式应该为1\+1=2。否则, + 会被当作特殊字符对待。

除了特殊字符,所有的其他字符都不应该加 \ 。因为 \ 也是一个特殊字符。\ 和普通字符组合在一起也可以创造一种特殊的意义。比如 \d 表示匹配所有的数字。

作为程序员,单引号和双引号不是特殊字符会也许让我们感到很惊讶。但这是正确的。因为我们在编程的时候,编程语言会知道引号之间的哪些字符表示特殊意义,编译器在把字符串x传递给regex解释引擎之前,会把它们处理成regex。比如,在C#中,如果我们要匹配 1\+1=2 ,在程序中我们要这样写: “1\\+1=2” ,C#编译器会把 “\\” ,处理为一个“\” 。同样,如果要匹配 C:\Temp ,首先,正则表达式要这样写 C:\\Temp,然后在程序中我们应该这样写:“ C:\\\\temp”。

哇哈哈...不可天下人负我!!!
穷!机子天天蓝屏......
 
 


3.1.4字符集
        字符集描述了一组字符,Regex解释器会认为匹配字符集中的一个字符就可以认为匹配成功。

字符集用[ ]括起来即可。

        比如gr[ae]y就可以匹配gray或者grey。

        字符集只能匹配一个字符,gr[ae]y就不能和graey匹配。字符集中的字符顺序是任意的,得到的结果都是唯一的。

        可以在字符集中用连字符“-”来表示一个范围。[0-9]的结果和[0123456789]的匹配结果都是相同的。字符集中的范围可以有多种。比如[0-9a-fA-F]表示匹配所有的16进制,包括大小写。也可以把范围和单个字符组合在一起用,[0-9a-fxA-FX]表示匹配所有的16进制或者一个字符X。字符集的顺序不会影响结果。

        在字符集的开始标志“[”后面加上一个“^”符号,表示否定,表示匹配除字符集中定义的字符以外的所有字符。包括非打印字符和行结束符。

        注意:字符集匹配的一个字符,而不是一个位置。所以。q[^u]的意义不是“q后面的字符不是u”。而是“q后面的字符可以是除了u以外的所有字符”。

        q[^u]不会和Iraq匹配。

但是会和Iraq is a country匹配,因为q后面的空格字符是一个“不是u的字符”。

3.1.5在字符集中使用元字符
字符集中的元字符只能是 ‘]’, ‘\’, ‘^’, 和 ‘-‘ 。

其他元字符在字符集中都失去了特殊意义,表示的只是一个普通字符。也不需要用加“\”。

比如:

匹配一个“*”或者“+”,用[*+]就足够了。即使给他们加上“\”,regex解释器也会把他们忽略掉。

 

四种特殊字符的处理:

在字符集中要表示“]”,“^”和“-”需要在后面加上转义符“\”,来表示它们代表的分别是普通字符“]”,“^”和“-”。

也可以把它们放在一个不能表示特殊意义的位置,后一个方法比较好,因为他们不会影响可读性。

“^”
要想匹配一个“^”,可以把它放在除了紧跟“[”的任意一个位置。

Regex
String
Description

[x^]
A string with x and ^.
匹配x或者“^”

 


“]”
可以把“]”放在紧跟着“[”的位置,或者使用否定字符集。

Regex
String
Description

[]x]
A string with x and ]
匹配x或者“]”

[^]x]
A string with x and ]
匹配除了x和”] ”以外的所有字符


“\”
要想把“\”当作一个普通字符来匹配,而不是一个特殊字符,必须把“\”再用一个“\”括起来。


[\\x]
A string with x and \
匹配x或者“\”

 


“-”
连字符可以放在紧跟着“[”的后面,或者正好“]”的前面,或者紧跟着“^”的后面。

 

[-x]
A string with x and -
匹配x或者“-”

[x-]
A string with x and -
匹配x或者“-”

预定义字符集
因为很多字符集是经常使用的,所以Regex解释器预定义了一些常用字符集:

d
[0-9]
所有数字

\w
[a-zA-Z]
表示所有的字符,和文化字体有关

\s
[ \t\r\n]

空格,回车和tab。和文化字体有关

预订一字符集可以既可以用在字符集里面,也可以用在字符集外面。
\s\d
1<space>+<space>2<space>=<space>3
匹配后面紧跟着一个数字的空白符

[\s\d]
1<space>+<space>2<space>=<space>3
匹配一个单独的字符或者一个数字或者一个空白符

[\da-fA-F]和[0-9a-fA-F]的匹配结果是一样的。

 

同样,在预定义字符集前面加一个“^”符号表示否定。它们也有预先定义好的表示:

\D
[^\d]
非数字

\W
[^\w]
非字符,和文化字体有关

\S
[^\s]
非空格,回车和tab。和文化字体有关

在“[]”使用否定预订一字符集时要特别心。[\D\S]不等于[^\d\s]。[^\d\s]会匹配除了数字和空白符以外的所有字符。而[\D\S]会匹配要么不是一个数字,要么是空白符。因为数字不是空白符,空白符也不是数字,所以[\D\S]会匹配任意的字符。

限定符
有时候不知道要匹配多少字符。为了能适应这种不确定性,正则表达式支持限定符的概念。这些限定符可以指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。

下表给出了各种限定符及其含义的说明:

* 匹配前面的子表达式零次或多次。例如,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?'。请注意在逗号和两个数之间不能有空格。
对一个很大的输入文档而言,章节数很轻易就超过九章,因此需要有一种方法来处理两位数或者三位数的章节号。限定符就提供了这个功能。下面的JScript 正则表达式可以匹配具有任何位数的章节标题:

/Chapter [1-9][0-9]*/
下面的 VBScript 正则表达式执行同样的匹配:

"Chapter [1-9][0-9]*"
请注意限定符出现在范围表达式之后。因此,它将应用于所包含的整个范围表达式,在本例中,只指定了从 0 到 9 的数字。

这里没有使用 '+' 限定符,因为第二位或后续位置上并不一定需要一个数字。同样也没有使用 '?' 字符,因为这将把章节数限制为只有两位数字。在 'Chapter' 和空格字符之后至少要匹配一个数字。

如果已知章节数限制只有99 章,则可以使用下面的 JScript 表达式来指定至少有一位数字,但不超过两个数字。

/Chapter [0-9]{1,2}/
对 VBScript 可以使用下述正则表达式:

"Chapter [0-9]{1,2}"
上述表达式的缺点是如果有一个章节号大于 99,它仍只会匹配前两位数字。另一个缺点是某些人可以创建一个 Chapter 0,而且仍能匹配。一个更好的用来匹配两位数的 JScript 表达式如下:

/Chapter [1-9][0-9]?/
或者

/Chapter [1-9][0-9]{0,1}/
对 VBScript 而言,下述表达式与上面等价:

"Chapter [1-9][0-9]?"
或者

"Chapter [1-9][0-9]{0,1}"
'*'、 '+'和 '?' 限定符都称之为贪婪的,也就是说,他们尽可能多地匹配文字。有时这根本就不是所希望发生的情况。有时则正好希望最小匹配。

例如,你可能要搜索一个 HTML 文档来查找一处包含在 H1 标记中的章节标题。在文档中该文字可能具有如下形式:

<H1>Chapter 1 – Introduction to Regular Expressions</H1>
下面的表达式匹配从开始的小于号 (<) 到 H1 标记结束处的大于号之间的所有内容。

/<.*>/
VBScript 的正则表达式为:

"<.*>"
如果所要匹配的就是开始的 H1 标记,则下述非贪婪地表达式就只匹配 <H1>。

/<.*?>/
或者

"<.*?>"
通过在 '*'、 '+' 或 '?' 限定符后放置 '?',该表达式就从贪婪匹配转为了非贪婪或最小匹配。

 

posted on 2008-07-12 08:32 windone 阅读(487) 评论(0)  编辑 收藏 引用 所属分类: 计算机软件
只有注册用户登录后才能发表评论。