平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“正则表达式模式匹配的简单方式”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
被匹配的文本如下所示:
THE RIME OF THE ANCYENT MARINERE, IN SEVEN PARTS.
ARGUMENT.
How a Ship having passed the Line was driven by Storms to the cold Country
towards the South Pole; and how from thence she made her course to the tropical
Latitude of the Great Pacific Ocean; and of the strange things that befell;
and in what manner the Ancyent Marinere came back to his own Country.
I.
1 It is an ancyent Marinere,
2 And he stoppeth one of three:
3 "By thy long grey beard and thy glittering eye
4 "Now wherefore stoppest me?
1、匹配字符串字面值
正则表达式最为直接和明显的功能就是用一个或多个字符字面值来匹配字符串。
从实现思路看,匹配字符串字面值的方法就是采用普通的字符。这听起来是否有些熟悉呢?这就和你在Word等字处理程序中采用查找或者在搜索引擎中输入关键字类似。当你以逐个字符对应的方式查找文本字符串的时候,就是在用字符串字面值查找。
在这个场景下,举个例子,如果你要匹配以上诗下文开头部分的单词Ship,只需在RegExr上方的文本框中键入Ship,该单词就会在下方的文本框中标亮(首字母要大写)。

2、匹配数字
在RegExr中左上方的文本框里,输入以下字符组简写式来匹配数字:
d

实际处理时,因为默认勾选了global(全局匹配)复选框,这将会匹配下方文本区域中所有的阿拉伯数字。
理解这一步时,现在用字符组替代d来匹配相同的内容。在RegExr的上方文本框中输入以下范围的数字:
[0-9]

虽然语法不一样,但d和[0-9]的效果是一样的。
字符组[0-9]表示范围,这意味着它会匹配0至9范围内的数字。你也能够列出0至9范围内的所有数字来进行匹配:
[0123456789]
若只想匹配0和1两个数字,能够采用这个字符组:
[01]
结合项目来看,请在RegExr中尝试一下[12]同时看看结果。采用字符组可精确匹配字符。数字的字符组简写式d更为简短,但却没有字符组强大、灵活。在无法采用d时(不是所有情况下都兼容这种方式),或者想匹配特定数字时,就需采用字符组;合适的时候能够采用d,因为它更简短。
3、匹配非数字字符
理解这一步时,通常能够将简写式取反,取反的结果就是排除。比如,要匹配非数字字符,可采用包含以下大写字母D的简写式:
D
理解这一步时,请在RegExr中试一试。大写字母D取代小写字母d,就会匹配非数字字符(如图所示)。
该简写式与以下字符组取反的作用相同(字符组取反的意思其实就是“不匹配这些”或“匹配除这些以外的内容”):
[^0-9]
下面这个表达式作用也一样:
[^d]
4、匹配单词和非单词字符
在RegExr中,将D替换为:
w
落到代码里,这个简写式将匹配所有的单词字符(前提是勾选global选项)。D与w的区别是D会匹配空格、标点符号(引号、连字符、反斜杠、方括号)等字符,而w却不会,它只匹配字母、数字和下划线。
在英语环境里,与w匹配相同内容的字符组为:
[_a-zA-Z0-9]

现在用大写字母W匹配非单词字符:
W

实际处理时,在本示例里,这个简写式匹配空格、标点以及其他非字母、非数字字符。采用以下字符组也能够匹配相同的内容:
[^_a-zA-Z0-9]
结合项目来看,字符组允许你匹配更多类型的字符,但有时你不想而且也没有必要键入所有字符。这也就是“按键次数最少则胜”的原则。但有时你确实需将所有的字符键入才能得到准确的结果。反正你自己决定。
轻松一下,在RegExr中试一下:
[^w]

以及
[^W]

理解这一步时,下表提供了更多的字符简写式。不过同时不是所有的正则表达式处理器都能识别这些简写式。

5、匹配空白符
能够用以下简写式匹配空白符:
s

理解这一步时,请在RegExr试一试同时看看高亮的部分。以下字符组与s匹配的内容相同:
[ tnr]

也就是说,它会匹配:
- 空格
- 制表符(t)
- 换行符(n)
- 回车符(r)
可想而知,s也有对应的大写形式。要匹配非空白字符,则采用:
S

这个简写式匹配除空白符之外的所有符号。它匹配字符组:
[^ tnr]
或者是:
[^s]
实际处理时,除了s匹配的字符之外,还有其他不太常用的空白字符。下表列出了匹配常用和不太常用的空白字符的简写式。

6、再谈匹配任意字符
理解这一步时,用正则表达式匹配任意字符的一种方法就是采用点号(U+002E)。点号能够匹配除行结束符之外的所有字符,个别情况除外。
实际处理时,在RegExr里,去掉对global复选框的勾选。这样,任何正则表达式都会匹配目标文本中第一个匹配项。
在RegExr上方的文本框中键入单个点号来匹配任意字符。
如图所示,点号匹配了目标文本中的第一个字符T。

要匹配THE RIME整个短语,则可采用八个点号:
........

但这种方法太麻烦,所以建议用量词:
.{8}
实际处理时,这个表达式就能匹配前两个单词以及它们之间的空格,但只是粗略地匹配。勾选global旁的复选框,看看这个表达式还有什么作用,你就知道我所说的粗略是什么意思了。它匹配了连续多组的八个字符,头尾相连,只有目标文本的最后几个字符除外。
理解这一步时,下面我们再试试匹配单词的边界和字母的开始和结束位置。在RegExr上方文本框中键入以下内容,能够看到细微的差异:
bA.{5}Tb

理解这一步时,这个表达式有更强的特指性(请记住特指性,specificity,这个概念很重要),它匹配单词ANCYENT,也就是ancient的旧式拼写形式。这是如何做到的呢?
- 简写式 b匹配单词边界,不消耗任何字符;
- 字符A和T限定了字符序列的首尾字母;
- .{5} 匹配任意五个字符;
- 简写式 b匹配单词的另一个边界。
这个正则表达式实际上能够匹配ANCYENT和ANCIENT。
现在再试一下这个简写式:
bw{7}b

最后再试试匹配零个或多个字符:
.*
它就相当于:
[^n]
或
[^nr]

类似地,点号也能够与表示“一个或多个”的量词(+)连用:
.+
实际处理时,请在RegExr中尝试这些表达式,它们都会匹配第一行内容(在取消勾选global复选框的情况下)。原因是点号通常不会匹配折行符,比如换行符(U+000A)或回车符(U+000D)。勾选dotall旁边的复选框,随后.*和.+就会匹配下方文本框中的全部文本。 (dotall表示点号匹配包括换行符在内的所有字符。)
理解这一步时,这就涉及量词的贪心特性了。所谓“贪心” (greedy),指量词会匹配所有能匹配的字符。别急,第7章会详细介
7、给文本加标签
在这个场景下,怎么让“The Rime of the Ancient Mariner”的内容以网页而不是纯文本形式显示呢?换句话说,怎么采用正则表达式而不是手写方式为它们添加HTML5标签呢?
在之后的几章里,我会陆续展示相应的方法,本章先从轻松情况的开始。
从实现思路看,点击RegExr中的Replace(替换)标签,选中multiline(多行)选项,随后在第一个文本框中键入:
(^T.*$)

落到代码里,这个表达式会从文件的开始匹配诗文的第一行,随后采用括号将文本捕获到一个分组中。在第二个文本框中键入:
<h1>$1</h1>

从实现思路看,这个替换表达式将$1捕获的内容嵌套在了h1标签中。能够在底部的文本框中看到结果。$1是一个Perl风格的后向引用。此外,在包括Perl在内的多数实现程序中,还能够采用1表示后向引用,但是RegExr只兼容$1、$2、$3这样的方式。
7.1、用sed为文本加标签
理解这一步时,在命令行中采用sed也能够为文本添加标签。sed是Unix流编辑器,它兼容用正则表达式转换文本。sed最初在20世纪70年代早期由Lee McMahon于贝尔实验室开发。如果用户采用的是Mac或者Linux,那就已经有sed了。
请在shell提示符(比如Mac中的Terminal终端窗口)中测试以下内容:
echo Hello | sed s/Hello/Goodbye/

运行的过程应该如下所示:
- 在这个场景下,echo命令将在标准输出设备(通常是屏幕)中打印单词Hello,竖线符(|)将打印内容借助管道传到之后的sed命令;
- 管道将echo的输出转为sed的输入;
- 结合项目来看,sed的s命令将单词Hello变为Goodbye,而Goodbye就显示在屏幕上了。
现在试着在命令或shell提示符中键入:
sed -n 's/^/<h1>/; s/$/</h1>/p; q' rime.txt

以下是正则表达式处理器的工作过程解析。
- 首先调用sed程序。
- 落到代码里,sed默认的操作是直接复制每行输入同时输出,-n选项覆盖了该默认操作。之所要覆盖默认操作,是因为我们只想让正则表达式影响第1行。
s/ ^/<h1>/在行的开头(^)添加<h1>标签。- 分号(;)用来分隔命令。
s/$/</h1>/在行的结尾($)添加</h1>标签。- 命令p会打印受影响的那一行(第1行)。与-n不同,后者会打印所有行。
- 最后命令q会结束程序,这样sed程序就只会处理第1行。
- 所有的操作都是针对rime.txt文件执行的。
实际处理时,这行命令还有另一种写法,即用-e选项分别引导每个命令。我当然更喜欢采用带分号的写法,因为那种写法更简短。
sed -ne 's/^/<h1>/' -e 's/$/</h1>/p' -e 'q' rime.txt

结合项目来看,能够将这些命令写到文件里,比如这里所示的文件h1.sed(该文件就在之前所说的代码库里):
#! /usr/bin/sed
s/^/<h1>/
s/$/</h1>/
q
在这个场景下,若要运行该文件,请在与rime.txt同一个路径或文件夹里运行如下所示命令:
sed -f h1.sed rime.txt

7.2、用Perl为文本加标签
结合项目来看,最后,我将展示如何用Perl来做类似的事。Perl是由LarryWall于1987年创立的一种通用程序设计语言。它以对正则表达式的强大兼容和文本处理能力而闻名。
在命令提示符中键入
perl -v
从实现思路看,然后回车,看看你的系统中是否已经安装了Perl。该命令会得到系统中Perl的版本信息或者得到错误。
在命令提示符中键入:
perl -ne 'if ($. == 1) { s/^/<h1>/; s/$/</h1>/m; print; }' rime.txt
就能够得到和sed示例中一样的输出结果。
以下是这个Perl命令的执行过程解析。
- perl调用了Perl程序。
- -n选项输出全部输入内容(rime.txt文件)。
- -e选项允许在命令行(而不用在文件)中提交程序代码。
- 在这个场景下,if语句检查是否在第一行。在Perl中$.是个特殊的变量,它匹配当前行。
- 结合项目来看,第一个替换命令s先找到第一行的开头(^)随后插入<h1>标签。
- 第二个替换命令s搜寻行结尾($)再插入</h1>标签。
- 结合项目来看,替换命令最后的m(多行)修饰符,表示将本行单独处理;这样,$就只匹配第一行的结尾而不会匹配整个文本的结尾。
- 最后在标准输出设备(屏幕)中打印出结果。
- 所有这些操作都是针对rime.txt文件的。
同样能够将这些命令放入程序文件中,比如示例库中的h1.pl文件。
#! /usr/bin/perl -n
if ($. == 1) {
s/^/<h1>/;
s/$/</h1>/m;
print;
}
然后在rime.txt的同一个目录下,执行如下所示命令:
perl h1.pl rime.txt

到此这篇关于正则表达式轻松的模式匹配的文章就介绍到这了,更多相关正则表达式模式匹配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!
- 正则表达式之字符串模式匹配实例详解
- 正则表达式常用的4种匹配模式小结
- 正则表达式之文本模式的匹配和查找
- 正则表达式模式匹配字符串基础知识
- 正则表达式中最短匹配模式的用法浅析
- 正则表达式惰性匹配模式(?)
- 正则表达式全局匹配模式(g修饰符)
- 正则表达式模式匹配的String方法
- MySQL 字符串模式匹配 扩展正则表达式模式匹配
- php正则表达式中的非贪婪模式匹配