【夯实技术基本功】「底层技术原理体系」全方位带你认识和透彻领悟正则表达式的开发手册
- 前提介绍
- 正则表达式
- 正则表达式的历史
- 正则表达式的定义
- 正则表达式的组成
- 普通字符
- 非打印字符
- 特殊字符
- 限定符
- 限定符案例分析
- 贪婪匹配/非贪婪匹配方式
- 定位符
- 选择组合符
- 后向引用
- 总结心得
前提介绍
目前的软件世界中广泛应用着正则表达式,包括*nix(Linux、Unix等)、HP等操作系统,以及PHP、C#、Java等开发环境和许多其他应用软件。无论是在哪个领域,你都能看到正则表达式发挥重要作用的踪影。使用正则表达式可以通过简洁的代码实现强大的功能。然而,为了追求简洁和强大,正则表达式代码的复杂性较高,从而给学习者带来了一定的困难。因此,学习正则表达式需要付出一些努力,但一旦入门后,并参照一定的参考指南,使用它就会变得相对简单有效。
比如下面这个例子: ^.+@.+\\..+$
,很多人可能被这样的代码吓到过。但如果继续阅读本文,你将能够自如地应用这样的代码。
正则表达式
正则表达式被认为是一种繁琐的工具,因为它的语法可能会让初学者感到困惑。学习使用正则表达式可能需要一些时间和练习,但坚持下去一定会见到回报。相信自己的能力,相信你可以掌握并善用这个强大的工具,它将成为你的利器,为你的工作和学习带来巨大的便利和效果。一旦你掌握了它的应用技巧,你会发现它的强大威力。使用正则表达式可以大大提高你的工作效率,而且成功地运用它会给你带来无比的成就感。
正则表达式的历史
正则表达式的起源可以追溯到人类对神经系统如何工作的早期研究。神经生理学家Warren McCulloch和Walter Pitts开创了一种用数学方式描述神经网络的方法。
-
在1956年,数学家Stephen Kleene在McCulloch和Pitts的基础上发表了一篇名为《神经网事件的表示法》的论文,引入了正则表达式的概念。正则表达式被用来描述他所称之为"正则集的代数"的表达式,因此才采用了"正则表达式"这个术语。
-
随后,发现正则表达式可以应用于使用Ken Thompson的计算搜索算法的一些早期研究中,而Ken Thompson则是Unix的主要发明人。正则表达式的第一个实际应用程序是Unix中的qed编辑器。
正则表达式的定义
正则表达式(Regular Expression)是一种描述字符串匹配模式的工具,可以用于检查一个字符串是否含有特定的子串,对匹配的子串进行替换,或从一个字符串中提取符合特定条件的子串等操作。
例如,在列目录时,使用dir *.txt
或ls *.txt
命令,其中的*.txt
并不是一个正则表达式,因为这里的*
与正则表达式中的*
具有不同的含义。
正则表达式的组成
正则表达式由普通字符(例如字符a到z)和特殊字符(称为元字符)组成的文本模式。正则表达式可以视为一个模板,用来将某个字符模式与要搜索的字符串进行匹配。通过结合不同的普通字符和元字符,可以构建出灵活且强大的正则表达式,以适应不同的匹配需求。
普通字符
正则表达式由所有未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符、所有数字、所有标点符号以及一些特殊符号。正则表达式可以利用这些字符来构建不同的模式,用于匹配、查找或替换文本中的特定内容。
非打印字符
非打印字符指代一些在文本中不可见或无法直接表示的特殊字符。这些非打印字符用于匹配特定的文本模式,通常由转义序列或特殊语法来表示。
\n
:换行符。匹配文本中的换行符。\r
:回车符。匹配文本中的回车符。\t
:制表符。匹配文本中的制表符。\s
:空白字符。匹配任何空格、制表符、换行符等空白字符。\S
:非空白字符。匹配任何非空白字符。\d
:数字字符。匹配任何数字字符。\D
:非数字字符。匹配任何非数字字符。\w
:单词字符。匹配任何字母、数字或下划线字符。\W
:非单词字符。匹配任何非字母、数字或下划线字符。\b
:单词边界。匹配单词的开始或结束位置。
这些非打印字符可以在正则表达式中嵌入,以便匹配特定的文本模式或进行替换操作。通过利用这些非打印字符,可以更精确地定义匹配规则,以满足特定的需求。
特殊字符
特殊字符是一些具有特殊含义的字符,如前面提到的*
在正则表达式中表示匹配任意字符串的意思。如果想要查找文件名中包含*
字符的文件,需要对*
进行转义,即在其前面添加\
进行表示,例如使用ls \*.txt
。正则表达式中还有其他一些特殊字符,具体如下:
$
: 匹配输入字符串的结尾位置。如果设置了正则表达式对象的Multiline属性,则$
也可以匹配换行符\n
或\r
。如果要匹配$
字符本身,请使用\$
。()
: 标记子表达式的开始和结束位置。子表达式可以捕获匹配结果以供后续使用。如果要匹配这些字符本身,请使用\(
和\)
。*
: 匹配前面的子表达式零次或多次。如果要匹配*
字符本身,请使用\*
。+
: 匹配前面的子表达式一次或多次。如果要匹配+
字符本身,请使用\+
。.
: 匹配除了换行符\n
之外的任何单个字符。如果要匹配.
字符本身,请使用\.
。[
: 标记字符集合的开始。如果要匹配[
字符本身,请使用\[
。?
: 匹配前面的子表达式零次或一次,也可以指定为非贪婪的限定符。如果要匹配?
字符本身,请使用\?
。\
: 用于标记下一个字符为特殊字符、原义字符、向后引用或八进制转义符。例如,'n'
匹配字符n
,而'\n'
匹配换行符。要匹配\
字符本身,请使用\\
。为了匹配(
字符,请使用\(
。^
: 匹配输入字符串的开始位置,除非在字符集合方括号内使用,此时它表示不接受该字符集合中的字符。如果要匹配^
字符本身,请使用\^
。{
: 标记限定符表达式的开始。如果要匹配{
,请使用\{
。|
: 指定两项之间的一个选择。如果要匹配|
字符本身,请使用\|
。
构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
限定符
限定符用于指定正则表达式中的一个组件必须出现的次数,以满足匹配条件。常见的限定符包括 *
、+
、?
、{n}
、{n,}
和 {n,m}
,共计6种,正确理解和使用限定符,可以更精确地控制匹配的次数,以满足特定的匹配需求。
限定符案例分析
-
*
: 匹配前面的子表达式零次或多次。例如,zo*
可以匹配 “z” 以及 “zoo”。*
等价于{0,}
。 -
+
: 匹配前面的子表达式一次或多次。例如,zo+
可以匹配 “zo” 以及 “zoo”,但无法匹配 “z”。+
等价于{1,}
。 -
?
: 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 或 “does” 中的 “do”。?
等价于{0,1}
。 -
{n}
: n 是一个非负整数,表达式匹配确定的 n 次。例如,o{2}
无法匹配 “Bob” 中的 “o”,但可以匹配 “food” 中的两个 “o”。 -
{n,}
: n 是一个非负整数,至少匹配 n 次。例如,o{2,}
无法匹配 “Bob” 中的 “o”,但可以匹配 “foooood” 中的所有 “o”。o{1,}
等价于o+
,o{0,}
等价于o*
。 -
{n,m}
: m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 “o”。o{0,1}
等价于o?
。请注意,在逗号和两个数之间不能有空格。
贪婪匹配/非贪婪匹配方式
*
、+
和 ?
是贪婪的限定符,意味着它们会尽可能多地匹配文本。如果要将它们标记为非贪婪或最小匹配,只需在它们后面添加一个 ?
即可。
具体解释如下:
*
:匹配前面的组件零次或多次。它会尽可能多地匹配前面的组件。+
:匹配前面的组件一次或多次。它会尽可能多地匹配前面的组件。?
:匹配前面的组件零次或一次。它会尽可能多地匹配前面的组件。
例如,对于字符串 “abbbbbcc”,使用正则表达式 ab+
进行匹配,会匹配到最长的连续的b,即 “abbbbb”。
注意,通过在贪婪限定符后面添加
?
,即可将其转换为非贪婪或最小匹配模式。例如,使用正则表达式ab+?
进行匹配,会匹配到最短的连续的b,即 “ab”。
定位符
-
^
:匹配字符串的开始位置。在多行模式下,也可以匹配换行符\n
之后的位置。如果要匹配字符^
本身,请使用\^
转义。 -
$
:匹配字符串的结束位置。在多行模式下,也可以匹配换行符\n
之前的位置。如果要匹配字符$
本身,请使用\$
转义。 -
\b
:匹配一个单词的边界,即单词字符和非单词字符之间的位置。例如,\bword\b
可以匹配独立的单词"word",而不会匹配该单词的部分。如果要匹配字符\b
本身,请使用\\b
转义。 -
\B
:匹配一个非单词的边界,即不位于单词字符和单词字符之间的位置。例如,\Bword\B
可以匹配不包含任何单词字符的字符串"word",而不会匹配包含该单词的部分。如果要匹配字符\B
本身,请使用\\B
转义。
注意,定位符不能与限定符一起使用。例如,
^*
或+$
是无效的表达式,因为^
和$
是定位符,不可以匹配任意次数。它们用于定位字符串的开始和结束位置,而不是用来匹配具体的字符内容。
选择组合符
-
用圆括号将所有的选择项括起来,并使用
|
分隔相邻的选择项。然而,圆括号会导致相关的匹配结果被缓存。如果不希望相关的匹配结果被缓存,可以在第一个选择项前面使用?:
进行标记。 -
?:
是其中的一个非捕获元。还有两个非捕获元是?=
和?!
。这两个元字符具有更多的含义。-
?=
是正向预查,在任何开始匹配圆括号内的正则表达式模式的位置时,对搜索字符串进行匹配。这个匹配结果不会被包含在最终的匹配结果中。也就是说,它用于查找满足某个模式的字符串,而不会将其作为实际匹配的一部分。 -
?!
是负向预查,在任何开始不匹配圆括号内的正则表达式模式的位置时,对搜索字符串进行匹配。这个匹配结果同样也不会被包含在最终的匹配结果中。它用于查找不满足某个模式的字符串。
-
后向引用
-
当对一个正则表达式模式或部分模式的两边添加圆括号时,会导致相关的匹配结果被存储到一个临时缓冲区中。每个被捕获的子匹配都按照在正则表达式模式中从左至右遇到的顺序进行存储。存储子匹配的缓冲区编号从1开始,依次递增,最多可以达到99个子表达式。通过使用
\n
的形式,可以访问每个缓冲区中的内容,其中n是用来标识特定缓冲区的一位或两位十进制数。 -
为了忽略对相关匹配结果的保存,可以使用非捕获元字符
?:
、?=
或?!
。这些非捕获元字符可以在圆括号内的模式前面进行标记。它们的作用是告诉正则表达式引擎不要将其标记的部分作为实际匹配结果中的一部分进行存储。
总结心得
正则表达式成为基于文本的编辑器和搜索工具中不可或缺的一部分,这一点现在众所周知。它在这些工具中发挥着重要作用,成为了我们日常工作中不可或缺的工具之一。
要掌握正则表达式,并不是一件难事。关键是你需要有耐心和专注地阅读相关的资料,并在实际应用中进行适当的参考。可以通过反复练习和实践来加深理解。循序渐进地学习正则表达式的语法规则和常见的匹配模式,逐步完善你的技巧。此外,还可以寻找一些在线工具或者学习资源,它们可以帮助你更好地理解和运用正则表达式。与其他开发者或专家交流,参加相关的社区或论坛,也是拓宽知识的好途径。