正则表达式的用途

  • 搜索:查找特定的信息
  • 替换:查找并编辑特定的信息

匹配单个字符

匹配纯文本

一段纯文本就是一个正则表达式,比如“asdf”,这本身就是一个正则表达式,或者我们称之为模式(pattern),开始匹配的时候他将只匹配原始文本里的”asdf“。 纯文本一般会有以下几种情况:

  • 有多个匹配结果 有多个匹配结果的时候,绝大多数正则表达式默认的是只返回第一个匹配结果。绝大多数正则表达式的实现都提供了一种能够把所有的匹配结果全部找出来的机制,通常返回一个数组或者其专用格式。

  • 字母大小写问题 正则表达式是区分字母大小写的,所以”asdf“是不会匹配”ASDF“的,不过绝大多数正则表达式实现支持不区分字母大小写的匹配操作(例如javascript可以用i标志来强制执行一次不区分大小写的匹配)。

匹配任意字符

特殊字符:. 字符用途:匹配任何一个单个的字符,字母,数字以及它本身(这相当于windows里的”?”或者SQL里的”_“)。(P.S:事实上,大多数正则表达式的实现里面”.”只能匹配除换行符以外的任何单个字符。) 当然,相应的,”..”就表示的是两个任意字符。 需要注意的一点是:正则表达式匹配的并不总是整个字符串,而是与某个模式相匹配的字符。

匹配特殊字符

假如我们在匹配里面需要用到”.”字符本身时,我们需要在”.”的前面加上一个元字符”"来对它进行转义。 转义字符:\ 字符用途:表示\后面的字符不是在正则表达式里的含义,而是它自身的含义。 相应的,如果要匹配”"本身的话就要输入”\"。

匹配一组字符

匹配多个字符中的某一个

在正则表达式里面我们可以用”[“和”]“来定义一个字符集合,在这个集合里的所有字符都是该集合的组成部分,匹配的结果是能够与该集合里的任意一个成员相匹配的文本。

利用字符集合区间

在使用正则表达式的时候我们会经常用到一些字符区间,比如:”0~9”,”A-Z”等,为了简化字符区间的定义,正则表达式提供了一种特殊的元字符”-“。 元字符:- 字符用途:一段字符区间表示时用于首尾字符之间的连接,称之为连字符。明显”[0123456789]“与”[0-9]“是等价的。

另外比如:

  • A-Z:匹配A到Z的所有大写字母
  • a-z:匹配a到z的所有小写字母
  • A-z:匹配A到z的所有大小写字母以及”[“和”^”,因为这个是按照ASCII字符表的顺序的,Z到a中间包含上述两个字符。

需要注意的是,定义一个字符区间的时候,一定要避免区间的尾字符小于其首字符,比如[3-1],这种区间是毫无意义的,往往会导致整个模式的失败。还有一点,”-“只能用在”[“和”]“之间,在这以外,”-“是一个普通字符,只能与它本身匹配,所以在正则表达式里不需要被转义。 另外,在同一个字符集和里面可以给出多个字符区间。

取非匹配

字符集合通常用来指定一组必须匹配其中之一的字符,但是有的情况下我们需要反过来做,给出一组不需要得到的字符,这时我们可以用元字符”^”来对一个字符集进行取非匹配。 元字符:^ 字符用途:对一个字符集进行取非匹配。

使用元字符

对特殊字符进行转义

元字符是一些在正则表达式里有着特殊含义的字符,所以这些字符就不能代表它们本身,所以就只能使用\来进行转义。

匹配空白字符

元字符大致分为两种:

  1. 用来匹配文本的。比如:.
  2. 正则表达式的语法所要求的。比如:[]

在进行正则表达式搜索的时候我们会经常遇到需要对原始文本里的非打印空白字符进行匹配的情况,我们可以使用下面的特殊元字符来输入它们。

[\b]:回退(并删除)一个字符(Backspace键)
 \f :换页符
 \n :换行符
 \r :回车符
 \t :制表符(Tab键)
 \v :垂直制表符

匹配特定的字符类别

  • 匹配数字(与非数字) 匹配数字和非数字的类元字符如下:
    • \d:任何一个数字字符(等价于[0-9])
    • \D:任何一个非数字字符(等价于[^0-9])
  • 匹配字母和数字(与非字母和数字) 匹配字母和数字和非字母数字的类元字符如下:
    • \w:任何一个字母数字字符(大小写均可)或下划线字符(等价于[a-zA-Z0-9_])
    • \W:任何一个非字母数字或非下划线字符(等价于[^a-zA-Z0-9_])
  • 匹配空白字符(与非空白字符) 用来匹配所有空白字符的类元字符如下:
    • \s:任何一个空白字符(等价于[\f\n\r\t\v])
    • \S:任何一个非空白字符(等价于[^\f\n\r\t\v])
  • 匹配十六进制或八进制数值
    • 使用十六进制值 在正则表达式里面,十六进制数值要用前缀\x来给出。比如,\x0A对应于ASCII字符10(换行符),其效果等价于\n
    • 使用八进制数值 在正则表达式里,八进制数值要用前缀\0来给出,数值本身可以使两位或者三位数字。比如,\011对应于ASCII字符9(制表符),其效果等价于\t

使用POSIX字符类

对元字符以及各种字符集和进行讨论,必须要提到POSIX字符类。POSIX字符类是许多(但不是所有)正则表达式都支持的一种简写形式。POSIX字符类如下:

[:alnum:]:任何一个字母或数字(等价于[a-zA-Z0-9])
[:alpha:]:任何一个字母(等价于[a-zA-Z])
[:blank:]:空格或者制表符(等价于[\t ])
[:cntrl:]:ANSCII控制字符(ANSCII0到31,加上ANSCII 127)
[:digit:]:任何一个数字(等价于[0-9])
[:graph:]:和[:print:]一样,但不包含空格
[:lower:]:任何一个小写字母(等价于[a-z])
[:print:]:任何一个可打印字符
[:punct:]:既不属于[:alnum:]也不属于[:cntrl:]的任何一个字符
[:space:]:任何一个空白字符,包括空格(等价于[^\f\n\r\t\v])
[:upper:]:任何一个大写字母(等价于[A-Z])
[:xdigit:]:任何一个十六进制数字(等价于[a-zA-Z0-9])

重复匹配

有多个重复匹配

  • 匹配一个或多个字符 要想匹配同一个字符(或字符集合)的多次重复,只要简单地给这个字符(或字符集合)加上一个+字符作为后缀就行了。+匹配一个或多个字符(至少一个,不匹配零个字符的情况)。

  • 匹配零个或多个字符 要想匹配字符(或字符集合)连续出现零次或多次的情况,只要简单的给这个字符(或字符集合)加上一个*后缀就行了。

匹配零个或一个字符

?只能匹配一个字符(或字符集合)的零次或者一次出现。

匹配的重复次数

该功能使用{和}两个元字符来实现。

  • 为重复匹配次数设定一个精确的值 如果想为重复匹配次数设定一个精确的值,把那个数字写在{和}之间即可。
  • 为重复匹配次数设定一个区间 为重复匹配次数设定一个区间,也就是说为重复匹配次数设定一个最小值和一个最大值。这种区间必须以{2,4}这样的形式给出。{2,4}的含义是最少重复2次,最多重复4次。
  • 匹配至少重复多少次 省略最大值部分即可,例如{3, }表示至少重复三次。

防止过度匹配

*+都是贪婪型字符,它们会尽可能地从一段文本的开头一直匹配到这段文本的末尾,而不是从这段文本的开头匹配到碰到第一个匹配是为止。可以通过使用这些元字符的懒惰型版本,即直接在后面加上一个?后缀即可。

贪婪型 懒惰型
* *?
+ +?
{n, } {n, }?

位置匹配

边界

有时候匹配一个字符,比如cat,在匹配的结果中可能除了cat外还有scattered这样的字符,所以就需要使用边界限定符,也就是在正则表达式中用一些特殊的元字符来表明我们想让匹配操作在什么位置(或边界)发生。

单词边界

第一种边界(也是最常用的边界)是有限定符\b指定的单词边界。它用来匹配一个单词的开始或者结尾。可以限定以同一串字符开头或者结尾的单词或者一个完整的单词。

其实\b的位置相当于\w(能够用来构成单词的字符)和\W(不能用来构成单词的字符)中的一个字符。

如果想表明不匹配一个单词边界,即字母数字下划线之间,或者非字母数字下划线之间,请使用\B。

字符串边界

用来定义字符串边界的元字符有两个:一个是用来定义字符串开头的^,另一个使用来定义字符串结尾的$。

分行匹配

一般使用(?m)来启用分行匹配模式,例如:(?m)^\s//.$(但好多正则表达式不支持这个)。

分行匹配模式将使得正则表达式引擎把行分隔符当做一个字符串分隔符来对待。在分行匹配模式下,^不仅匹配正常的字符串开头,还将匹配行分隔符(换行符)后面的开始位置(这个位置是不可见的);类似的,$不仅匹配正常的字符串结尾,还将匹配行分隔符(换行符)后面的结束位置。

使用子表达式

什么是子表达式

子表达式

用途:把一组字符编组为一个字符集合。

子表达式的嵌套

回溯引用:前后一致匹配

子表达式的另一个用途:定义回溯引用

回溯引用有什么用

比如匹配html中的标题标签(<h1\><h6\>,以及其结束标签)。开始可能想到<[hH][1-6]>.*?</[hH][1-6]\>,但是很有可能匹配到<h2\>……</h3\>这样的非法文本。这时候就要使用回溯引用。

回溯引用匹配

回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式,可以将回溯引用想象成变量。不同的正则表达式在回溯引用方面的语法有着比较大的差异。

回溯引用的匹配通常从1(模式中定义的子表达式的位置)开始计数。在许多实现里,第0个匹配可以用来代表整个正则表达式。

回溯引用在替换操作中的应用

同一个子表达式可以被引用任意多次,只要在需要用到的地方写出它的回溯引用就行了。

  • 大小写转换
    • \E:结束\L或\U转换
    • \l:把下一个字符转换为小写
    • \L:把\L到\E之间的字符全部转换为小写
    • \u:把下一个字符转换为大写
    • \U:把\U到\E之间的字符全部转换为大写

前后查找

向前查找

向前查找指定了一个必须匹配但不在结果中返回的模式。向前查找实际就是一个子表达式,而且从格式上看也确实如此。从语法上看,一个向前查找模式其实就是一个以?=开头的子表达式,需要匹配的文本跟在=后面(但返回结果中不包含此文本)。

比如要匹配一个网址的开头协议名的时候使用模式.+(?=:)就可以提取出来协议名,并且不包含协议名与主机名之间的:

向前查找和向后查找匹配本身其实是有返回结果的,知识这个结果的字节长度永远是0而已。因此,前后查找操作又是也被称为零宽度匹配操作。

任何一个子表达式都可以转换为一个向前查找表达式,只要给它加上一个?=前缀即可。在同一个搜索模式里可以使用多个向前查找表达式,他们可以出现在模式里的任意位置。

向后查找

向后查找也就是查找出现在被匹配文本之前的字符(但返回结果中包含此字符),向后查找操作符是?<=

需要注意的是:

  • 向前查找模式的长度是可变的,它们可以包含.和+之类的元字符,所以它们非常灵活。
  • 向后查找模式的长度只能是固定长度——这是一条几乎所有的正则表达式实现都遵守的限制。

把向后查找和向前查找结合起来

比如要匹配html的<title>标签中间的文本,但是不包含标签本身,此是可以使用模式:(?<=\<[tT][iI][lL][eE]>).*(?=</[tT][iI][lL][eE]>)

对前后查找取非

各种前后查找操作符 + (?=) :正向前查找 + (?!) :负向前查找 + (?<=) :正向后查找 + (?<!) :负向后查找

嵌入条件

为什么嵌入条件

并非所有的正则表达式都支持条件处理

正则表达式里的条件

正则表达式里的条件要用?来定义。事实上已经见过几个特定条件了:

  • ?匹配前一个字符或表达式——如果它存在的话。
  • ?=和?<=匹配前面或后面的文本——如果它存在的话。

嵌入条件语法也使用了?,有以下两种情况:

  • 根据一个回溯引用来进行条件处理。
  • 根据一个前后查找来进行条件处理。
回溯引用条件

回溯引用条件只在前面的子表达式搜索取得成功的情况下才允许使用一个表达式。 语法如下:

?(backreference)true-regex
?(backreference)true-regex|false-regex
  • ?:表明条件
  • backreference:一个回溯引用
  • true-regex:一个只在backreference存在时才会被执行的子表达式。
  • false-regex:一个只在backreference不存在时才会被执行的子表达式
前后查找条件

前后查找条件只在一个向前查找或者向后查找操作取得成功的情况下才允许一个表达式被使用。定义一个前后查找条件的语法与定义一个回溯引用条件的语法大同小异——只需把回溯引用(括号里的回溯引用编号)替换为一个完整的前后查找表达式就行了。