对于vim中^$及删除空白的理解
目录
基础
<![if !supportLists]>1. <![endif]>说明
本文以下图为例,展开说明对删除空白的理解
<![if !vml]><![endif]>
<![if !supportLists]>1. <![endif]>基础理论一 行理论与串理论
<![if !supportLists]>1) <![endif]>我们看到的结果是显示的结果,其实际存储时是在每一行结尾处加一个控制字符\n,显示的时候一遇到\n就显示换行。
如下图可以理解为wowo\n\n\nwo\n
<![if !vml]><![endif]>
图 1
这里的$只是一种显示,表示一行的结尾,并不是实际的内容。在这里要把内容与显示分开理解。显示的时候一遇到\n就换行,并显示一个$
<![if !supportLists]>2) <![endif]>\n 是一种控制符,如果文件内容出现\n 并不会匹配到
下图是输入 /\n 后的结果<![if !vml]><![endif]>
<![if !supportLists]>3) <![endif]>看上去的空行 可以理解为 空\n 这里的空要理解是一种概念,而不是真正用眼睛看到的空行。
<![if !supportLists]>2. <![endif]>基础理论二
<![if !supportLists]>1) <![endif]>^ $正则理论
^$ 匹配“空” 但不匹配空行后面的回车\n,即我们看到的空行实际上是“空”\n,这里加引号以区别我们看到的空行。或者说^$匹配的是“空”这种概念。[用行理论]
^ 匹配所有行的开头,这里的开头可以理解为位置 用行理论
$匹配所有行的行尾,这里的结尾也可以理解为位置用行理论
^wo 代表一行的开头为wo,并匹配这个字符 用串理论
$wo wo$(估计以前写错了)代表一行的结尾为wo,并匹配这个字符用串理论
wo^ 无效 ,注意并不等价于wo
$wo 无效 ,注意并不等价于wo
\n ^等价于\n
$\n等价于\n
总结:^ $与字符串联用时用串理论。单个或合用时用行理论。
<![if !supportLists]>3. <![endif]>基础理论三 s处理串 g 处理行
<![if !supportLists]>1) <![endif]>举例:^$同样是匹配空行, g/^$/d 可以将空行删除, 但%s/^$//g却不能删除空行。因为g处理的是行,只要找到有空行的行就删除了。但s处理的是字符串,虽然匹配到了空行并把空行替换成空,但换行仍在,所以应该写成 %s/^$\n//g
<![if !supportLists]>2) <![endif]>再比如:\n$ 对于g/\n$/d 表示一行的结尾有\n,而所用行的结尾都是\n,所以会删除所有行。对于%s/\n$//g 表示删除连续2个\nk 中前面那个\n,所以其效果是删除空行。
<![if !supportLists]>3) <![endif]>正因为s 处理串 g处理行,所以g只能处理删除操作,而不能处理替换操作。广义来讲删除是另一种替换(替换成空)所以g能处理的s也能处理,但相对麻烦。所以对于行的处理可以考虑用g 对于行中的字符的处理再考虑用s
<![if !supportLists]>4. <![endif]>最后说明
^ $有时用行理论解释,有时用串理论解释。s命令用串理论解释,g命令用行命令解释。当混合时,只要有用行理论解释的,就用行理论解释。否则用串理论解释。
如:%s/^/1/g 在每一行前加一个1。由于^用行理论解释,所以用行理论解释。
g/^$/d 直接用行理论解释。
举例
<![if !supportLists]>1. <![endif]>%s/$/1/gc
行尾加一个1,这里把行尾理解成一种位置,而不要理解成一个字符。如下图1是一行中最后一个字符,但不要把1理解成行尾,注意图中的$只是一种显示,要与表达式中的$区别开来
<![if !vml]><![endif]>
图 2
<![if !supportLists]>2. <![endif]>%s/^/1/g 会在每行的行首加一个1
<![if !vml]><![endif]>
图 4
<![if !supportLists]>3. <![endif]>%s/\n/1/gc 与 %s/$\n/1/gc %s/\n^/1/g
把换行替换成1, 也即所有行都一行了。
<![if !vml]><![endif]>
图 3
<![if !supportLists]>4. <![endif]>%s/^\n/1/g
在\n的后面还有一个\n 即把\n\n中的第2个\n替换成1
<![if !vml]><![endif]>
图 6
<![if !supportLists]>5. <![endif]>%s/\n$/1/g
<![if !vml]><![endif]>
图 9
\n$ 表示在\n的前面还有一个\n时匹配前面那个字符,所以也就是说把两个连续\n中的前一个替换成1
例5 与例6 展示了%s/^\n/1/g 和%s/\n$/1/g的区别虽然 %s/^\n//g和 %s/\n$//g 在删除空行时表现一样。这样更进一步说明了两个\n理论的正确性。
<![if !supportLists]>6. <![endif]> %s/^$/1/g
仔细看下图,把空行替换成了1, 再次强调要把空行与空行\n区分开来,这里把空行替换成了1,而不是把空行\n替换成了1
<![if !vml]><![endif]>
图 7
实战演练
<![if !supportLists]>1. <![endif]>删除空行
删除空行,这里的空行是指纯粹的空行,不带空白字符
<![if !supportLists]>1.1. <![endif]>g/^$/d
g 处理的是行,找到有空行的行,然后删除即达到了删除空行的目的。
g/^$\n/d 也可以。
<![if !supportLists]>1.2. <![endif]>g/^\n/d
匹配以\n开头的行,并删除。即删除空行。
g/\n$/d 会删除所有行,因为所有行都是以\n结尾的。
也再一次证明了g用行理论, s用串理论。
<![if !supportLists]>1.3. <![endif]>%s/^$\n//g
<![if !vml]><![endif]>
图 11
解释:^$\n 匹配空行\n ,将空行\n 替换成空,即达到了删除空行的目的。
另一种解释:其中的表达式$\n 等价于\n 所以^$\n 就变成了^\n 即表示在\n的后面还有一个\n,把后面那个\n替换成空。
可以看出\n 等价于 ^\n 所以上述命令也可以写成 %s\^\n//g
<![if !supportLists]>1.4. <![endif]>%s/^\n//g
实质就是把连续两个\n中的后面的那个\n替换成空,由于替换成空后剩余的\n又连成了一片,如此下去,直到还剩一个\n
<![if !supportLists]>1.5. <![endif]>%s/\n$//g
<![if !vml]><![endif]>
图 12
只能删除空行,是那种不包括空白的空行
实质就是把连续两个\n中的前面的那个\n替换成空,由于替换成后剩余的\n又连成了一片,如此下去,直到还剩一个\n
v/./d 也可以删除空行 (是空行不是空白行)
v命令代表g命令的相反,即g命令如果选择则v命令不选择
.代表非空行。 把v/./d 会删除空行
<![if !supportLists]>2. <![endif]>删除空白行
<![if !supportLists]>2.1. <![endif]>%s/^\s*$\n//g
解释:匹配以空白开头,以空白结尾 加\n
由于 $\n 与\n 等价所以 上式也可写成 %s/^\s*\n//g
<![if !supportLists]>2.2. <![endif]>%s/^\(\s*\n\)\+//g
该例其实是上一例的变种,只是上一例要替换2次(对于本文档的例子),而本例只替换1次。该方法在把多个空白行变成一个空行是有用。
<![if !vml]><![endif]>
图 14删除后状态
解释: 同 %s/^\(\s*$\n\)\+//g效果一样
+ 指的是重复至少一次。+ 号前面的都要重复,即^(\s*\n) 由于()与+号有其特殊的含义,所以用\进行转义。
所以上述表达式的关键就是^(\s*)+ 的理解。即在\n的后面有一些空白,而这些空白的后面有一个\n ,并且这些模式出现了1次以上,即<首行空白>\n 连续1次以上的模式。
举个例子
第一行\n
<空白字符>\n
<空白字符>\n
第二非空白行
的内容就是 第一行\n<空白字符>\n<空白字符>\n第二非空白行
对于^(\s*\n)+ 匹配的是:<空白字符>\n<空白字符>\n 也就是说^(\s*\n)出现了二次。然后把这个匹配项替换成空。
\s* 表示空白字符出现任意多次(0次或0次以上)至于具体是空格还是tab,还是两者的混合没有关系,都当成抽象的\s即可。即如 <空格><tab> 与<tab><tab> <空格><空格> 都是\s*的匹配项。
注意对于%是必须的,%表示查找的范围是全文件,若不加则表示查找当前行,一行之内是不会出现\n的,更不会出现^(\s*)+匹配的情况。所以%是必须要加的。
<![if !supportLists]>2.3. <![endif]> g/^\s*$/d
解释一下:g前面不加范围,默认是全文件,所以在全文件中搜索 ^\s*$ 的模式,即删除 含有 空白开头并以空白结尾的 行。
<![if !supportLists]>3. <![endif]>多个空行合并成一个空行
<![if !supportLists]>3.1. <![endif]>%s/^\(\s*\n\)\+/\r/g
将多个空行、含有空白字符的空行合并成一个空行。在2.2节里已说过,这里替换一次,所以能将多个空行合并成一个空行。替换一次的原因是从串的角度分析得来。
<![if !supportLists]>3.2. <![endif]>%s/^\n$//g
解释一下^\n 可以理解为当\n的后面还有一\n时匹配后面的那个\n,\n$可以理解为当\n的前面还有一个\n时匹配前面那个\n,所以两个合起来就是当同时满足\n的后面有一个\n,\n的前面也有一个\n时匹配中间那个\n,也即匹配3个连续\n的中间那个\n,由于把\n替换成了空,所以可以一直替换直到还剩两个\n。
<![if !supportLists]>4. <![endif]>删除行末空白
<![if !supportLists]>4.1. <![endif]>%s/\s\+$//g.
在\n的前面有一个或多个空白,把这些空白替换成空。对于带有空白的空行会删去空白,但空行仍然保留
删除行前空白
%s/^\s\+//g