Vim / Neovim 完全指南 / 08 - 搜索与替换
“Know your regex, know your editor.”
8.1 搜索基础
8.1.1 基本搜索
/pattern " 向前搜索
?pattern " 向后搜索
n " 下一个匹配
N " 上一个匹配
* " 搜索单词(光标下,向前)
# " 搜索单词(光标下,向后)
g* " 部分匹配(向前)
g# " 部分匹配(向后)
8.1.2 搜索选项
/pattern/e " 光标停在匹配末尾
/pattern/b " 光标停在匹配开头
/pattern/+2 " 停在匹配下方 2 行
/pattern/-1 " 停在匹配上方 1 行
8.1.3 搜索设置
:set ignorecase " 忽略大小写(ic)
:set smartcase " 智能大小写(scs)— 有大写字母时区分
:set hlsearch " 高亮搜索结果(hls)
:set incsearch " 增量搜索(is)
:nohlsearch " 取消高亮
:noh " 同上
-- Neovim 配置
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.hlsearch = true
vim.opt.incsearch = true
8.2 正则表达式(Regular Expression)
8.2.1 Vim 正则引擎
Vim 有 4 种正则模式:
| 前缀 |
名称 |
说明 |
| 无 |
magic |
默认模式 |
\v |
very magic |
接近标准正则语法 |
\m |
magic |
等同默认 |
\M |
nomagic |
特殊字符需转义 |
\V |
very nomagic |
几乎所有字符按字面匹配 |
" 使用 \v(very magic)避免大量转义
/\vfunction\s+\w+ " 标准正则风格
/function\s\+\w\+ " 默认 magic 模式(需要转义 +)
" 使用 \V(very nomagic)搜索特殊字符
/\V$100.00 " 精确搜索 $100.00
/$100\.00 " 默认模式需要转义
8.2.2 常用正则模式
| 模式 |
含义 |
示例 |
. |
任意字符 |
/a.c 匹配 abc, aXc |
* |
0 次或多次 |
/ab*c 匹配 ac, abc, abbc |
\+ |
1 次或多次 |
/ab\+c 匹配 abc, abbc |
\? |
0 次或 1 次 |
/ab\?c 匹配 ac, abc |
\{n,m} |
n 到 m 次 |
/a\{2,4} 匹配 aa, aaa, aaaa |
^ |
行首 |
/^function 匹配行首的 function |
$ |
行尾 |
/end$ 匹配行尾的 end |
\< |
词首 |
\<word 匹配词首的 word |
\> |
词尾 |
word\> 匹配词尾的 word |
\b |
词边界 |
\bword\b 匹配完整单词 |
[] |
字符类 |
/[aeiou] 匹配元音 |
[^] |
否定字符类 |
/[^0-9] 匹配非数字 |
| |
或 |
/foo|bar 匹配 foo 或 bar |
\(\) |
分组 |
/\(foo\)\+ 匹配 foo, foofoo |
\1 |
反向引用 |
/\(.\)\1 匹配连续相同字符 |
8.2.3 字符类(Character Class)
| 模式 |
含义 |
\d |
数字 [0-9] |
\D |
非数字 |
\w |
单词字符 [a-zA-Z0-9_] |
\W |
非单词字符 |
\s |
空白字符 |
\S |
非空白字符 |
\l |
小写字母 [a-z] |
\u |
大写字母 [A-Z] |
\a |
字母 [a-zA-Z] |
\h |
字母或下划线 |
8.2.4 实用正则示例
" 匹配邮箱
/\v[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
" 匹配 IP 地址
/\v\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
" 匹配 URL
/\vhttps?://[^\s]+
" 匹配 HTML 标签
/\v\<\zs[^>]+\ze\>
" 匹配函数定义(Python)
/\v^\s*(def|class)\s+\w+
" 匹配中文字符
/\v[\x{4e00}-\x{9fff}]
8.3 替换(Substitute)
8.3.1 替换语法
:[range]s/pattern/replacement/[flags]
| 参数 |
说明 |
range |
范围(% 全文, 1,10 第1-10行, '<,'> 选区) |
pattern |
搜索模式 |
replacement |
替换文本 |
flags |
标志(g 全局, c 确认, i 忽略大小写, e 无错) |
8.3.2 替换标志
| 标志 |
含义 |
g |
全局替换(一行中所有匹配) |
c |
逐个确认(confirm) |
i |
忽略大小写 |
I |
区分大小写 |
e |
不显示错误 |
n |
只计数不替换 |
& |
复用上次标志 |
8.3.3 替换范围
:s/old/new/g " 当前行全局替换
:%s/old/new/g " 全文替换
:1,10s/old/new/g " 第 1-10 行替换
:'<,'>s/old/new/g " 可视选区替换
:'<,'>s/old/new/gc " 选区替换(逐个确认)
:.,$s/old/new/g " 当前行到文件末尾
:.,+5s/old/new/g " 当前行及以下 5 行
8.3.4 替换中的特殊字符
| 字符 |
含义 |
\r |
换行 |
\n |
NULL 字节(替换中用 \r 表示换行) |
\t |
Tab |
\\ |
反斜杠 |
\0 |
整个匹配 |
\1-\9 |
第 N 个捕获组 |
\u |
下一个字符大写 |
\U |
后续字符大写 |
\l |
下一个字符小写 |
\L |
后续字符小写 |
\e |
结束 \U 或 \L |
& 或 \0 |
整个匹配 |
8.3.5 替换实战
" 基本替换
:%s/foo/bar/g " 全文 foo → bar
:%s/foo/bar/gc " 逐个确认
" 大小写转换
:%s/\v(\w+)/\U\1/g " 全文转大写
:%s/\v^(\w)/\u\1/g " 每行首字母大写
" 行操作
:%s/\v^$/\r/ " 空行后加一行
:%s/\v^\s+$//g " 删除只含空白的行
:%s/\v\n\n+/\r\r/g " 多个空行压缩为一个
" 添加行号
:%s/^/\=line('.').'. '/ " 每行前添加行号
" 删除行
:%g/^\s*$/d " 删除空行
:%v/pattern/d " 删除不包含 pattern 的行
" 交换捕获组
:%s/\v(\w+)\s+(\w+)/\2 \1/g " 交换两个单词
" 引号转换
:%s/"/'/g " 双引号转单引号
" 日期格式转换
:%s/\v(\d{4})-(\d{2})-(\d{2})/\3\/\2\/\1/g " 2024-01-15 → 15/01/2024
8.3.6 替换表达式
" 在替换中使用表达式
:%s/\v\d+/\=submatch(0)*2/g " 所有数字乘以 2
:%s/\v\d+/\=printf('%04d', submatch(0))/g " 数字补零到 4 位
8.4 全局命令(Global)
8.4.1 基本语法
:[range]g/pattern/command
对匹配 pattern 的行执行 command。
8.4.2 常用全局命令
" 列出匹配的行
:g/pattern/p
" 删除匹配的行
:g/pattern/d
" 删除不匹配的行
:v/pattern/d " 等同 :g!/pattern/d
" 复制匹配的行到文件末尾
:g/pattern/t$
" 移动匹配的行到文件末尾
:g/pattern/m$
" 对匹配行执行 Normal 模式命令
:g/pattern/normal @q " 对匹配行执行宏 q
" 在匹配行上方添加文本
:g/pattern/-1append\nNew line here
" 给匹配行添加行号
:g/pattern/s/^/\=line('.').': '/
8.4.3 全局命令实战
" 删除所有注释行(Python)
:v/^\s*#/d " 保留注释行,删除其余
:g/^\s*#/d " 删除注释行
" 删除所有 debug 语句
:g/console\.\(log\|debug\)/d
" 将所有 TODO 行复制到文件末尾
:g/TODO/t$
" 给所有函数添加注释
:g/^def /normal O# TODO: implement this
8.5 vimgrep 与 Grep
8.5.1 内置 vimgrep
:vimgrep pattern files " 搜索文件
:vimgrep /TODO/ **/*.py " 搜索所有 Python 文件中的 TODO
:vim /function/ src/**/*.js " 搜索 JavaScript 文件
" 导航结果
:copen " 打开 quickfix 窗口
:cclose " 关闭 quickfix
:cn " 下一个匹配
:cp " 上一个匹配
:cfirst " 第一个匹配
:clast " 最后一个匹配
8.5.2 外部 grep
:grep pattern files " 使用外部 grep
:grep! -r "TODO" . " 递归搜索
:copen " 查看结果
8.5.3 ripgrep 集成
-- 使用 ripgrep 作为 grep 程序
vim.opt.grepprg = "rg --vimgrep --no-heading --smart-case"
vim.opt.grepformat = "%f:%l:%c:%m"
8.6 搜索高亮管理
:noh " 临时取消高亮
:set nohlsearch " 永久取消高亮
" 或使用映射
nnoremap <Esc> :noh<CR><Esc>
8.7 业务场景
| 场景 |
方法 |
| 全局重命名变量 |
:%s/old_name/new_name/g |
| 删除空行 |
:g/^\s*$/d |
| 提取 TODO |
:g/TODO/p 或 vimgrep |
| 批量注释 |
:'<,'>s/^/#/g |
| 代码审查 |
vimgrep + quickfix |
| 格式化日期 |
:%s + 捕获组 |
| 去重排序 |
:sort u |
8.8 总结
| 功能 |
命令 |
| 搜索 |
/pattern ?pattern * # n N |
| 替换 |
:[range]s/pattern/repl/[flags] |
| 全局命令 |
:[range]g/pattern/command |
| 文件搜索 |
:vimgrep :grep |
| 结果导航 |
:copen :cn :cp :cc |
下一步:第 09 章 - VimScript 编程 → 学习 Vim 的脚本语言,编写函数和自动命令。
扩展阅读