正则表达式:批量移除修改版论文颜色标记
0 学习动机:LaTeX代码批量替换
在论文修改时,往往会在修改处使用颜色标记,比如:
模型预测控制是\color{blue}{世界上}最好的控制方法。
对应的LaTeX代码为:
模型预测控制是{\color{blue}世界上}最好的控制方法。
但修改完成后,手动移除这些颜色标记费时费力。
右大括号的位置太难以捉摸了!
能否借助正则表达式,将这些蓝色标记批量移除呢?
即能否使用正则表达式将 \text{\{\\color\{blue\} * \}} 批量替换为 \text{*} 呢?
今天我来学习一下正则表达式,并尝试解决这个问题。
1 正则表达式简单学习
1.0 正则表达式学习资源
目前网络上有很多优秀的正则表达式教程:
学习过程中,可以使用正则表达式在线测试工具:
1.1 正则表达式简介
正则表达式(Regular Expression)是一个字符串,用于匹配符合某个规则的字符串。
正则表达式可以用来:
- 验证字符串是否符合某种规则(数据验证)
- 查找和替换符合规则的字符串(查找替换)
正则表达式分为普通字符和元字符,元字符可以构建复杂的匹配规则。
1.2 正则表达式元字符
\ 转义下一个字符
| 或 c(a|b)t匹配cat,cbt
^ 匹配字符串的开始位置 ^a不会匹配cat
$ 匹配字符串的结束位置 a$不会匹配cat
{m} 前面字符出现m次
{m,} 前面字符至少出现m次
{m,n} 前面字符出现m到n次
+ 前面字符出现1次或多次 cat+匹配cat,catt
? 前面字符出现0次或1次 cat?匹配ca,cat
* 前面字符出现任意次 cat*匹配ca,cat,catt
? 在以上元字符后面,指尽可能少匹配 a+?匹配aa中第一个a
() 定义运算优先级和范围 c(at)?匹配c,cat
[] 匹配括号内所有字符 [a]匹配cata中的两个a
[^] 匹配除了括号内所有字符 [^a]匹配cata中的c,t
[-] 匹配除了括号内所有字符 [A-Z]匹配所有大写字母
. 匹配除换行符的所有字符
\b单词边界\B非单词边界 \d数字\D非数字
\f换页\n换行\r回车 \t制表\v垂直制表
\s空白\S非空白 \s=[\f\n\r\t\v]
1.3 正则表达式预查
(?:pattern) 非获取匹配
(?=pattern) 正向肯定预查
(?!pattern) 正向否定预查
(?<=pattern) 反向肯定预查
(?<!pattern) 反向否定预查
2 批量移除论文蓝色修改标记
2.0 有bug的现有方案
知乎上已经有该问题的解答,但是有bug:
其构造的正则表达式为: \{\\color\{purple\}(.*?)\} ,并将相应文本替换成 $1 。
(注:$num代表第num个分组的匹配内容)
正如原文作者所说,使用该方法,不能处理有嵌套的括号的情况:
2.1 失败的正则表达式堆栈
参考这篇博客:
构造替换正则表达式:
{\s*\\color\s*{purple}([^{}]*(((?'Open'{)[^{}]*)+((?'-Open'})[^{}]*)+)*)}
但堆栈并不能正常工作,使用这种方法将会在有嵌套括号的情况下,直接匹配到文本最后一个右括号:
我还尝试了 正则表达式30分钟入门教程 中的“平衡组/递归匹配”,这时候Sublime直接报错了,合理怀疑Sublime对堆栈的支持并不好。
教程中也提到:“这里介绍的平衡组语法是由.Net Framework支持的;其它语言/库不一定支持这种功能,或者支持此功能但需要使用不同的语法”
2.2 不含有嵌套括号的匹配
于是,我放弃了使用堆栈,这就意味着不能处理无限嵌套的括号。
先搞一个没有嵌套括号的正则表达式: {\s*\\color\s*{purple}\s*([^{}]*)}
成功了!正则表达式仅匹配了不含有嵌套括号的文本:
2.3 含有单层嵌套括号的匹配
只需要在没有嵌套括号的基础上,增加任意次仅有单层括号的匹配即可。
构造正则表达式如下:正则表达式: {\s*\\color\s*{purple}\s*[^{}]*([^{}]*{[^{}]*}[^{}]*)*[^{}]*}
又成功了!正则表达式匹配了最多含有单层嵌套括号的文本:
2.4 含有多层嵌套括号的匹配
手动套娃几层,构造匹配最多含有3层嵌套括号的正则表达式:
{\s*\\color\s*{purple}\s*([^{}]*({[^{}]*({[^{}]*({[^{}]*}[^{}]*)*}[^{}]*)*}[^{}]*)*)}
完美,完全匹配了!
2.5 成功解决方案
一般文章中的括号嵌套层数是有限的,因此列举了一些,各位按需取用(记得修改颜色):
不含有内嵌括号: {\s*\\color\s*{purple}\s*([^{}]*)}
最大3层内嵌括号: {\s*\\color\s*{purple}\s*([^{}]*({[^{}]*({[^{}]*({[^{}]*}[^{}]*)*}[^{}]*)*}[^{}]*)*)}
最大5层内嵌括号: {\s*\\color\s*{purple}\s*([^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*)}
最大10层内嵌括号: {\s*\\color\s*{purple}\s*([^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*({[^{}]*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*}[^{}]*)*)}
再多的请自行使用如下matlab代码生成吧~
% gen_regex(color, inner nesting num)
gen_regex('blue',30)
function str=gen_regex(color,n)
str=['{\s*\\color\s*{', ...