指尖上的正则表达式–入门篇

1)历史和起源

正则表达式的“鼻祖”或许可一直追溯到科学家对人类神经系统工做原理的早期研究。美国新泽西州的Warren McCulloch和出生在美国底特律的Walter Pitts这两位神经生理方面的科学家,研究出了一种用数学方式来描述神经网络的新方法,他们创新地将神经系统中的神经元描述成了小而简单的自动控制元,从而做出了一项伟大的工做革新。python

在1956 年,出生在被马克•吐温(Mark Twain)称为“美国最美丽的城市之一的”哈特福德市的一位名叫Stephen Kleene的数学科学家,他在Warren McCulloch和Walter Pitts早期工做的基础之上,发表了一篇题目是《神经网事件的表示法》的论文,利用称之为正则集合的数学符号来描述此模型,引入了正则表达式的概念。正则表达式被做为用来描述其称之为“正则集的代数”的一种表达式,于是采用了“正则表达式”这个术语。正则表达式

以后一段时间,人们发现能够将这一工做成果应用于其余方面。Ken Thompson就把这一成果应用于计算搜索算法的一些早期研究,Ken Thompson是 Unix的主要发明人,也就是大名鼎鼎的Unix之父。Unix之父将此符号系统引入编辑器QED,而后是Unix上的编辑器ed,并最终引入grep。Jeffrey Friedl 在其著做“Mastering Regular Expressions (2nd edition)”中对此做了进一步阐述讲解,若是你但愿更多了解正则表达式理论和历史,推荐你看看这本书。    自此之后,正则表达式被普遍地应用到各类UNIX或相似于UNIX的工具中,如你们熟知的Perl。Perl的正则表达式源自于Henry Spencer编写的regex,以后已演化成了pcre(Perl兼容正则表达式Perl Compatible Regular Expressions),pcre是一个由Philip Hazel开发的、为不少现代工具所使用的库。正则表达式的第一个实用应用程序即为Unix中的 qed 编辑器。算法

而后,正则表达式在各类计算机语言或各类应用领域获得了广大的应用和发展,演变成为目前计算机技术森林中的一只形神美丽且声音动听的百灵鸟。    以上是关于正则表达式的起源和发展的历史描述,到目前正则表达式在基于文本的编辑器和搜索工具中依然占据这一个很是重要的地位。express

在最近的六十年中,正则表达式逐渐从模糊而深奥的数学概念,发展成为在计算机各种工具和软件包应用中的主要功能。不只仅众多UNIX工具支持正则表达式,近二十年来,在WINDOW的阵营下,正则表达式的思想和应用在大部分 Windows 开发者工具包中获得支持和嵌入应用!从正则式在Microsoft Visual Basic 6 或 Microsoft VBScript到.NET Framework中的探索和发展,WINDOWS系列产品对正则表达式的支持发展到无与伦比的高度,目前几乎全部 Microsoft 开发者和全部.NET语言均可以使用正则表达式。若是你是一位接触计算机语言的工做者,那么你会在主流操做系统(*nix[Linux, Unix等]、Windows、HP、BeOS等)、目前主流的开发语言(PHP、C#、Java、C++、VB、Javascript、Ruby以及python等)、数以亿万计的各类应用软件中,均可以看到正则表达式优美的舞姿。(摘自《百度百科--正则表达式》)编程

2) 正则表达式的定义

正则表达式是对字符串操做的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。数组

给定一个正则表达式和另外一个字符串,咱们能够达到以下的目的:1. 给定的字符串是否符合正则表达式的过滤逻辑(称做“匹配”);2. 能够经过正则表达式,从字符串中获取咱们想要的特定部分。浏览器

正则表达式的特色是:1. 灵活性、逻辑性和功能性很是的强;2. 能够迅速地用极简单的方式达到字符串的复杂控制。3. 对于刚接触的人来讲,比较晦涩难懂。网络

因为正则表达式主要应用对象是文本,所以它在各类文本编辑器场合都有应用,小到著名编辑器EditPlus,大到Microsoft Word、Visual Studio等大型编辑器,均可以使用正则表达式来处理文本内容。编辑器

3) 正则表达式的语法

正则表达式由一些普通字符和一些元字符组成,普通字符就是咱们平时常见的字符串、数字之类的,固然也包括一些常见的符号,等等。而元字符则能够理解为正则表达式引擎的保留字符,就像不少计算机语言中的保留字符同样,他们在正则引擎中有特殊的意义,下面将介绍JavaScript中和正则相关的元字符以及API。(不一样的计算机语言,对正则引擎的实现不是彻底一致的,因此,有些元字符和组合方式在JavaScript中不存在) 咱们将按照下面的分类将元字符一一列出:函数

3.1字面量字符( Literal Characters )

字符 描述 描述
f 换页符 (u000C)
n 换行符 (u000A)
n 换行符 (u000A)
r 回车 (u000D)
o NUL字符 (u0000)
t 制表符 (u0009)
v 垂直制表符 (u000B)
xnn 由十六进制数nn指定的拉丁字符 x0A等价于n
uxxxx 由十六进行xxxx指定的Unicode字符 u0009等价与t
cX 控制字符(X的值必须是A-Z或a-z) cJ等价于换行符n

3.2字符类( Character Classes )

字符 描述 示例
[xyz] 匹配位于括号内的任意字符 [abc]匹配'plain'中的a
[^xyz] 匹配不在括号之中的任意字符 [^abc]匹配'plain'中的p
w 等价于[a-zA-Z0-9_] w匹配'sina'中的s
W 等价于[^a-zA-Z0-9_] w不能匹配'sina'
s 任何Unicode空白符 [ fnrtv]
S 任何非空白字符 [^ fnrtv]
d 等价于[0-9] d匹配'sina123'中的1
D 等价于[^0-9] D不能匹配'sina1'中的1
[b] 退格直接量(特例)  
除换行符和其它Unicode换行符以外的任意字符,[sS]匹配任意字符 a.c 匹配 "abc", "a1c", and "a-c".

3.3重复( Repetition )

字符 描述 示例
{n,m} 匹配至少n次,但不超过m次,n和m必须是非负整数,且n<=m
? 等价于 {0,1}
[abc]匹配'plain'中的a
{n,}

td>
匹配至少n次 o{2,} 不匹配'Bob'中的'o',但匹配'food'中的 o.
{n} 刚好匹配n次 o{2} 不匹配'Bob'中的'o',但匹配'food'中的o.
? 匹配0次或1次,等价于{0,1} zo? 匹配 "z" and "zo", 但不匹配"zoo".
+ 匹配1次或屡次,等价于{1,} zo+ 匹配 "zo" and "zoo", 但不匹配 "z".
* 匹配0次或屡次,等价于{0,} zo* 匹配 "z" 和 "zoo".

3.4非贪婪的重复(non-greedy)

字符 描述 示例
*?
+?
??
{n}?
{n,}? 
{n,m}?
在重复字符后加上问号,匹配模式就是非贪婪的匹配.这种模式会尽量少的对目标字符串进行匹配 o+? 匹配"oooo"中的一个"o", o+ 则匹配全部的 "o".
/a+?b/.exec('aaab')

3.5选择、分组和引用

字符 描述 示例
|

(选择)匹配该符号左边或右边的子表达式

(z|f)ood匹配zood或food
(pattern)

(分组)将几个项目组合成一个单元,这个单元可由*,+,?和|等符号使用,并且还可记住和这个组合匹配的字符以供后面的引用使用

(A|B) [1-9] 匹配 "A5", 字母A被保存,可经过n或RegExp的$1-$9引用该值
(?:pattern) 只组合,不记忆

ai(?:r|R) 等价于air|aiR

n 和第n个分组第一次匹配的字符串匹配  

3.6指定匹配位置

字符 描述 示例
^

匹配字符串的开头,在多行检索中,匹配一行的开头

^food&匹配以f开头,d结尾的food,而不会匹配'eatfood222'中的'food'
$

匹配字符串的结尾,在多行检索中,匹配一行的结尾

同上
b 匹配一个单词的边界

erb 匹配'never'中的'er',但不匹配'verb'中的'er'.

B 匹配非单词边界  
(?=pattern) 正向确定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不须要获取供之后使用 “Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配以后当即开始下一次匹配的搜索,而不是从包含预查的字符以后开始
(?!pattern) 正向否认预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不须要获取供之后使用 “Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配以后当即开始下一次匹配的搜索,而不是从包含预查的字符以后开始

3.7标志

字符 描述
i 忽略大小写
g 执行全局匹配,即找到全部的匹配,而不是在找到第一个匹配后中止
m

多行模式,^匹配一行的开头和字符串的开头,$匹配一行的结尾或字符串的结尾

4) JavaScript中的正则API

上一部分,咱们了解了正则表达式的语法,那么,如何在JavaScript中构造一个正则表达式的对象呢?有如下两种方法:

1.字面量表示法,例如:var reg = /d+/i; 就表示一个正则表达式的实例,这个写法须要注意的地方是:正则表达式的主体部分,也就是示例中的 d+ ,必须位于“ / / ”之间,而标志位则须要跟在结束 "/" 的后面。

2.实例化RegExp对象,例如: var reg = new RegExp( "d+", "igm" );第一个参数是表达式的主体,而第二个参数则是标志位,此参数可选。 上面两种方法能够获得正则表达式的实例,一般,咱们都使用第一种方式,即字面量的方式,这种方式比较直观,第二种方式一般用来组合表达式,好比,经过用户输入的某个值,来构造表达式,例如:var input = 'this is a test'; var reg = new RegExp( "dw" + input,); 经过这种方式,咱们能够组合成新的表达式,来达到咱们的目的。在使用这种方式时,须要注意对特殊字符的转义和过滤,以避免遭受恶意攻击。 JavaScript中,除了正则表达式对象外,字符串也和正则表达式息息相关。能够说,RegExp和String 是密不可分的,缺一不可。因此,和正则相关的API就是RegExp对象和String对象的一些API。

首先是RegExp对象,该对象的实例中,有三个方法和正则表达式相关,分别是test( ),exec( )和compile( )。而String对象中和正则相关的方法则包括:match( ), replace( ), search( ), split( ),接下来会对这些方法作详细介绍。

4.1 exec( )

exec( ) 方法检索字符串中的指定值。返回值是被找到的值。若是没有发现匹配,则返回 null。

例子1:

var patt1 = new RegExp("e"); //  var patt1 = /e/;
document.write( patt1.exec("The best things in life are free") ); //output : e

从上面的例子中能够看出,exec( )方法接收一个参数,类型为字符串,能够简单的理解为:我是一个正则表达式实例,请给我一个字符串来测测吧,因而,咱们知足了它的愿望,把目标字符串丢给它。

若是匹配成功,该方法的返回结果是一个数组[array],该数组的第一个元素则是匹配的字符串,后面的元素则是表达式分组所捕获到的值,分组为1,则是该数组中的第一个元素,依次类推。若没有分组,该数组只包含一个元素。固然,该数组还有另外3个属性,input,index和lastIndex。若没有匹配成功,则返回null。

input属性表示目标字符串。

index属性表示匹配到的位置。

lastIndex属性表示上一次匹配后的位置。

另外,须要注意的是,若表达式实例中包含了全局(g)标志位,则会从 lastIndex 所标记的位置开始查找,而不会从字符串的开始位置进行。因此,咱们能够屡次调用exec( )方法,好比:

function RegExpTest()
{
   var src = "The quick brown fox jumps over the lazy dog.";

   // Create regular expression pattern with a global flag.
   var re = /w+/g;

   // Get the next word, starting at the position of lastindex.
   var arr;
   while ((arr = re.exec(src)) != null)
      {
      // New line:
      document.write ("<br />");  
      document.write (arr.index + "-" + arr.lastIndex + " ");
      document.write (arr[0]);
      }
}

详细描述请参考:

http://msdn.microsoft.com/en-us/library/z908hy33(v=vs.94).aspx

4.2 test( )

test( ) 方法比exec( )方法简单,该方法只会返回一个Boolean值,若是匹配成功,则返回true,不然,返回false。可参考下面的示例:

function TestDemo(re, teststring)
{
   // Test string for existence of regular expression.
   var found = re.test(teststring)

   // Format the output.
   var s = "";
   s += "'" + teststring + "'"

   if (found)
      s += " contains ";
   else
      s += " does not contain ";
      
   s += "'" + re.source + "'"
   return(s);
}

详细描述请参考:

http://msdn.microsoft.com/en-us/library/a55e5s6b(v=vs.94).aspx

4.3 match( )

须要注意的是,该方法的参数,也就是正则表达式添加了全局( g )标志位时的变化。若是没有添加全局标志位,返回结果和exec( )方法的返回值同样,但不包含全部的属性。

详细描述请参考:
http://msdn.microsoft.com/en-us/library/7df7sf4x(v=vs.94).aspx

4.4 replace( )

该方法是字符串中很是强有力的方法,它接收两个参数,第一个参数为表达式,也能够直接传入字符串,该方法在内部会自动将字符串转换为正则表达式;第二个参数是想要替换成的字符串,该参数能够为一个函数,可是该函数的返回值必须为字符串。具体用法请参考 http://msdn.microsoft.com/en-us/library/t0kbytzc(v=vs.94).aspx

4.5 split( )

该方法能够按照传递的参数对字符串进行分割,会返回一个数组,里面包含了分割后的元素。通常用法是直接传入字符串进行分割,固然,也能够传入正则表达式,按表达式分割,详细信息请参考: http://msdn.microsoft.com/en-us/library/t5az126b(v=vs.94).aspx

4.6其它

compile( ),search( )等方法,在平常使用中较少使用,大体了解一下便可。

5) 实例解析

你们能够参考【正则表达式经典实例】,请参考附件。

小结

以上主要讲解了正则表达式的基本语法和经常使用API,对于如何构造复杂的表达式和表达式的匹配原理,本文并未涉及。说实话,正则表达式自己确实晦涩难懂,比较拗口,你们需在平时,多多温习这些基础的语法和API。

另外,能够用一些工具来辅助咱们写出严谨的正则表达式,好比:RegexBuddy或者一些在线的应用。固然,还有浏览器的控制台,能够在里面很方便的进行测试。

在平常的开发中,千万不要在正则上钻牛角尖,由于咱们能够利用其它API,达到一样的目的。并且,太过复杂的表达式,效率可能较低,并且,对于之后的维护人员来讲,也是很是不方便维护的。

但千万不要浅尝辄止,应该继续深挖正则中的思想,正是这些基础的思想,才能帮我咱们在编程上更上层楼。

参考信息
http://msdn.microsoft.com/en-us/library/z908hy33(v=vs.94).aspx
exec()

http://msdn.microsoft.com/en-us/library/a55e5s6b(v=vs.94).aspx
test()

http://msdn.microsoft.com/en-us/library/7df7sf4x(v=vs.94).aspx
match()

http://msdn.microsoft.com/en-us/library/t0kbytzc(v=vs.94).aspx
replace()

http://www.w3school.com.cn/js/jsref_obj_regexp.asp
regexp obj

精通正则表达式(第三版)
正则表达式经典实例

转:http://ued.sina.com.cn/?p=1121

相关文章
相关标签/搜索