Vim案例两则
在博问中看见两个比较典型的Vim处理文本的案例,总结一下,希望对大家有所帮助。如果大家有好的方法,欢迎讨论。尤其是第二个案例,如果能够一个命令处理更好。
案例一
这是在博问中看到的一个问题,原始文本如下。
要求处理成下面的样子,也就是把逗号从行尾移动到下一行第一个单词前面。
我的第一想法是先将2-6行的逗号删除,然后在3-7行加上逗号即可。
删除2-6行的逗号
:%s/,//
解释一下:
- : 冒号在Vim里面是命令引导符,想要输入命令必须先输入冒号。
- % 表示匹配所有行
- s/old/new/ 表示用new来替换old。s表示substitude(替换)。在s/,//中,old是逗号,new是空(注意后面两个斜线之间啥也没有,表示空)。
在3-7行前面加上逗号
:3,7s/\</,/
解释一下:
- 3, 7 表示对第3行至第七行进行处理,在Vim中m, n表示对第m到第n行进行处理,比如:1,10d表示删除1-10行
- s/\</,/ 表示用逗号替换\<,<表示单词边界,属于特殊字符,需要转义,所以是\<
这样就搞定了,但是转头一想,有没有更简单的办法呢?上面的办法虽然也很简单,但是需要两次命令才能完成,有没有办法用一个命令搞定呢。搞了一会,终于搞定,命令如下。
:%s/,\n\(\s*\)/\r\1,/
解释一下:
- %s/old/new/ 表示对所有行中的old用new来替换,这里每一行只有一个逗号,所以不用加g,如果有多个逗号,那么要变成%s/old/new/g的形式,g表示对每行中的所有满足条件的文本进行替换。也就是说%是纵向的,而g是横向的。
- ,\n\(\s*\) 匹配逗号,换行以及行首的若干空格。如下图
- \r\1,匹配换行,行首的若干空格及逗号,这里\1是回溯引用,表示前面第一个括号对应的匹配,也就是行首的若干空格,因为添加逗号时不能破坏这些空格,所以匹配的时候进行保留以便替换时引用之。
注意,上面的(和)属于特殊字符,需要转义,如果觉得\(\)这种方式难看,可以在表达式前面加上\v。如此则特殊字符无需再转义,可以像perl一样使用正则表达式了。
:%s/\v,\n(\s*)/\r\1,/g
案例二
也来来自于博问,要求将只包含5个字符(字母,数字,或者下划线)的行,删除其行尾的换行符。
原始文本如下:
处理之后如下:可见2,3,4行的换行符都被删除了。
我首先想到的命令是
:g/\v^\w{5}$/ s/\n//g
解释一下:
整个表达式可以分成两个主要部分。:g/\v^\w{5}$/设为A,s/\n//g设为B。那么这个命令的含义就是对于满足条件A的行,执行命令B。也就是对于所有只包含5个字符的行,删除其行尾的换行符。
- :g/patter/ g表示global,patter是一个正则表达式,这句话的意思是对于所有满足patter的行,用随后地命令(即s/\n//g)进行处理。
- \v^\w{5}$ 表示只包含5个字符的行,\v表示后面的特殊字符不用转义,比如{}。^表示行首,$表示行尾。\w表示单词字符,即字母,数字或者下划线。
- s/\n//g 表示用空替换\n,也就是删除换行符。
但是非常遗憾的是,执行完这个命令后,结果并不正确,如下:
可以看到,第二个abcde末尾的换行并没有删除掉,为什么会这样呢?仔细分析一下这个命令的执行步骤。
首先Vim解析命令然后找到第一个满足条件的行abcde,并删除其末尾的换行符,此时文本变成下面的样子。
这时,第二行文本是abcdeabcde,共10个字符,已经不再满足5个字符的条件了,所以Vim不会再删除其末尾的换行了,因而就产生了上面的错误结果。
看来用一个命令处理有点困难,那就分成两个吧。虽然麻烦一点,但是结果正确。
首先把要处理的行加上一个特殊标志,这个标志能够将待处理的行与其它行区别开来,所以要选择未在文本中出现的字符。另外一个要求是处理完之后这个标志也要删除,所以把它加在行尾,可以与换行符一并删除。
先在包含5个字符的行末尾加上#
:%s/\v(^\w{5}$)/\1#/g
删除所有的#和换行符。
:%s/#\n//g
由于这次不再以字符个数来甄别处理的行,而是以#号来定位,即使两行合并之后,#号仍然存在,结果自然就正确了。你有更简单的办法么?
参考
关于跨行匹配,下面这个网页上有详细的介绍。