利用「正则表达式」进行批量文本替换

利用「正则表达式」可以显著的提高文档处理的效率。本文围绕下列例子,简要的介绍了如何通过正则表达式进行文档的快速查找与替换。

查找字符串 ^[►](\D*)(\d+).*
替换字符串 -> b\2 [label="\1"];

请注意,「正则表达式」Regular Expression 简称 Regx,是一门博大而深邃的艺术,本文只能算作入门教程的导论[1] [2]

最简单的例子 - 删除多余空行

最直接的想法即是查找「换行符」直接删除,如下:

查找字符串 \n
替换字符串为空

然而,这样是行不通的:

第一行文本

第二行文本

第三行文本

经过处理后就成为了:

第一行文本第二行文本第三行文本

正常段落间换行也被删除了。不过,稍加变化即可解决问题:

查找字符串 ^\n

在 Regx 中,^ 标记行首;$ 标记行尾。

上述代码的意思即是查找出现在行首的「换行符」,从而解决了上述问题。

略微复杂的例子 - 删除行首行标

查找字符串 ^\d+

我们已经清楚通过 ^ 可以定位行首,现在新的问题是如何追踪到行标。凡事先问「是什么」,「行标」用自然语言来说,就是「出现在行首的一个或多个数字」。那么在 Regx 中可以用,\d+ 来表示。借此,引入「元字符」和「限定符」的概念,如下表:

元字符 匹配内容
. 匹配除换行符以外的任意字符
\ w 匹配字母或数字或下划线或汉字
\ b 匹配单词的开始或结束
\ s 匹配任意的空白符
\ d 匹配数字
限定符 重复次数
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次

真实的应用:一堆文本与一个问题

最近在处理一个超过十万字的 [3] 文本,需要把它处理为某种计算机可用的格式。里面含有大量的类似于这样的文本:

► 指向 1984 。

需要替换为这个

-> 1984 [label = “ 指向 “];

其中,文本和数字都是任意的。这是个有些复杂的问题,最终解决的办法就是开头的那串复杂的 Regx,如下:

查找字符串 ^[►](\D*)(\d+).*
替换字符串 -> b\2 [label="\1"];

除了上述章节已经提及的元素,首先映入眼帘的[4],是一堆括号,那么就先来解释括号的意义。

  • 方括号 [ ] 指没有预定义的「元字符」集,比方说[►]就是指当匹配到任何一个「 ► 」的时候。同理, [aeiou]指匹配到任何一个元音字母的时候。

  • 小括号 ( ) 指分组,每个组会被按照次序依次加上序号。替换字符串这里是最简单的应用,

\1 指代 (\D*) 的内容
\2 指代 (\d+) 的内容

显然,Regx 是大小写敏感的。再浏览一遍上面的 Regx,只有一个地方不清除了,\D 的意义。现在引入「反义字符」的概念

反义字符 匹配内容
\W 匹配任意不是字母,数字,下划线,汉字的字符
\B 匹配不是单词开头或结束的位置
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
^x 匹配除了x以外的任意字符

同理 [^aeiou] 匹配除了 a e i o u 这几个字母以外的任意字符。那么,用自然语言解释这一串 Regx 的意义即是:

查找到行首;匹配 ► ;接着匹配任意数量非数字字符,编为第一组;接着匹配一个或多个数字,编为第二组;匹配任意数量的在本行的任意字符;完

一只 BUG 与「贪婪模式」

查找字符串 ^[►](*)(\d+).*

之前处理此问题的时候,用的是上面这个 Regx ,但最后替换的结果令人哭笑不得

-> b4 [label=” 指向198”];

原因如下:

通常当 Regx 中包含能接受重复的限定符时,在使整个 Regx 能得到匹配的前提下,匹配尽可能多的字符。这被称为「贪婪模式」Greedy Mode 。与此对应的称之为「懒惰模式」Lazy Mode 。

这样的话,在「► 指向 1984 。」中,前面的 * 一直取到 8 也就不足为奇了。

###

思考题:Unix Command

rm image*[02468].jpg evenImage

是什么意思?[5]

综上所述

Regx 也应该可以算作「计算机科学」中的上古神器之一 — 简洁,优雅,烧脑。


  1. 1.本文默认读者知悉基本的计算机文本编码规则。
  2. 2.General Reference 正则表达式30分钟入门教程
  3. 3.OCR - Optical Character Recognition「光学字符识别」,通俗的说,就是扫描件。此外,ABBYY 真乃 OCR 神器。
  4. 4.这是「人民教育出版社九年制义务教育语文教科书」课文开头惯用语。
  5. 5.答案:将所有名称为偶数的 jpg 图像移动到同目录下的 evenImage 目录