[倚天屠龙记] vim 查找与替换(高级)

[倚天屠龙记] vim 查找与替换(高级) {{{ 行文到此,忍不住想吹捧一番vim的查找与替换,但仔细检阅了上下文,没发现任何值得吹捧之处,技术交流领域最忌讳"法轮大法好"之类的宣传,所以还是忍一忍,多一些客观描述,少一些主观吹嘘,这大概也是编辑器圣战要达成停火协议所需要的一点共识。 如前所述,正则表达式将会使vim的查询替换能力达到一个新高度,但上一节仅仅介绍了正则表达式的概念,尚未将其正式应用到查询与替换中来,这正是本节的任务。 正则表达式首要的任务就是判断匹配与否,这根据其规则是不难办到的,毕竟那规则定的跟数学公式一般,而数学的确定性是有目共睹的。但是要将它应用到查找与替换中来,还是会产生一些问题的,本文的宗旨就是描述这些问题的解决之道。 先就搜索而言,假使你想搜索一处数字,用正则表达式 \d\&; 进行搜索,而文件中有一处数字是 123,这自然是能匹配的,但问题是这个正则表达式不但能匹配 123,还能匹配 1、2、3、12、23,如果你启用了高亮搜索功能,那现在vim应该高亮哪一部分? 这就是“吞多少”的问题,正则表达式能同时匹配一个目标字符串的整体和部分,但搜索的结果必须是确定的,所以就需要约定一种规则来达成这种确定性。 vim规定,星号*,加号\&;,等号\=或者问号\?,以及大括号\{m,n}都尽可能多的吞掉字符,也就是所谓的“贪婪”法则,在这规定下,尽管正则表达式能同时匹配目标字符串的整体和部分,但搜索结果将是整体,高亮显示的也是整个目标字符串。如果希望尽可能少的匹配,对于星号而言,它的底限是零次,那只要修正正则表达式,去掉星号修饰的那一部分就行了,加号也一样,只是需要去掉加号,而等号和问号也是直接去掉就行,对于大括号\{m,n},希望尽可能少的匹配可以使用负数的形式\{-m,n},这样它就匹配元素的m到n次出现,但它的搜索结果是最少的那一部分,例如用正则表达式\d\{-2,5}去搜索2到5个数字,而文件中出现了12345,则它搜索出来的结果是12,而不是12345,而下一次出现则是34,依此类推。 解决了“吞多少”的问题,接下来再看看替换所面临的挑战。 在替换过程中,正则表达式只是用来搜索需要执行替换操作的位置,确定出需要替换的文本块的两个边界后,要使用新的内容进行替换,如果这个新的内容是一段与搜索内容无关的固定文本,这没啥好说的,但是有时这个新的内容可能要依赖于要被替换掉的旧内容,这听起来像数学语言一样,还是举个例子形象点,假如我的文件中的所有日期都是形如 2015-06-12 的格式,我现在要将它们都替换成 06/12/2015 的格式,啊哈,这个新的内容的年月日部分是来自于要被替换掉的旧内容,,这就引出了正则表达式的第二个问题:“吐出来”。 为解决上述问题,可以在正则表达式中,将用来匹配希望正则表达式“吐出来”的那一部分子表达式用括号括起来,比如上面日期的正则表达式是 \d\{4}-\d\{2}-\d\{2},我希望它把年月日这三个数字给吐出来,就将用来匹配这三个数字的部分括起来:\ (\d\{4}\ )-\ (\d\{2}\ )-\ (\d\{2}\ ),然后在用来替换旧内容的新内容中用\1、\2、\3这样的格式来引用被正则表达式吐出来的各个子字符串,而这个顺序是由正则表达式中左括号出现的先后来决定的,这样\1将是年份,\2是月份,\3是天数,于是用于替换的新内容可以写成 \2/\3/\1,即将 2015-06-12 替换成 06/12/2015。但要说明一点,由于替换命令s中是用左斜杠区分命令的各个部分,所以这个用于替换的新内容中如果本身也包含左斜杠,需要去掉它的转义,即在其前加上反斜杠,所以完整的命令就是 :%s/\ (\d\{4}\ )-\ (\d\{2}\ )-\ (\d\{2}\ )/\2\/\3\/\1/g,当然你也可以用井号来作分隔符而不用进行这个转义操作,好了,现在你开始吐槽它的可读性吧。 补充说明一点,上面使用了\1、\2等来引用正则表达式引用的各个部分,如果想引用匹配的整体,除了在正则表达式整体加上一个括号外,也可以直接使用\0来引用它,在上文中,\0就是整个日期字符串。利用\0,你可以使用命令 :%s/正则表达式/\0\0\0/g 在全文中将某个模式字符串由一个复制成若干个,而这却是由替换命令来完成的! 在替换命令中还有一个用于匹配指定位置的特殊字符,\zs和\ze,它俩不匹配任何字符,仅仅是一个位置标记,就好比^和$分别匹配行首和行尾一样,例如你想把 forever 中的 for 改成 FOR,但又不希望 forest 中的 for 有任何变化,你除了使用 :s/forever/FORever/g 之外也可以使用 :s/\zsfor\zeever/FOR/g 来完成这一点,这在有时非常有用,因为它搜索的条件更精确,而替换的只是某一部分。 在正则表达式一节中曾经说过,vim有能力将七十万字的《红楼梦》中的第一回第二回这样的中文回目序号,用一个替换命令修改成第1回第2回这样的数字序号,其实vim并不能读懂“一二三”这些中文的意思,它只是在替换时使用了一个序列,在每一处替换完成后将序列向后推进一次,达到数字递增的效果,本节偷懒不再详细描述具体方法了。 需要强调的是,本文并不是叙述正则表达式用途的,仅仅是为了描述在搜索和替换中如何使用正则表达式,这就好比只教你如何使用计算机而不是教你计算机能干什么一样,正则表达式的用途相关广泛,下面举一些例子: :%s/^\n\&;/\r/ 将文件中的连续若干个空行压缩成一行 :%s/^\n\{3}// 删除连续三个空行 :%s/\r//g 将文件的换行由dos风格换为unix风格 :%s#<[^>]\&;>##g 删除文件中的所有html标记,仅仅留下文本内容 :%s/\ (.*\ ):\ (.*\ )/\2:\1 将用冒号分隔的两段文字颠倒顺序 :s/freg/\=@a/g 将当前行中freg的每一处出现替换为寄存器a中的内容 要查看更多类似这样的复杂正则表达式,网上流传一份由大师级人物写的一份 vimtips,很容易搜索到,有兴趣的可以自行百度。}}}

与其在那里苦苦挣扎,碍于面子硬撑,倒不如微笑着面对,

[倚天屠龙记] vim 查找与替换(高级)

相关文章:

你感兴趣的文章:

标签云: