正则表达式必知必会--学习笔记

第一章,表达式入门

 正则表达式(regular expression)和正则表达式语言已经出现很久了,正则表达式是一种工具,和其他工具一样,它是为了解决一类专门的问题而发明的,就像我们的播种机,是为了播种而发明的。那么主要来解决什么问题呢?我们经常用的到的,从一个文件夹中,快速的找到一个文件,在表单中,验证一个邮箱,我们也会用到正则。我们主要用正则表达式来进行搜索和进行替换,书上的解释是正则表达式是用来匹配和处理文本的字符串。正则表达式是用正则表达是语言创建的,用来解决我们前面所提到的问题,验证邮箱等,它并不是一门完整的程序设计语言,但是它也有我们要学习的特殊语法和指令,在我看来他更像一个扩展,好像现在所有程序语言都支持正则。语法是正则最容易掌握的,真正的挑战是学会如何运用那些语法把实际问题分解为正则表达式并最终处理! 

一句话:正则就是更好的帮我们解决一些字符串搜索和替换的工作,学习目标:通过学习语法,用正则解决实际问题! 

第二章,匹配单个字符

 正则表达式可以包含纯文本,也可以全是纯文本,像这样使用正则有点浪费,

匹配纯文本,有多个匹配结果,字母的大小写问题(区分大小写),匹配任意字符串 .(字符)

在 my name is xiami,my life befault 里面可以找到两个my,在正则表达式引擎的默认行为是返回第一个匹配结果,就像刚才的找到第一个my,第二个不会返回,但是在javascript中,/正则表达式/g  后面跟g意思global,将返回所有匹配的数组。在javascript 用 i 来强制执行一次不区分大小写。上面举那个静态的纯文本例子,体现不出正则表达式的威力,我们一起来学习如何使用正则表达式匹配不可预知的字符。在正则表达式中,特殊字符(或字符集合)用来给出要搜索的东西,. 字符(英文句号)用来匹配任何一个单个的字符。

.字符可以匹配任何单个的字符串、字母、数字、甚至是.字符本身,在同一个正则表达式中允许使用多个.字符,它可以连续使用,也可以间隔的出现在模式的不同位置。如果在实际中,我们真的是需要.字符,而不是它在正则表达式中的特殊含义,需要使用转义符(\)。在正则表达式中,\字符永远出现在一个特殊含义字符的前面,注意:使用\本身时,也需要通过转义,使用 \\ 来搜索反斜杠。

第三章,匹配一组字符(集合)

 匹配多个字符中的某一个,使用元字符[]来定义一个字符集合,在使用正则表达式的过程中,会频繁的用到一些字符区间(0-9、A-Z,等等),为了简化字符区间的定义,正则表达式提供特殊的元字符,字符区间可以使用-来定义。例如[0-9]的意义和[0123456789]是相同的,也可以使用多个区间,例:[A-Za-z0-9]匹配所有字母和数字。

取非匹配,用元字符^来表示,通常是为了排除不需要得到的字符,这里的[^0-9]和上面的[0-9]正好的相反,^效果将作用于给定字符集合里的所有字符和字符区间

总结:使用[和]元字符定义一个字符集合,意思是必须包含集合里的字符之一,字符集合使用^来求非,将给定的字符集合排除在匹配操作以外

第四章,使用元字符

 学习使用更多的元字符,去匹配特定的字符或字符类型,那么什么是元字符呢?元字符就是在正则表达式中有特殊含义的字符。在一个完整的正则表达式里,字符\ 的后面永远跟着一个字符,匹配空白字符串:

[\b]    回退(并删除一个字符)一个字符(Backspace 键)

\f     换页符

\n      换行符

\r      回车符

\t      制表符(Tab 键)

\v     垂直制表符

在window系统中,用\r\n 匹配一个回车+换行组合,而在unix/linux系统中,使用\n\n匹配空白行

匹配特定的字符类型:

1,匹配数字

\d      任何一个数字字符(等价于[0-9])

\D      任何一个非数字字符(等价于[^0-9])

2,匹配数字和字母

\w      任何一个字母数字字符(大小均可)或下划线字符,等价于[A-Za-z0-9_]

\W      任何一个非字母数字字符或非下划线字符,等价于[^A-Za-z0-9]

3,匹配空白字符串与非空白字符串

\s      任何一个空白字符串(等价于 [\f\n\r\t\v])

\S      任何一个非空白字符串(等价于 [^\f\n\r\t\v])

用来匹配退格符的[\b]元字符是一个特例,他不在类元字符\s 覆盖范围内,也没有被排除在\s的覆盖范围内。好纠结,意思就是不确定吧

4,匹配十六进制或八进制数值

十六进制前缀要用\x来给出,八进制要使用\0 来给出

5,使用POSIX字符类(基本程序语言都支持,至少php支持)

这里说明以下,POSIX类字符和上面学习的元字符是没有什么区别的,只是表达形式不一样,达到的结果都是一样的

第五章,重复匹配

 匹配一个或多个字符(或字符集合)不匹配零个     +

匹配零个或多个字符(或字符集合)          *    主要用来匹配一个可有可无的

匹配零个或一个字符(或字符集合)          ?    例如:https?  可以匹配 http,也可以匹配 https

1,匹配的重复次数(使用元字符{和} 定义重复次数)

{3}    必须匹配连续的3次          为重复次数匹配固定的值

{2,4}   最少重复2次,最多重复4次      为重复次数设置一个区间

{3,}   至少重复3次             匹配至少重复次数

上面我们大概了解的都是贪婪匹配原则,贪婪匹配就是从一段文本的开始一直匹配到这段文本的结束,而当我们只想匹配到第一个重复的时候,使用懒惰型

懒惰型元字符的写法很简单,只需要在贪婪性元字符上加上一个?后缀即可 例如:*?  +?  {n,}?

总结:所有元字符如果要匹配,都必须用它的转义序列,当在字符集合中使用元字符时,不需要转义,当然转义也没错

第六章,位置匹配

1,单词边界

例如下面一句话: my cat is good,cater is life。 句子是随便写的

想在上面句子中找到cat,单独使用 cat 取查找的话,会查出 cat 和 cater,这里我们就要定义边界,用来只取到这个词,例如: \bcat\b  两次是空格 ,这样取出来就是了

2,字符串边界

用来定义字符串结尾的有两个,一个是用来定义字符串的开头^,另一个用来定义字符串的结尾$

3,分行匹配模式

使用(?m) 启用分行模式,在正则表达式前面使用,分行模式匹配将使正则表达式引擎把行分隔符当做一个字符串分隔符来对待,^不仅匹配正常的字符串开头,还将匹配行分隔符(换行符)后面的开始位置

警告: 有许多正则表达式不支持(?m)

总结: \b 用来指定一个单词边界, ^和$ 来指定字符串边界(字符串的开头和结尾)

第七章,使用子表达式

 首先提两个问题,什么是子表达式?什么时候用到子表达式呢?

子表达式是一个更大表达式的一部分,把一个表达式划分为多个子表达式的目的是为了把子表达式当做一个元素来使用,子表达式用(和)括起来

比如我们使用上面的正则表达式来匹配下面的句子,它的本意是想匹配两个连续的 空格( ),但是{2,} 只能匹配到紧挨他前面的一个字符,那是一个分号,最终匹配到的是 ;; 匹配到多个分号,这个时候就需要用到我们的子表达式,这样子表达: ( ){2,}

下面使用正则查找一个ip号: \d{1,3} 在这个模式中重复了4次,可以简化 (\d{1,3}\.){3}\d{1,3}

分析:{3} 把前面的 \d{1,3}重复了3次,用 \d{1,3} 来匹配ip地址的最后一组

下面在分析一个例子: 他的本意是想匹配到 1967, 最终匹配到的结果是 19 。这里 | 表示 或运算符, 19|20 匹配到的结果是 19或者20,这里为什么只匹配到19呢,因为 | 运算符,把 19 和 20\d{2} 分成两部分, 这里需要给到一个字表达式  (19|20)\d{2} 这样就可以了

1,子表达式 的嵌套

上面是一个正确ip地址的匹配,(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))

我认为这是还是有错误的,如果第一位是0呢,这个ip地址正确么,当然这只是作为 子表达式的一种学习方式

把必须匹配的情况考虑周全并写出符合的正则表达式是简单的,但把不需要匹配的也考虑周全,并确保他们都被排除在结果以外往往不是那么容易

怎样编写对的正则表达式:当你在构造一个正则表达式的时候,一定要把你想匹配的和不想匹配的详细的定义清楚

子表达式常见用途:对重复次数的元字符做出精确的设定和控制,允许嵌套使用

第八章,回溯引用:前后一致匹配

 那么什么是回溯呢,记得以前看到这个词语就感觉自己不懂,结果后来真没有懂,回溯:向上推导,根据预览所了解,这里是向上推导找出重复的字符集合。

前面已经学习了很多知识,大概能解决不少问题,为什么还要学习回溯呢,这里举个例子:

有下面一段 html 的字符串:

<body>
<h1>sdfgkl</h1>
content to you <br/>
<h2>werlcom</h2>
info to me you
<h2>I am a big</h2>
you is goods
<h2>sdfdsf</h3>
</body>

 这里我们要获取到 h1 标签 到 h6标签的内容,那么怎么使用正则呢?

可能大家想到了前面学到的集合:把每一个数字当做一个集合来,<[hH][1-6]>.*</[Hh][1-6]> 这样子来

这里的全局查找也就是我们常说的贪婪型,但是匹配到了 一对错误的 <h2>sdfdsf</h3> 标签

如果用 懒惰型呢,匹配到的呢:

通过以前学过的知识,始终达不到我们想要的效果,那么这里就要学习一下回溯:

回溯引用允许正则表达式引用前面的匹配结果,如果这样的话,我们在匹配 h1或者 h2 标签的时候,就能够配对的找出,不怕错误的 h标签。

怎么用回溯匹配上面的 h标签内容呢?

<[hH]([1-6])>.*</[Hh]\1>

下面的是结果:

完美的解决了我们遇到的问题,没有匹配到错误的 <h2>sdfdsf</h3> 标签内容

在上面的这个正则表达式中,<[hH]([1-6])>.*</[Hh]\1> 里面有一个 \1,这里称为 回溯引用,用它来匹配前面划分出来的子表达式,当子表达式中的[1-6] 匹配到1的时候,\1 也匹配 1, 匹配到2 的时候 \1 也匹配 2,所以不会出现 h2 h3 这样的标签。

\1 也代表着 模式(这里指正则表达式)里的第一个子表达式,\2 代表这 第二个子表达式,\3 代表着第三个,依次轮推。

警告  回溯引用只能引用 模式中 的子表达式 ( 和 ) 括起来的 正则表达式片段

提示      回溯引用通常 从 \1 开始计数

子表达式是通过它们的相对位置来引用的,在这里引出了 命名捕获

借用 这一章的标题,回溯引用,前后一致匹配

不同的正则表达式在实现回溯引用的语法中有着巨大的差异,这里指的是不同的编程语言中

回溯引用在文本匹配和文本替换操作中非常重要

第九章,前后查找

为什么要使用前后查找,是因为我们在使用正则表达式的时候需要标记要匹配的位置,对某一位置前后进行查找 可称为 前后查找。

举个例子:在一段html文本中,我们想找出标题,也就是 <title>和</title>之间的文字,不包含title 标签,你会怎么做呢?

上面一般使我们想到的,但是我们所要的结果是不包含title的,那么就需要用到前后查找了。

写到这里的时候,突然想到自己前几天在采集中遇到的一个问题,当时记得是采集一个数字,但是不能单独的去匹配,前面有一个关键字 年收益率,每次我想匹配单纯的年收益率数字的时候,我都要先匹配一次完整的包含年收益率这几个文本,然后再使用 \d 匹配数字,来达到我的效果,有点笨拙,如果之前我会前后查找的会,应该就不会那么麻烦了,可见,前后查找还是重要的。

1,向前查找

向前查找指定了一个必须匹配但不在结果中返回的模式,语法是:一个向前查找其实就是一个以 ?= 开头的子表达式,需要匹配的文本跟在 = 的后面

举个例子:取出下面 url 地址中的协议名

http://www.baidu.com
https://www.baidu.com
ftp://192.168.1.1

  黄色是 匹配到的结果,通过向前查找 匹配到 :前面的内容,但不包含: 

如果我们不使用 向前查找: 使用 .+(:)

   是不是我们要的内容呢

注意:向前和向后查找本身是有返回结果的,只是这个结果的字节长度永远是0而已,因为前后查找也被称为零宽度匹配

任何一个子表达式都可以转换为向前查找表达式,只要给它加一个 ?= 前缀即可

2,向后查找  (向后查找也是使用同样的方法) 向后查找操作符使用 ?<= ,在向前查找?= 中间加了一个 < 小于号

在向后查找测试中,使用多个网页版的正则工具都无法使用,于是使用一款 正则表达式工具 regex match tracer

点击下载

sdfsd $87
cvxvx $98.2
iuo $99.3

 目的是要匹配 所需要数字,$ 后面的

  结果ok,找到需要的内容 ,在测试中,里面的 . 可以 转义也可以不转义

3,向前查找和向后查找结合使用

通过向前向后查找来实现我们刚才要匹配的title内容

注意: 为减少歧义,我在上面< 前面都 加了 \ ,进行了转义,其实不转义也是可以的

4,对前后查找取非

我们一般说的前后查找指的是正前后查找,在前后查找中还有一种不太常用的用法叫做负前后查找,从字面上理解 肯定是和 正前后查找相对的

上面是负前后查找匹配到的内容,和正前后查找匹配到的刚好相反

通过以上发现,正前后查找中使用 = ,负前后查找 使用 !

有了前后查找,我们就可以对最终的匹配结果包含且只包含哪些内容,做出更精确的控制

第十章,嵌入条件

 也就是在表达式的内部嵌入条件处理功能。

1,正则表达式里的条件(嵌入条件也使用 ?)

嵌入条件不外乎两种情况:根据一个回溯引用来进行条件处理,根据一个前后查找来进行条件处理。

2,回溯引用条件

用来定义这种条件的语法是 (?(backreference)true-regex),其中 ?表名这是一个条件,括号里的backreference 是一个回溯引用,true-regex 是一个 只在backreference 存在时才会被执行的子表达式

举例:我们要匹配下面字符中的 img标签,如果img标签是一个连接(在<a>标签内),你还要把整个连接匹配出来

 

<td>
<a href="/home"><img src="/images/home.gif"></a>
<img src="/images/spacer.gif">
<a href="/serch"><img src="/images/search.gif"></a>
<img src="/images/spacer.gif">
<a href="/help"><img src="/images/help.gif"></a>
</td>

 

 使用 (<a\s+[^>]+>\s*)?<img\s+[^>]+>(?(1)\s*</a>) 进行匹配

如何匹配的呢:首先使用 (<a\s+[^>]+>\s*) 匹配到 <a> 标签,这个标签可有可无,其中的 [^>] 指的是 非> 号的任何字符,<img\s+[^> 匹配一个 <img>标签及任意属性,(?(1)\s*</a>) 是一个回溯引用条件,?(1) 的含义是:如果第一个回溯引用(在本例中是 <a>)存在, 则使用 \s*</a> 继续匹配。

注意: ?(1) 检查第一个回溯引用是否存在,在条件里 回溯引用编号不需要转义,因此,?(1) 是正确的,?(\1) 是不正确的

上面使用的模式只是在给定模式满足条件的情况下才执行一个表达式,条件还可以有否则表达式,只在给定的回溯引用不存在时,才会被执行

用来定义这种条件的语法是: (?(backreference)true-regex|false-regex),这个语法接受一个条件和两个将分别在这个条件成功和失败执行的子表达式。

 

(\()?\d{3}(?(1)\)|-)\d{3}-\d{4} 分析:

(\()? 匹配一个可选的 左括号,做为一个表达式,\d{3} 匹配三个数字 ,(?(1)\)|-) 是一个回溯引用条件,它将根据条件是否满足而去匹配) 和  -,如果(1)存在,也就是找到了一个 左括号,\) 必须被匹配,否则 - 必须被匹配,这样一来只有配对的括号能被匹配,如果没有括号或括号不配对,电话号码中的区号和数字之间的 - 必须被匹配

3,前后查找条件

前后查找条件指的是,只在向前或向后查找的操作取得成功的条件下,才允许一个表达式被引用

定义一个向前查找或向后查找条件的语法与定义一个回溯引用条件的语法大同小异==只需把回溯引用(括号里的回溯引用编号)替换为一个完整的前后查找表达式就行了

\d{5}(?(?=-)-\d{4}) 讲解如下:

\d{5} 匹配5个数字,(?(?=-)-\d{4}) 向前查找条件,这个条件使用了(?=-)来匹配一个连字符,如果匹配成功,那个连字符存在,将匹配连字符和后面的4位数字,(?=-) 前面的? 指的是一个查找条件哦,不要搞晕了

基本上正则表达式的一些基本语法都学习和了解了,后面会学习一些常用的例子,待续

 

正则表达式工具 regex match tracer

点击下载

posted @ 2014-11-03 23:44  诚人小李  阅读(959)  评论(0编辑  收藏  举报