正则表达式与通配符

警告
本文最后更新于 2020-09-02,文中内容可能已过时。

正则表达式在字符串处理时使用的非常普遍,这次来学习正则的相关知识,分别结束通配符、正则及它们的区别。

1. 通配符

首先明确场景,通配符和正则表达式都是做匹配的,匹配常见的场景有

  1. Office 系列,尤其是 Word 中的查找和替换;
  2. 常用的文本编辑器中的查找和替换,比如 Win10 下的记事本、VSCode、Typora 等
  3. Win10 或 Linux 命令行输入各种命令时使用
  4. 编程时进行字符串匹配使用

Office 系列的查找和替换默认都是正则表达式,但可以在选项中勾选使用通配符,而且 Office 系列的通配符很简单,只有三种:问号(?)、星号(*)、波形符(~)。

  • 使用问号(?)查找任何单个字符,例如 s?t 找到 “sat” 和 “set”。
  • 使用星号(*)查找任意数量的字符,例如 s*d 可以找到 “sad”。
  • 使用波形符(~),后跟?、* 或 ~ 查找问号、星号或其他波形符,其实就是用来转义。

常见的文本编辑器在尝试后发现基本都只能使用全字匹配,VSCode 可以勾选使用正则表达式。

命令输入是典型的通配符使用场景,比较出名的是 Linux 下的 glob,用来匹配文件路径名。glob 的语法也很简单

  • ? 匹配任意单个字符
  • * 匹配任意字符串,包括空串
  • […] 匹配方括号中任意一个字符,比如[abc]匹配字符a, b, c中任意一个,方括号中不可为空。同时,可以使用 - 放在方括号中表示范围,使用! 放在方括号中表示非

基本上 glob 通配符可以算作正则的简化。

2. 正则表达式

正则的目的是字符串模式匹配,实现查找与替换,正则的基本原理是有限状态自动机。

一个正则表达式的基本组成元素包括字符和元字符

  • 字符就是普通的计算机字符,比如字母、数字等
  • 元字符是某类有特定含义的字符,比如 ^ 和 * 等

2.1 字符

单个的字符比如字母、数字等,是它们原本的含义。

一些功能字符无法直接表示,比如换行、回车等,需要在普通字符前面加上转义元字符 \ 来表示,比如

字符字符描述
\n换行符
\f换页符
\r回车符
\t制表符
\v垂直制表符
[\b]回退符,之所以使用[]符号是避免和\b重复

还有一部分是正则表达式特殊的规定,在普通字符前面使用转移字符表达特定含义,如下表

字符描述
\w任意一个字母或数字或下划线
\W任意一个字母或数字或下划线以外的字符
\s空白字符,比如空格、tab、换行、换页
\S非空白字符
\d数字字符,0~9
\D非数字字符
\b单词边界
\B非单词边界

这里解释一下单词边界的含义,一般情况,单词指的是字母、数字、下划线构成的字符串,边界指的是一个具体的位置,而不是一个字符,比如对于字符串 example:a+b=3,用 | 表示位置,如下所示

1
|e|x|a|m|p|l|e|:|a|+|b|=|3|

单词边界指的就是下面几个位置

1
|example|:|a|+|b|=|3|

非单词边界就更好理解了,不是单词边界的就是非单词边界

1
e|x|a|m|p|l|e:a+b=3

2.2 元字符

元字符是具有特定语义的字符,比如上面的转义字符 \ 就是一个元字符,用于改变普通字符的含义。正则表达式中定义的常用元字符包括

元字符描述
.匹配除了换行符外的任意一个字符
匹配同一个字符出现 0 次或 1 次
+匹配同一个字符出现 1 次或多次
*匹配同一个字符出现 0 次或 1 次或多次

量词

?, +, *三个元字符我们通常称作量词,因为它们的作用是表示某个字符出现了多少次,除此之外,还可以声明更具体的出现次数

  • {n} 表示字符出现 n 次,比如a{2},匹配aa
  • {m,n} 表示字符出现最少 m 次,最多 n 次,比如a{1,3},可以匹配aaa、aa、a
  • {m,} 表示字符出现 m-∞次,优先匹配∞次,比如a{1,},可以匹配 aaaa…

注意正则默认是贪婪的,凡是表示范围的量词,都优先匹配上限而不是下限。比如

1
a{1, 3} // 匹配字符串'aaa'的话,会匹配aaa而不是a

取消贪婪模式可以在量词后加 ?

1
a{1, 3}? // 匹配字符串'aaa'的话,会匹配a而不是aaa

边界

边界的匹配是一个很重要的需求,主要包括三种

  1. 使用 ^ 匹配开头,比如 ^abc 表示以 abc 为开头的字符串
  2. 使用 $ 匹配结尾,比如 abc$ 表示以 abc 为结尾的字符串
  3. 使用 \b 匹配单词边界,比如我们想搜索单词 cat,结果 scattered 也会被搜索到,这时候就可以使用 \b 表示单词边界,使用方法为 \bcat\b

集合

很多时候我们还需要匹配一个字符集合,比如,匹配 a,b,c 这三个字符中的任意一个,这种需求通过一对方括号实现

1
[abc]

使用连字符可以表示一个范围

1
[0-9] // 等于[0123456789]

使用 ^ 可以表示除了方括号中的字符以外的其它字符

1
[^abc] // 表示除了 a,b,c 以外的其它字符

字符串的选择通过 | 来表达,实际上就是或的概念,比如

1
123|456  // 表示匹配 123 或者匹配456

修饰符

在正则表达式的末尾添加修饰符可以更改某些正则默认的规则

  • i ,正则默认区分大小写,i 可以忽略大小写
  • g,正则遇到第一个匹配的字符串就会结束,g 可以匹配所有符合条件的字符串
  • m,正则默认遇到换行结束,不匹配多行文本,m 可以匹配多行文本

举一个多行匹配的例子,在下面的文本中匹配 I am scq000 这个句子,使用的正则表达式为 /^I am scq000\.$/m

1
2
3
I am scq000.
I am scq000.
I am scq000.

2.3 子表达式

利用上面的语法可以构成基本的正则表达式,通过对基本正则表达式的组合,可以推导出无限复杂的正则表达式,使用的基本思想是嵌套递归和自身引用,涉及的概念包括分组、回溯引用、预搜索。

分组

就是利用成对的小括号 () 将一个正则表达式包围,包围以后就表示这是一个子表达式,子表达式是构成复杂正则表达式的基本单位。

一个简单的分组使用如下

1
(abc){2} // 匹配abcabc

回溯引用

回溯引用指的是整个正则表达式后面的部分引用前面子表达式匹配到的字符串,类似于将前面的子表达式看作了一个变量。基本结构如下

1
(子表达式1)(子表达式2)(子表达式3)\1\2\3

\1代表第一个子表达式、\2代表第二个子表达式、\3代表第三个子表达式。以此类推,\0 一般表示整个表达式。

注:有些语言(比如JS)使用 $ 而不是 \

比如要在一段文本里匹配两个连续相同的单词,用简单的正则只能保证两个单词符号相同的规则,但不能保证它们相同

1
Hello what what is the first thing, and I am am scq000.

使用回溯就可以很容易的获得结果: \b(\w+)\s\1\b,后面的 \1 会再一次匹配前面的字符串本身。

如果某个子表达式不想被引用,可以使用 (?:regex)这样的表达方式,比如 \b(?=\w+)\s\1\b 这样的表达式就无法匹配到之前获得的结果了

预搜索

预搜索是为了应对四种情况

  1. 要求字符串前面必须是另一个字符串,比如要求 ple 前面必须是 ap,正则表达式为

    1
    
    (?<=ap)ple
  2. 要求字符串前面不能是另一个字符串,比如要求 ple 前面不能是 ap,正则表达式为

    1
    
    (?<!ap)ple // 可能匹配到 people
  3. 要求字符串后面必须是另外一个字符串,比如要求 happ 后面必须是 ily,正则表达式如下

    1
    
    happ(?=ily)
  4. 要求字符串后面不能是另外一个字符,使用 ! 表示非的含义即可

    1
    
    happ(?!ily) // 表示happy 不能是 ily,可能匹配到 happy

还有一些正则可能用到的工具

  • Regulex:用有限状态自动机可视化正则表达式
  • OKTools:在线正则表达式测试

3. 区别

通配符和正则表达式的区别比较明显,

  1. 通配符语法比正则表达式简单很多
  2. 通配符一般用于文件路径名匹配,正则表达式一般用于字符串匹配

所以在命令行中,与路径相关的命令,比如 cp, ls, find 等,都使用通配符;而与字符串匹配相关的命令,比如 grep,使用正则表达式。

Office 或者编程基本上是对字符串的匹配,因此一般使用正则表达式。

参考

[1] Office 支持,在搜索中使用通配符

[2] 掘金,正则表达式不要背

[3] 知乎,正则表达式教程-语法篇

[4] CSDN,通配符和正则表达式的区别

支付宝
微信
0%