Vim - substitute (替换, replace)

创建时间:
2015-07-20 23:47
最近更新:
2018-09-09 22:46

Documentation

:h :s
:h :su
:h substitute
:h su
:h flags

VIM 用 substitute 而非 replace,截至 2015-11-24 Tony 尚未发现官方说法。

分隔符

Instead of the '/' which surrounds the pattern and replacement string, you can use any other single-byte character, but not an alphanumeric character, '\', '"' or '|'. This is useful if you want to include a '/' in the search pattern or replacement string. Example: :s+/+//+
-- :h su

2015-11-28 Tony 测试记录:
可以代替 / 的字符有: #!
不可代替 / 的字符有: \"|

替换命令中的函数式 \=line()\=submatch()

在替换命令 s/// 中可以使用函数表达式来书写替换内容,格式为 :s/替换字符串/\=函数式
在函数式中可以使用 submatch(1)submatch(2) 等来引用 \1\2 等的内容,而 submatch(0) 可以引用匹配的整个内容。
:%s/\<id\>/\=line(".") 将各行的 id 字符串替换为行号。
:%s/^\<\w\+\>/\=(line(".")-10).".".submatch(1) 将每行开头的单词替换为 (行号-10).单词 的格式,便如第 11 行的 word 替换成 1.word

Substitute with an expression \=

*sub-replace-expression*
*sub-replace-\=*

When the substitute string starts with "\=" the remainder is interpreted as an expression.

-- :h sub-replace

line()

以下命令测试有效:
每行行首添加行号: :%s//\=line('.')
每行行首添加行号: :%s/^/\=line('.')
每行行首添加行号: :%s/^/\=line('.').'. xxx'/

submatch() - 替换 中的 计算

:%s/(\(\d+\))/\="(".(submatch(1)+1).")"/g
上一行命令 将 (1), ...., (2), ....,(100) 替换成 (2), ...., (3), ...., (101)。该命令各组成部分解释如下:

%       全文(“%”是“1,$”范围的缩写)
s       替换
/       搜索字符串开始
(       左括号
\)      开始记录匹配
\d+     一个或多个数字
\)      结束记录匹配
)       右括号
/       搜索字符串结束
\=      把后面的表达式计算出来作为替换字符串
"("     左括号
.       字符串连接运算符
(submatch(1) + 1)       把第一个匹配的结果加一作为一个整体返回
.")"    添上右括号
/g      替换字符串结束,g表示替换每一行的所有匹配结果。

Carriage-Return & Line-Feed (替换回车与换行)

在 gVim 中 Tony 曾使用 :%s/\n\n\n/\r/g 成功替换了换行符。

说明:
执行 [range]s[ubstitute]/{FromPattern}/{ToString}/[flags] [count] 命令时,
FromPattern 中的每 1 个 ``\n 均匹配了 \r\n 共 2 个字符;``
ToString 中的每 1 个 ``\r 均写入了 \r\n 共 2 个字符。```
gVim 的上述行为,Tony 对比执行 substitute 命令前后的 16 进制才找出此替换规则,在 gVim-Help 与 Internet 上均未找到官方说明。

常用

:%s/\s\+$// 去掉所有的行尾空格。% 表示在整个文件范围内进行替换;\s 表示空白字符 (空格和制表符);\+ 对前面的字符匹配一次或多次 (贪婪);$ 匹配行尾。
:%s/\(\s*\n\)\+/\r/ 去掉所有的空白行。* 代表对前面的字符匹配零次或多次;\( 和 ) 对表达式进行分组,使其被视作一个不可分割的整体。因此,这个表达式的完整意义是,把连续的换行符 (包含换行符前面可能有的连续空白字符) 替换成为一个单个的换行符。唯一很特殊的地方是,在模式中使用的是 \n,而被替换的内容中却不能使用 \n,而只能使用 \r。原因是历史造成的,详情如果有兴趣的话可以查看 :help NL-used-for-Nul
:%s!\s*//.*!! 去掉所有的 // 注释。首先可以注意到,这儿分隔符改用了 !,原因是在模式或字符串部分使用了 / 字符,不换用其他分隔符的话就得在每次使用 / 字符本身时写成 \/,即 :%s/\s*\/\/.*//,可读性较低。
:%s!\s*/\*\_.\{-}\*/\s*! !g 去掉所有的 /* */ 注释。\_. 匹配包含换行在内的所有字符;\{-} 表示前一个字符可出现零次或多次,但在整个正则表达式可以匹配成功的前提下,匹配的字符数越少越好;标志 g 表示一行里可以匹配和替换多次。替换的结果是个空格的目的是保证像 int/* space not necessary around comments */main() 这样的表达式在替换之后仍然是合法的。
:%s/\w*/\u&/g 单词首字母大写
:%s/\w*/\U&/g 全部大写
:3,9s!^!//!g 注释 3 至 9 行