《正则表达式30分钟入门教程》笔记

寒假时做的笔记,现转发到这里。

原文请见此处

本文介绍的是 .Net Framework 4.x 下的正则表达式,但大部分语法可以通用。

入门

假设你在一篇英文小说里查找hi,你可以使用正则表达式hi。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配 hi,HI,Hi,hI 这四种情况中的任意一种。

hi这个表达式会匹配到包含hi的其他单词,如 him, history, high 等等。如果只想匹配 hi 这个词,需要使用\bhi\b

\b匹配这样的位置:它的前一个字符和后一个字符不全是\w\b并不表示单词分隔符,尽管看起来的效果确实如此。

\b是一个元字符(metacharacter)。

*也是元字符,它代表的是数量——它指定*前边的内容可以连续重复使用任意次。

.也是元字符,匹配除了换行符'\n'以外的任意字符。

于是,.*就表示任意数量的非换行符。

​ 假如要匹配hi后面不远处跟着一个Lucy,则可用\bhi\b.*Lucy

元字符\d匹配一位数字(0-9)。如\d\d\d\d表示一个4位的数字。为了简洁,上面的表达式可以简写为\d{4},表示\d要连续匹配4次。而\d{5,12}表示重复的次数不能少于5次,且不能多于12次。

元字符

对中文的特殊处理是由 .Net 正则表达式引擎支持的,不是标准内容。

\s匹配 任意的空白符。这里的空白符指空格,Tab,'\n'和中文全角空格等。

\w匹配 字母或数字或下划线或汉字。

+*类似,不同的是*允许重复任意次(包括0次),而+允许重复1次或更多次。

^匹配 字符串的开始。它匹配位置,下面的$也一样。

$匹配 字符串的结束。

​ 上面两个元字符常常用于验证输入的内容。比如匹配一个5-12位的QQ号,可用^\d{5,12}$

一些例子:

\ba\w*\b 匹配以a开头的单词。这里的单词是指不少于一个的连续的\w

\d+ 匹配1个或更多连续的数字。

\b\w{6}\b匹配刚好6个字符的单词。

字符转义

要查找\这个字符本身,应使用\\

要查找.这个字符本身,应使用\.

其他类同。

重复

正则表达式中所有的限定符(即用于指定数量的代码):

代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

字符类

想要匹配数字、空白等的集合是容易的,因为已经有对应的元字符。如果想要匹配字母的集合,可以在方括号里列出所有备选的字母。如[aeiou]匹配任何一个英文元音字母,[.?!]匹配三种标点中的任何一个。

也可以指定字符范围,如[0-9]等价于\d,而在纯英文场景下,[a-z0-9A-Z]等价于\w

注意[]中的字符不能用空格分隔。如果用了空格,则空格也会被匹配。

分枝条件

正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。

0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。

\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。

使用分枝条件时,要注意各个条件的顺序。例如\d{5}-\d{4}|\d{5}\d{5}|\d{5}-\d{4}匹配的是不同的模式,因为分枝条件总是从左向右匹配,后者的前一个条件会涵盖后一个条件的全部情况。

分组

表示重复的单个字符是容易的:只需要在该字符后面加限定符即可;如果想要表示重复的多个字符,则需要用小括号指定子表达式(又名 分组)。

(\d{1,3}\.){3}\d{1,3} 是一个简单的IPv4匹配表达式。注意IPv4允许前导0。这个表达式有个缺陷,就是它会匹配类似256.300.888.999这种不合法的地址。

标准的Ipv4匹配表达式是((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

反义

若想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义

代码/语法 说明
\W 匹配任意不是字母、数字、下划线和汉字的字符
\S 匹配任意不是空白的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

后向引用

使用小括号指定一个分组后,每个分组会自动拥有一个组号,规则如下:

  • 分组0对应整个正则表达式
  • 从左向右扫描整个表达式,以分组的左括号为准,第一个分组为1,第二个分组为2,依此类推。这样的扫描进行两遍,第一遍只给未命名组分配,第二遍只给命名组分配。
  • 可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权。

后向引用用于重复搜索前面某个分组匹配到的文本。如\1表示分组1匹配到的文本。

例子:

表达式\b(\w+)\b\s+\1\b用来匹配重复的单词,如go gokitty kitty。其中\b(\w+)\b表示一个单词,这个单词被捕获到编号为1的分组中。然后跟1个或更多空白字符(\s+),最后还是分组1捕获到的内容(\1)。

也可以手动指定子表达式的组名。语法为(?<Word>\w+),或将尖括号换成'(?'Word'\w+)。这样这个分组的组名就变成Word。上面例子中的表达式变成\b(?<Word>\w+)\b\s+\k<Word>\b

常用分组语法

  • (exp) 匹配exp,并捕获文本到自动命名的组里。
  • (?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
  • (?:exp) 匹配exp,但不捕获匹配的文本,也不给此分组分配组号。
  • (?=exp)零宽断言。匹配exp前面的位置。
  • (<=exp)零宽断言。匹配exp后面的位置。
  • (?!exp)零宽断言。匹配后面跟的不是exp的位置。
  • (?<!exp)零宽断言。匹配前面不是exp的位置。
  • (?#comment)注释。这种分组没有任何实际意义,仅仅用于注释。

零宽断言

(?=exp)又名零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b)匹配以ing结尾的单词的前面部分。如对字符串I'm singing while you are dancing应用该表达式,将匹配singdanc

(?<=exp)又名零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分。

负向零宽断言

零宽度负预测先行断言(?!exp)断言此位置的后面不能匹配表达式exp。例如\d{3}(?!)匹配三位数字,且这三位数字的后面不能是数字。

零宽度负回顾后发断言(?<!exp)断言此位置的前面不能匹配表达式exp。例如(?<![a-z])\d{7}匹配前面不是小写字母的7位数字。

一个更加复杂的例子:表达式(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。它不匹配标签本身。

注释

如果想要包含注释,最好启用“忽略模式里的空白符”选项,这样在编写表达式时可以任意的添加空格,Tab或换行,这些空格将被自动忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:

(?<=    # 断言要匹配的文本的前缀
<(\w+)> # 查找尖括号括起来的内容
        # (即HTML/XML标签)
)       # 前缀结束
.*      # 匹配任意文本
(?=     # 断言要匹配的文本的后缀
<\/\1>  # 查找尖括号括起来的内容
        # 查找尖括号括起来的内容
)       # 后缀结束

贪婪与懒惰

正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符。如a.*b将会匹配最长的a开始,以b结束的字符串。这称为贪婪匹配

有时我们需要于此相反的懒惰匹配,前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。于是,a.*?b就表示最短的a开始,以b结束的字符串。

例如,对于字符串aababa.*b将匹配整个字符串,a.*?b将匹配aabab

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
posted @ 2022-06-25 10:59  Eslzzyl  阅读(197)  评论(0编辑  收藏  举报