正则表达式——边界

边界

待匹配的文本:

js 复制代码
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.
       It is an ancyent Marinere,
          And he stoppeth one of three:
       "By thy long grey beard and thy glittering eye
          "Now wherefore stoppest me?

1、行的起始与结束

就像之前看到的那样,要匹配行或字符串的起始要使用脱字符(U+005E)​:

bash 复制代码
^

根据上下文,^会匹配行或者字符串的起始位置,有时还会匹配整个文档的起始位置。而上下文则依赖于应用程序和在应用程序中所使用的选项。

如你所知,若要匹配行或字符串的结尾位置要使用美元符:

js 复制代码
$

请确定RegExr中的multiline(多行)选项被勾选。global(全局)选项在打开RegExr时默认勾选,但是本例中你可以勾选也可以不勾选该选项。如果不勾选multiline选项,整个目标文本被视做一个字符串。

在上方的文本框中键入下面这个正则表达式:

js 复制代码
^How.*Country\.$

它会匹配以单词How开头的整行。请注意结尾的点号之前有一个反斜杠,它对点号进行转义,这样点号就被解释为字面值。如果不对点号转义,它就会匹配任意字符。如果想匹配作为字面值的点号,则必须将点号转义或者将其放入字符组中。

如果不勾选multiline选项会是什么情况?高亮显示功能会被关闭。在不勾选multiline但选dotall的情况下,键入:

js 复制代码
^THE.*\?$

可以看到它匹配了整个文本。

dotall选项表示点号除了匹配其他字符之外,还会匹配换行符。取消勾选dotall选项,则该表达式什么也不匹配。而以下表达式:

js 复制代码
^THE.*

则匹配第一行。再点击dotall选项,全部文本都会被匹配。不需要使用? $来匹配文本的结尾。

2、单词边界与非单词边界

我们已经多次遇到使用\b的情况了。它匹配单词边界。请尝试:

js 复制代码
\bTHE\b

(在勾选global选项的情况下)这个表达式会匹配第一行的两个THE。就像^和$一样,\b是个零宽度断言,表面上它会匹配空格或者是行起始,而实际上它匹配的是个零宽度的不存在的东西。你有没有注意到第二个THE两边的空格是没有标亮的?这是因为它们不是匹配的部分。这个理解起来不是很容易,但你可以通过观察它匹配和不匹配的内容来理解。

你还可以匹配非单词边界。非单词边界匹配除单词边界之外的位置,比如单词或者字符串中的字母或数字。要匹配一个非单词边界,试一下:

js 复制代码
\Be\B

看看它匹配了什么​。可以看到它匹配了小写字母e,而匹配的字母e的两边都是其他字母或者是非单词字符。零宽度断言不会匹配两边的字符,但它会识别文字e的两边是否是非单词边界。

在有些应用程序中,指定单词边界的另一种方法是使用:

js 复制代码
\<

来指定单词的开头,而使用

js 复制代码
\>

来指定单词结尾。这是比较旧的语法,在很多最新的正则表达式应用程序中无法使用。但在有些情况下,这种语法就很有用,因为它不会像\b那样匹配任意单词边界,而是允许你分别匹配单词的开头或结尾。

如果你的系统上有vi或者vim,可以用这类编辑器试一下。即使你没用过vim,这也很简单,只要按照以下步骤做即可。在命令行或者shell窗口中,将路径改为诗文所在的地方,再用这个命令打开:

js 复制代码
vim rime.txt

然后输入以下查找命令:

js 复制代码
/\>

再按回车键(Enter)或返回键(Return)​。在vim中使用斜杠符(/)开始一次查找。请观察光标,可以看到本次查找过程会找到单词的结尾。按下按键n可重复查找。再输入:

js 复制代码
/\<

再按回车键(Enter)或返回键(Return)​。这次查找会找出单词的开头。要退出vim,键入ZZ即可。

这一语法也可用于grep。自从20世纪70年代,grep像sed一样一直是Unix系统的主要工具。​(20世纪80年代,我一个同事的车牌上面就写着GREP。​)在shell提示符中试试这个命令:

bash 复制代码
grep -Eoc '\<(THE|The|the)\>' rime2.txt
  • -E选项表示要使用扩展的正则表达式(ERE),而不是grep默认使用的基本正则表达式(BRE)。
  • -o选项代表结果只显示一行中与模式相匹配的那部分。
  • -c选项的意思是只返回结果的数量。单引号中的模式会对THE、The或者the进行整词匹配。这就是<和 >帮你寻找的。

这个命令返回的是:

js 复制代码
259

也就是找到的单词的数目。

另一方面,如果不使用<和>,结果则不同。这样做:

bash 复制代码
grep -Eoc '(THE|The|the)' rime2.txt

得到的数字就是:

js 复制代码
327

为什么?因为该模式只匹配整词和任意包含该词的字符序列。这就是<和>能派上用场的一个原因。

3、其他锚位符

与锚位符^相似,以下简写式匹配主题词的起始:

js 复制代码
\A

这个写法不是在所有的正则表达式程序中都可以使用的,但可以在Perl和PCRE中使用。要匹配主题词的结尾,可以使用:

js 复制代码
\Z

在某些上下文中还可用:

js 复制代码
\z

pcregrep是带有PCRE库的grep版本。​​安装之后,要使用以上语法,则这样写:

bash 复制代码
pcregrep -c '\A\s*(THE|The|the)' rime2.txt

单词the出现在行首附近位置且之前有(一个或多个)空格的次数为108次,命令-c会返回这个次数。接下来输入命令:

bash 复制代码
pcregrep -n '(MARINERE|Marinere)(.)? \Z' rime.txt

这一命令会匹配一行(主题词)尾部的MARINERE或Marinere,之后是任何可选字符,在本例中可选字符就是标点符号或者字母S。​(点号两边的括号不是必需的。​)

可以看到输出为:

js 复制代码
1:THE RIME OF THE ANCYENT MARINERE,
10:      It is an ancyent Marinere,
38:        The bright-eyed Marinere.
63:        The bright-eyed Marinere.
105:      "God save thee, ancyent Marinere!
282:      "I fear thee, ancyent Marinere!
702:      He loves to talk with Marineres

pcregrep的-n选项在输出的每行起始处显示行号。pcregrep与grep的命令行选项十分相似。要了解所有选项,输入:

bash 复制代码
pcre --help

4、使用元字符的字面值

可以用

js 复制代码
\Q

js 复制代码
\E

之间的字符集匹配字符串字面值。

为了展示这一点,在RegExr下方文本框中输入以下元字符:

js 复制代码
.^$*+? |(){}[]\-

这15个元字符在正则表达式中有特殊含义,用来编写匹配模式。​(连字符在字符组的方括号中用来表示范围。但在其他情况下,则无特殊含义。​)

如果你在RegExr上方的文本框中尝试匹配这些字符,则不会看到任何结果。为什么呢?因为RegExr会认为这是个正则表达式而不是字符串字面值。现在试一下

js 复制代码
\Q$\E

它将匹配$,因为\Q和\E之间的任意字符都会被解释为普通字符。​(记住,可以在元字符之前加一个\使其匹配字面值。​)

5、添加标签

在RegExr中,取消勾选global但勾选multiline,点击Replace标签,然后在第一个文本框中输入:

js 复制代码
^(.*)$

这会匹配第一行文本并将其捕捉。然后在下一个文本框(标号为2)中输入以下内容:

html 复制代码
<! DOCTYPE html>\n<html lang="en">\n<head><title>Rime</title></head>\n
    <body>\n<h1>$1</h1>

输入替换文本的时候,你会注意(主题词文本在显示结果的文本框(标号为4)中变了,包含了你刚刚添加的标记。

RegExr很好地展示了添加标记的一种方式,但它也有自己的局限性,比如说它不能将结果保存为文件。因此,我们不能把目光仅局限于浏览器。

5.1、使用sed添加标签

在RegExr中能完成的工作,完全可以使用sed在命令行环境下做到。sed中的插入命令(i)允许你在文件或字符串中的某个位置之前插入文本。而与i命令相反的是命令a,它在某个位置之后添加文本(后面会用到a)​。

以下命令从第1行开始插入HTML5的doctype(文档类型)和其他标记:

js 复制代码
sed '1 i\
<! DOCTYPE html>\
<html lang=\"en\">\
<head>\
<title>Rime</title>\
</head>\
<body>
s/^/<h1>/
s/$/<\/h1>/
q' rime.txt

行尾的反斜杠(\)允许你在该流中插入新行而不会提前执行命令。引号前的反斜杠将引号转义为字面值。

正确运行这个sed命令会得到如下输出:

js 复制代码
<! DOCTYPE html>
<html lang="en">
<head>
<title>The Rime of the Ancyent Mariner (1798)</title>
</head>
<body>
<h1>THE RIME OF THE ANCYENT MARINERE, IN SEVEN PARTS.</h1>

以上sed命令保存在实例代码库中的top.sed文件中。用以下命令运行这些代码:

bash 复制代码
sed -f top.sed rime2.txt

可以得到与前面的命令相同的输出。要将输出保存到文件,则可以把输出重定位到一个文件中,比如:

bash 复制代码
sed -f top.sed rime2.txt > temp

除了会在屏幕上显示结果,重定位的那部分(>temp)还会将输出保存到文件temp中。

5.2、使用Perl添加标签

下面尝试用Perl完成同样的工作。先不解释,试一试这个:

bash 复制代码
perl -ne 'print "<! DOCTYPE html>\
<html lang=\"en\">\
<head><title>Rime</title></head>\
<body>\
" if $. == 1;
s/^/<h1>/; s/$/<\/h1>/m; print; exit; ' rime2.txt

以下是该命令的工作过程。

  • 变量 $.表示当前行,使用if语句测试。如果if语句返回true(真),则当前行就是第1行。

  • 当Perl用if语句找到第1行时,它会打印文档类型(doctype)和几个HTML标签。和在sed中一样,这里也需要将引号转义。

  • 第一个替换操作在行起始位置插入

    标签,第二个替换操作在行尾处插入

    标签。第二个替换操作结尾处的m表示使用多行修饰符。这就保证了该命令会识别第一行的结尾。如果没有m,则$会匹配该文件的结尾。

  • 命令print打印替换结果。

  • 命令exit则会立即结束Perl程序。否则,因为有-n选项,它会循环执行文件的每一行;但这里不需要。

以上命令要键入很多内容。我将这些Perl代码放入名为top.pl的文件中,也可以在代码库中找到。

bash 复制代码
#! /usr/bin/perl -n
if ($ == 1) {
print "<! DOCTYPE html>\
<html lang=\"en\">\
<head>\
<title>The Rime of the Ancyent Mariner (1798)</title>\
</head>\
<body>\
";
s/^/<h1>/;
s/$/<\/h1>/m;
print;
exit;
}

使用合集运行这个文件:

js 复制代码
perl top.pl rime2.txt

得到的结果与前面是一样的,只是形式上稍有不同。​(就像在sed中一样,你可以用>将输出重定位到文件中。​)

相关推荐
谢亮_vipxieliang1 天前
手机号验证技术方案:号段规则与正则表达式
java·服务器·spring boot·正则表达式·hibernate
zx_741484811 天前
【Python 入门】Python 正则表达式零基础讲解:基础语法 + 元字符 + 常用案例
python·正则表达式
deli0070071 天前
正则表达式可视化测试工具:输入即匹配,高亮一目了然
git·测试工具·正则表达式
ydd1001003 天前
正则表达式匹配
正则表达式·代理模式
落魄大学生之流水线上谋生计3 天前
正则表达式完全指南:从入门到精通
正则表达式
tianyu2346 天前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
OPEN-F7 天前
Python进阶教程:正则表达式进阶与文本处理
开发语言·python·正则表达式
小小龙学IT10 天前
C++ 正则表达式完全指南:从 std::regex 实战到 RE2 引擎原理(NFA/DFA/回溯陷阱)
c++·正则表达式
傻啦嘿哟10 天前
猫眼电影TOP100爬虫:Requests+正则表达式实战
爬虫·正则表达式
circuitsosk10 天前
AI输出的“质检员”:构建智能体质量评估、异常检测与人工兜底的三层防线
人工智能·python·microsoft·正则表达式·langchain