Skip to content

Vim 搜索、替换与特殊用法

Vim 的搜索和替换不是简单的“查找字符串”。它和移动、文本对象、寄存器、命令行模式结合后,可以承担大量结构化编辑任务。

本文聚焦三个方向:

  • 搜索和替换的日常用法。
  • Vim 正则里容易混淆的特殊语法。
  • 文本对象、屏幕移动、重复命令等高频特殊技巧。

搜索

基本搜索

vim
/pattern      " 向后搜索
?pattern      " 向前搜索
n             " 沿相同方向找下一个
N             " 沿相反方向找下一个
*             " 向后搜索光标下单词
#             " 向前搜索光标下单词
g*            " 搜索包含光标下字符串的位置,不要求单词边界
g#            " 反向 g*

搜索后常配合跳转和修改:

vim
/TODO
n
ciwDONE<Esc>
n
.

这表示:找到 TODO,改成 DONE,再到下一个匹配处重复修改。

搜索设置

vim
set ignorecase    " 搜索忽略大小写
set smartcase     " 搜索词包含大写时自动区分大小写
set incsearch     " 输入搜索词时实时跳转
set hlsearch      " 高亮搜索结果

清除搜索高亮:

vim
:nohlsearch

常用映射:

vim
nnoremap <leader><space> :nohlsearch<CR>

搜索寄存器

最近一次搜索模式保存在 "/ 寄存器里。

vim
:echo @/
:let @/ = 'error'

修改 @/ 后,按 n 会按新模式继续搜索。

替换

基本替换

vim
:s/old/new/        " 替换当前行第一个匹配
:s/old/new/g       " 替换当前行所有匹配
:%s/old/new/g      " 替换全文所有匹配
:%s/old/new/gc     " 替换全文所有匹配,并逐个确认

范围可以灵活指定:

vim
:10,20s/foo/bar/g      " 第 10 到 20 行替换
:'<,'>s/foo/bar/g      " 可视模式选区替换
:.,$s/foo/bar/g        " 当前行到文件末尾替换

确认替换

c 标志会逐个确认。

vim
:%s/foo/bar/gc

确认时常用按键:

text
y    替换当前匹配
n    跳过当前匹配
a    替换当前和后续所有匹配
q    退出替换
l    替换当前匹配后退出

使用上一次搜索作为替换目标

替换命令里省略搜索模式时,会使用上一次搜索。

vim
/foo
:%s//bar/g

这适合先确认搜索结果,再执行替换。

替换中引用匹配

vim
:%s/\v(\w+)\s+(\w+)/\2, \1/g

把两个词的顺序反过来。\1\2 表示第 1、第 2 个捕获组。

不想捕获的分组可以用 \%()

vim
/\v%(And|D)rew Neil

全局命令

:global 可以对匹配行执行命令。

vim
:g/TODO/p          " 打印所有包含 TODO 的行
:g/TODO/d          " 删除所有包含 TODO 的行
:v/TODO/d          " 删除所有不包含 TODO 的行
:g/^$/d            " 删除空行

它也可以和普通命令结合:

vim
:g/TODO/normal! A  # FIXME

这表示对所有包含 TODO 的行,在行尾追加文本。

Vim 正则特殊语法

Vim 的正则和很多语言里的正则不完全一样。最重要的是 magic 模式和若干 Vim 专用标记。

magic、very magic、very nomagic

Vim 默认正则需要对部分特殊字符加反斜杠。为了让表达式更接近现代正则,可以使用 \v

vim
/\vfoo|bar       " very magic,| 具有特殊含义
/\Vfoo.bar       " very nomagic,大多数字符按原义匹配

实践建议:复杂搜索优先使用 \v,普通字符串搜索可直接输入。

常见字符类

vim
.        " 任意字符,不含换行
\d       " 数字
\D       " 非数字
\w       " 单词字符,包括字母、数字和下划线
\W       " 非单词字符
\s       " 空白字符
\S       " 非空白字符
\x       " 十六进制字符

跨行匹配空白时使用 \_s

vim
/\vfoo\_s+bar

\_s 会匹配空白符或换行符。

单词边界

vim
/\<word\>       " 精确匹配单词 word
/\v<word>       " very magic 下的写法

* 搜索光标下单词时,本质上也会使用类似单词边界的匹配。

控制匹配范围:\zs\ze

\zs 标记匹配结果的开始,\ze 标记匹配结果的结束。它们常用于“上下文参与判断,但不进入最终匹配”。

vim
/\v"[^"]+"          " 匹配包括引号在内的字符串
/\v"\zs[^"]+\ze"    " 只匹配引号内部内容

替换示例:

vim
:%s/\vclass="\zs[^"]+\ze"/active/g

只替换 class="..." 里面的值,不替换引号和属性名。

分组与选择

vim
/\v(foo|bar)        " 捕获分组
/\v%(foo|bar)       " 非捕获分组

替换时,捕获分组可以用 \1\2 引用。

vim
:%s/\v(\w+),\s*(\w+)/\2 \1/g

文本对象

文本对象让命令从“按字符移动”变成“按语义选择”。

常见格式:

text
操作符 + i/a + 对象

i 表示 inner,不包含外侧符号或空白;a 表示 around,包含外侧符号或相关空白。

vim
ciw       " 修改当前单词
daw       " 删除当前单词及周围空白
yi"       " 复制双引号内部
ca"       " 修改整个双引号区域
di(       " 删除括号内部
ya{       " 复制整个花括号区域
vip       " 选中当前段落内部
dap       " 删除当前段落

常见对象:

vim
iw / aw     " 单词
iW / aW     " WORD,以空白分隔
i" / a"     " 双引号
i' / a'     " 单引号
i` / a`     " 反引号
i( / a(     " 圆括号
i[ / a[     " 方括号
i{ / a{     " 花括号
it / at     " 标签
ip / ap     " 段落
is / as     " 句子

文本对象是 Vim 从“移动光标”升级到“操作结构”的关键。

特殊移动与编辑习惯

屏幕内移动

vim
H        " 移动到当前屏幕顶部
M        " 移动到当前屏幕中间
L        " 移动到当前屏幕底部
zz       " 当前行居中
zt       " 当前行置顶
zb       " 当前行置底

HML 是按屏幕位置移动,不是按文件行号移动。

行内查找

vim
fx       " 向后移动到字符 x
Fx       " 向前移动到字符 x
tx       " 向后移动到字符 x 之前
Tx       " 向前移动到字符 x 之后
;        " 重复上一次 f/t/F/T
,        " 反向重复上一次 f/t/F/T

示例:

vim
dt,      " 删除到下一个逗号前
ct)      " 修改到右括号前

匹配跳转

vim
%        " 跳到匹配的括号、注释边界或部分语言结构

常见用法:

vim
d%       " 删除到匹配位置
v%       " 选中到匹配位置

重复修改

vim
.        " 重复上一次修改

推荐把修改设计成单次动作:

vim
ciwnew<Esc>
n
.

这样可以搜索下一个位置并重复同样修改。

可视块编辑

可视块适合列编辑。

vim
Ctrl-v      " 进入块选择
I           " 在块的每一行前插入
A           " 在块的每一行后插入

示例:给多行前面加 //

vim
Ctrl-v
j j j
I// <Esc>

推荐排查方式

遇到搜索或替换结果不符合预期时,可以这样排查:

  • 先用 /pattern 确认匹配范围,再用 :%s//new/g 替换。
  • 复杂正则加 \v,减少反斜杠噪音。
  • 需要保留上下文时,用 \zs\ze 控制真正替换的范围。
  • 替换前先加 c 标志逐个确认。
  • 不确定命令含义时查 :help pattern:help :substitute:help text-objects
别急,先让缓存热一下。