正则表达式

最近项目中用到正则表达式,每次用到,就查一下看一下正则表达式30分钟入门教程,不久后又忘记,对一些复杂一点的也不是很理解,导致每次都花费很多时间,就彻底整理一遍,找了《正则表达式必知必会》参考,整理出匹配的表达式和文本范例,下次看也高效一点。

定义:正则表达式就是用于描述这些规则的工具。
用途:

  1. 查找特定的信息(搜索)
  2. 查找并编辑特定的信息(替换)

元字符

代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或者结束
^ 匹配字符串的开始
$ 匹配字符串的结束
  • 匹配单个字符

    表达式:my

    文本:My name is yuan,her name is li,my brother name is ling.

  • 匹配数字

    表达式:\d

    file1.txt

  • \s表示任意的空白符,包括\r空格,\n换行,\tTab键,分别匹配空格,Tab键,换行。

    表达式:txt\sf

    文本:
    file1.txt file2.txt  file3.txt
    file4.txt

单词边界

边界可以由\b进行限定,\b不匹配任何字符,其只匹配一个位置,这个位置位于一个能否用来构成单词的字符(也就是和\w相匹配的字符)和一个不能用来构成单词的字符(也就是与`\W相匹配的字符)之间。

  • 匹配开头为n的单词,需要添加元字符\b,否则会匹配yuan ling

    表达式:\bn

    文本:My name is yuan,her name is li,my brother name is ling.

  • 匹配li而不匹配ling\b匹配了单词的开头和结尾

    表达式:li\b

    文本:My name is yuan,her name is li,my brother name is ling.

  • ^也是限制匹配词的开始,不过是针对整个字符串,匹配其中开头的My,而不是中间的my,表达式为:^my$则是限制字符串的结束

反义符

代码 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意不是数字的字符
\B 匹配不是单词开头或者结束的位置
[^X] 匹配除了X以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的人任意字符
  • [0-9]表示匹配0-9的数字,和\d一样。 [a-z]表示匹配a-z的所有单词,[A-Z]表示匹配A-Z的所有单词,[a-zA-Z]则匹配所有大小写单词单词。

  • 连字符-在字符集[ ]之间才有特殊的意义,在字符集外只是一个普通字符,匹配其本身不需要被转义。

转义符

1
2
// 以下字符有特殊意义,匹配该字符需要添加 \
* . ? + $ ^ [ ] ( ) { } | \ /
  • 上面的字符在字符集[ ]中会被解释为普通字符,不需要转义,但为了避免误解,最好进行转义。

限定符

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

在给一个字符集合加上限定符的时候,必须把限定符放在其外面

  • 表达式:n\d* 文本:n n1 n123
  • 表达式:n\d+ 文本:n n1 n123
  • 表达式:n\d? 文本:n n1 n123
  • 表达式:n\d{1} 文本:n n1 n123
  • 表达式:n\d{1,} 文本:n n1 n123
  • 表达式:n\d{1,2} 文本:n n1 n123

贪婪与懒惰

正则匹配默认情况下,是匹配尽可能多的字符,即贪婪匹配。如果需要匹配尽可能少的字符,就需要通过限定符转化为懒惰匹配。

代码 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或者1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

注:由于匹配到的文本想通过高亮显示出现,没办法使用代码块,导致<H1>会被网页识别为标题,因此,下面通过替代<替换>,一方面在Markdown中正确显示,又在网页中可以正确显示

  • 匹配《H1》标签之间的内容,默认是贪婪匹配,会匹配尽可能的多。

    表达式:《H1》.*《/H1》

    文本:
    《H1》Welcome to my Homepage《/H1》Content is divided into two sections.《H1》This is not valid HTML《/H1》

  • 过度匹配,需要采用懒惰模式,匹配尽可能的少

    表达式:《H1》.*?《/H1》

    文本:
    《H1》Welcome to my Homepage《/H1》Content is divided into two sections.《H1》This is not valid HTML《/H1》

子表达式

子表达式通过(和)把一系列元素括起来,把其当成一个独立元素使用。

默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的组号为1,第二个为2,以此类推。第0个匹配可以用来代表整个正则表达式。组号必须添加\进行转义。

  • 在表达式((A)(B(C)))中,存在4个分组

    1
    2
    3
    4
    ((A)(B(C)))  
    (A)
    (B(C))
    (C)

捕获

代码 说明
(exp) 匹配exp,并捕获文本到自动命名的组里
(?\exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给词分组分配组号
  • 匹配年份,如果不使用子表达式,则只会匹配19,因为|操作符是吧位于它左边和右边的两个部分都作为一个整体来看待的,就变成匹配1920\d{2}

    表达式:(19|20)\d{2}

    文本:1999

  • ​匹配HTML标题中的内容,会发现,H2 对应的 H3 为错误的,不应该被匹配到

    表达式:《H[1-6]》.*?《/H[1-6]》

    文本:
    《H1》Welcome to my Homepage《/H1》
    Content is divided into two sections.
    《H2》This is not valid HTML《/H3》

  • 如果不想匹配到错误的内容,只要使用回溯引用,确保标签头和标签尾一致。把[1-6]作为一个表达式,在后面使用\1去匹配。

    表达式:《H([1-6])》.*?《/H\1》

    文本:
    《H1》Welcome to my Homepage《/H1》
    Content is divided into two sections.
    《H2》This is not valid HTML《/H3》

  • 如果使用组号进行匹配,在正则表达式复杂的情况下,如果进行修改,比如在前面添加一个分组,所有的分组编号都要进行修改。因此,使用分组重命名就可以避免。

    表达式:《H(?<number>[1-6])》.*?《/H\number》

    文本:
    《H1》Welcome to my Homepage《/H1》
    Content is divided into two sections.
    《H2》This is not valid HTML《/H3》

  • 有时候在使用|的时候,需要用到(),但是实际上,不需要进行捕获,在后面也不需要用到此分组,为了提高效率,可以使用(?:exp)

    表达式:(19|20)\d{2}《H([1-6])》.*《H\2》

    文本:
    1993《H1》head《/H1》

  • 如果使用(?:exp),则没有该分组了,\2也需要换成\1

    表达式:(?:19|20)\d{2}《H([1-6])》.*《/H\1》

    文本:
    1993《H1》head《/H1》

断言

代码 说明
(?=exp) 正向前查找
(?!exp) 负向前查找
(?<=exp) 正向后查找
(?<!exp) 负向后查找

有些时候,匹配到的内容,不是你真正需要的,如果把它搜索出来,再手动去掉,比较麻烦,那么需要被匹配到的文本不包含在最终返回的匹配结果中,也就是“不消费”。

  • 匹配URL地址,把协议名部分提取出来

    表达式:.+(?=:)

    文本:
    http://www.baidu.com
    https://www.baidu.com

  • 提取产品中的价格

    表达式:(?<=\$)[0-9.]+

    文本:
    apple: \$1
    banana: \$2

  • 匹配数量不匹配价格

    表达式:\b(?<!\$)\d+\b

    文本:I paid $30 for 100 apples;

嵌入条件

正则表达式里的条件用?来定义,只有符合条件才执行后面的匹配。

代码 说明
(?(condition)true) 存在则执行true
(?(condition)true\ false) 存在则执行true,错误则执行false
  • 匹配电话号码,如果存在( 则需要),否则需要 -

    表达式:(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}

    文本:
    123-456-7890
    (123)456-7890
    (123-456-7890
    (123)-456-7890

参考

  1. 正则表达式30分钟入门教程
  2. 站长工具 正则测试
  3. 《正则表达式必知必会》

公众号:亦袁非猿

欢迎关注,交流学习