第五部分 模式
第 12 章 按模式匹配及按原义匹配
-
调整查找模式的大小写敏感性
① 全局调整
-
ignorecase
选项打开后,Vim的查找模式将不区分大小写- 副作用:影响Vim关键字自动补全的行为。
-
smartcase
选项打开后,只要输入一个大写字母,查找方式就会变成区分大小写的,换言之,全是小写字母的模式表示忽略大小写 -
ignorecase
和smartcase
同时启用时,smartcase
占主动,即Foo
会匹配不了foo
② 局部强制调整
- 每次查找时,可以通过在任意位置加入
\c
元字符表示忽略大小写,\C
元字符表示区分大小写 - 会覆盖全局调整
-
-
原义搜索 & 正则查找
①
magic
搜索模式 &very magic
搜索模式body { color: #3c3c3c; } a { color: #0000EE; } strong { color: #000; }
- 示例:要搜索上面代码中的颜色代码
magic
搜索模式:/#\([0-9a-fA-F]\{6}\|[0-9a-fA-F]\{3}\)
- 方括号缺省具有特殊含义,因此不用转义。
- 圆括号会按原义匹配字符
(
及)
,因此需要转义,且无论开闭括号都必须转义,使其具有特殊含义。 - 花括号也一样需要转义,不过,我们只需为开括号转义,而与之对应的闭括号则不用,因为Vim会推测我们的意图。
very magic
搜索模式:/\v#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})
- 使用
\v
模式开关将会激活very magic
搜索模式,即假定除_
、字母
以及数字
外的所有字符都具有特殊含义,所以各种括号不用再转义 - 用十六进制字符类进一步优化:
/\v#(\x{6}|\x{3})
,其中\x = [0-9a-fA-F]
#
是个特例啊,他虽然不属于[_a-zA-Z0-9]
,但是它不具有特殊含义,因此时按照原义进行匹配的
- 使用
②
nomagic
搜索模式nomagic
模式下和magic
模式基本一致,只是符号^
与$
代表开头和结尾- 我们可以通过
\m
与\M
开关,来分别使能magic
和nomagic
这两种语法
③
very nomagic
搜索模式- 即原义搜索,使用
\V
开启,开启后其后的模式只有反斜杠有特殊的意义,其他的都会按照原义匹配,最为直观
③ 四种模式之间的关系
- Vim 缺省使用
magic
搜索模式,magic模式的设计初衷,是想能更容易地构造简单的正则表达式,它会自动为某些额外的符号赋予特殊含义,例如:.
(匹配任意一个字符)、*
(前面的字符可以出现0到多次)以及[]
。但它却没能为诸如+
、?
、()
以及{}
等符号赋予特殊含义,这些符号还必须经过转义才具有特殊含义。 very magic
搜索模式弥补了magic
搜索模式的不足,除了[_a-zA-Z0-9]
外,它为所有符号都赋予了特殊含义。这样一来,既好记又恰好与Perl
正则表达式的规则保持一致,因此可以认为这是个正则表达式模式nomagic
模式用于模拟 vi 的行为,只是为^
和$
赋予了开头和结尾的特殊含义very nomagic
模式是最直观的方式,除了\
其他符号都没有特殊含义,都直接匹配原文- 作为通用法则,vim默认是需要打很多转义符号的 magic 模式;如果想按正则表达式查找,就用模式开关
\v
—— very magic 搜索模式;而如果你想按原义查找文本,就使用very nomagic模式;需要用到开头和结尾匹配的时候就用\M
—— nomagic搜索模式,只不过此模式下还是需要特别多的转义
- 示例:要搜索上面代码中的颜色代码
-
圆括号
① 使用圆括号捕获子匹配
-
当我们指定一个模式时,可以捕获其子匹配,并在其他地方引用它们。此功能与substitute命令组合起来尤为好用,但它也可用于定义某一类模式,这类模式的特点是重复包含某个单词。
-
/\v<(\w+)\_s+\1>
专门用来匹配重复单词- ⭐️ 任何圆括号内部的匹配文本都会被自动保存到一个临时的仓库。可以用
\1
引用这段被捕获的文本。如果模式中包含不止一组圆括号,则可以用\1
、\2
,直到\9
,引用被每对()
捕获的子匹配。 - 不论模式中是否使用了圆括号,元字符
\0
永远会引用整个匹配 - ⭐️
<
和>
两符号将用于匹配单词的边界,称为零宽度元字符- 像
the
这样的单词很容易出现在别的单词内部,如they
,这时用<the>
就可以避免匹配到后者 - 在
very magic
搜索模式下,<
与>
直接被解析为单词定界符,而在magic
、nomagic
以及very nomagic
搜索模式下,必须要将它们转义。 - 可以用
\W\ze\w
模拟元字符<
,而用\w\ze\W
模拟元字符>
*
和#
查找间接使用了单词定界符,而g*
和g#
不会使用单词定界符,这是它们唯一的差别
- 像
- ⭐️ 任何圆括号内部的匹配文本都会被自动保存到一个临时的仓库。可以用
-
\w
匹配单词类字符,包括字母、数字及下划线\W
匹配单词类字符以外的其他字符
\_s
匹配空白符或换行符
② 圆括号的分组功能
-
/\v(And|D)rew
可以匹配Andrew和Drew -
/\v%(And|D)rew
每个括号前面的%
指示Vim不要将该括号内的内容赋给寄存器\1
- 当分组功能用的比较多时,使用
%
可以加快速度
③ 搭配使用
/\v(%(And|D)rew) (Neil)
- 前面没有
%
的括号表示要捕获其子匹配 %(And|D)
则表示单纯使用分组功能- 这一匹配搭配
:s//\2,\1/g
使用可以把文档中的Andrew/Drew Neil
替换成Neil,Andrew/Drew
- 当分组功能用的比较多时,使用
-
界定匹配的边界
-
模式是指查找域输入的正则表达式(或则按原义匹配的文本)
匹配是指在文档中被高亮显示的文本(假设
hlsearch
选项开启) -
\zs
和\ze
是用在模式里的零宽度元字符,用来界定最终匹配的始末。\zs
标志着一个匹配的起始,\ze
标志着匹配的结束例如
/Pratical\zsVim
只会高亮显示在Pratical
之后的Vim
,和/Vim
还是有明显的区别的再如
/\v"\zs[^"]+\ze"<CR>
会匹配所有在双引号之内的内容而不包含双引号,[^"]+
表示匹配1到多个非引号的字符 -
环视表达式
- Vim的元字符
\zs
与\ze
类似于Perl
的肯定型逆序环视断言和肯定型顺序环视断言 - Vim提供完整的否定型/肯定型顺序环视/逆序环视断言,但语法与Perl的语法有所区别。详见
:h perl-patterns
- 用正向环视元字符代替
\zs
和ze
:/\v"@<=[^"]+"@=
- Vim的元字符
-
-
转义问题字符
- 需要转义查找域结束符
- 正向查找
/
时,/
会被当做结束符;反向查找?
时,?
会被当做结束符 - 如果在查找域结束符之后附加某些标志位,可以调整Vim查找命令的行为。例如,如果我们运行命令
/vim/e<CR>
,光标将会移到每个匹配的结尾,而非起始
- 正向查找
- 无论哪种查找,反斜杠
\
都需要转义 - 用编程的方式转义字符
- Vim脚本提供了库函数
escape({string},{chars})
来自动完成加转义符号的任务{chars}
指定了哪些字符需要使用反斜杠转义- 可以将查找模式保存在一个寄存器中,例如
u
,然后进入查找模式,按<C-r>=
进入表达式寄存器,输入escape(@u, getcmdtype().'\')
来自动添加转义字符
- Vim脚本提供了库函数
- 需要转义查找域结束符
第 13 章 查找
-
普通模式下,按下
/
键会调出 Vim 的查找提示符,可在它的后面输入要查找的模式或者原义文本- 按下
<CR>
键时,Vim才会执行查找命令,而如果换用<Esc>
键的话,查找提示符会消失,我们将重回普通模式。
- 按下
-
/
表示正向查找,?
表示反向查找n
跳转到下一个匹配;N
跳转到上一处匹配- 如果本来是反向查找,突然反悔想改成正向查找,可以使用
/<CR>
,模式空着表示自动使用上次的查找模式;反之同理
-
查找命令抵达文档结尾处时会回绕至文档开头继续查找
wrapscan
关闭后,搜索到文档结尾将不会绕回文档开头
-
浏览查找历史与浏览命令行历史的接口完全一致,即可以使用<Up><Down>,也可以使用<C-p><C-n>,后者没有过滤功能
-
查找高亮
-
hlsearch
选项开启后,所有窗口中的匹配都会高亮显示- 但是这可能会导致窗口中到处都是黄色的匹配,影响观感
:set nohlsearch
/:se nohls
/:se hls!
可以彻底禁止高亮显示:noh[lsearch]
命令会暂时关闭查找高亮,直到执行下一条查找命令为止- 可以为这条命令创建一个快捷键开关:
nnoremap <silent> <C-l> :<C-u>nohlsearch<CR><C-l>
<C-l>
通常用于清除并重绘显示屏。上面的映射项,为其增加了暂时关闭查找高亮的功能。
- 可以为这条命令创建一个快捷键开关:
-
incsearch
选项开启后, Vim 会根据已在查找域中输入的文本,预览第一处匹配。每当我们新输入一个字符时,Vim会即时更新预览内容。- 如果文档很长,Vim会移动文档让匹配显示,利用这个特性可以验证一些单词是否在文档中,只要不按下
<CR>
,光标就不会跳转
- 如果文档很长,Vim会移动文档让匹配显示,利用这个特性可以验证一些单词是否在文档中,只要不按下
-
-
<C-r><C-w>
自动补全功能,会用当前预览的匹配结果对查找域进行自动补全。例如输入carr
,预览为carrot
,按下这两个键就会把carrot
全部填充到模式里,不用自己手动输全- ⚠️ 一旦在查找内容中加入元字符
\v
前缀,<C-r><C-w>
会把光标下的完整单词,而不是单词的余下部分,作为补全的内容(例如,执行补全后会变成/\vcarrcarrot<CR>
)
- ⚠️ 一旦在查找内容中加入元字符
-
统计当前模式的匹配个数
- 使用
:%s/{pattern}//gn
命令,标志位n
会抑制正常的替换动作,因此该命令不会对每处匹配进行替换,而是简单地统计匹配的次数,并将结果显示到命令行上。
- 使用
-
Vim 的查找偏移功能(
:h search-offset
)- 执行查找命令时,光标总会被定位于匹配的首字母上,查找偏移可以改变光标定位的位置
- 一般都是挪到匹配的最后一位,命令为
/{pattern}/e
- 如果搜索到了中途想要使用查找偏移功能,也可以利用
//e
进行补救 - 使用场景
Aim to learn a new programming lang each year. Which lang did you pick up last year? Which langs would you like to learn?
将所有的lang补全为language,就可以用
/lang/e
,然后按a
输入余下部分,之后按n.
用.
范式进行处理即可 -
对完整的查找匹配进行操作
class XhtmlDocument < XmlDocument; end class XhtmlTag < XmlTag; end
- 如果要把其中的Xhtml和Xml都变成大写,可以通过以下几步
/\vX(ht)?ml\C<CR>
查找gU//e<CR>
利用上次的查找和查找偏移进行大写转换,但只能转换第一个//<CR>
重复上次的查找- 不用
n
是因为上次的查找为//e
,按n
会跳到尾部而不是头部
- 不用
- 用
.
重复gU//e<CR>
操作 - 之后就用
//<CR>.
连续操作
⭐️
textobj-lastpat
插件增加了一个i/
文本对象用于操作查找匹配,因此只需要使用gUi/
就可以完成一次修改,之后n.
即可 -
迭代完成复杂的模式
- 撰写正则表达式是一件很难的事情,很多时候不能一次写对
- 简单的修改可以直接回溯搜索历史,然后直接再命令行修改
- 复杂的修改可以
q/
打开搜索历史窗口,然后利用vim操作进行修改,最后<CR>
加载执行即可 - 例如要将所有单引号括住的内容改用双引号括住,可以使用
:%s/\v'(([^']|'\w)+)'/"\1"/g
命令,但是实际上,其中的查找命令是单独迭代构造,确认没有问题后,才运行:%s//"\1"/g
的
-
查找当前选中的文本 —— 即其他文本编辑器中<C-f>的功能
xnoremap * :<C-u>call <SID>VSetSearch()<CR>/<C-R>=@/<CR><CR>
xnoremap # :<C-u>call <SID>VSetSearch()<CR>?<C-R>=@/<CR><CR>
function! s:VSetSearch()
let temp = @s
norm! gv"sy
let @/ = '\V' . substitute(escape(@s, '/\'), '\n', '\\n', 'g')
let @s = temp
endfunction
将上面的vim脚本粘贴到.vimrc
中,就可以使得*
和#
在可视模式下的语义附加上搜索选中内容这一项
第 14 章 替换
-
语法:
:[range]s[ubstitute]/{pattern}/{string}/[flags]
- 缺省情况下,
:s
命令仅仅作用于当前行的第一处匹配
- 缺省情况下,
-
[flags]
为标志位(:h s_flags
)g
修改一行内的所有匹配,而不仅仅是第一处匹配 (在range里用%
才表示整个文档)c
可以确认或拒绝每一处修改n
不执行替换操作,而只是报告本次substitute 命令匹配的个数e
用于屏蔽错误提示,如E486: 找不到模式
&
重用上一次substitute命令的标志位
-
替换域中的特殊字符
:h sub-replace-special
符号 描述 \r
插入一个换行符 \t
插入一个制表符 \\
插入一个反斜杠 \N
插入第N个子匹配,最多到\9 \0
插入匹配模式的所有内容 &
插入匹配模式的所有内容 ~
使用上一次 :s
命令的{string}
\={Vim Script}
执行 {Vim Script}
,并将结果作为{string}
-
控制每一次替换操作
-
:%s/pattern/string/gc
-
每次替换都会提示,如
替换为 copy ?
-
回应选项为
答案 用途 y
替换此处匹配 n
忽略此处匹配 q
退出替换过程 l
替换此处匹配后退出 a
“all” —— 替换此处和之后所有的匹配 <C-e>
向上滚动屏幕 <C-y>
向下滚动屏幕
-
-
若将substitute命令的查找域留空,则Vim会重用上次的查找模式
- 把查找域留空,会在命令历史中留下一项不完整的记录。这是由于模式通常保存在Vim的查找历史记录中,而substitute命令则保存于Ex命令的历史记录中
- ⭐️ 只需在命令行中输入
<C-r>/
,即可把上次的查找内容粘贴进来
-
可以将需要搜索的字符串复制到寄存器,通过传值或引用的方式将寄存器的内容应用至替换域。
- 传值
<C-r>{register}
,缺点是一些有特殊含义的字符没有处理 - 引用
\=@{register}
➾:let @/='Pragmatic Vim' ➾:let @a='Practical Vim' ➾:%s//\=@a/g
:let @/='Pragmatic Vim'
是采用编程的方式输入查找模式,它等同于/Pragmatic Vim<CR>
,但不会留下历史记录:let @a='Practical Vim'
设置 a 寄存器的内容。它等同于高亮选中“Practical Vim”并用"ay
将选中的文本存入寄存器 a。可以通过调整上次的查找模式以及a寄存器中的内容来复用
:%s//\=@a/g
命令 - 传值
-
重复上一次substitute命令
- 假如忘记输入
%
,只需要输入g&
就可以在整个文件的范围内重复这条命令,等同于:%s//~/&
- 可以使用
:&&
命令重新执行替换操作。其中前一个&
重复上一次的:s
命令,而第二个&
会重用上一次:s
命令的标志位:&&
命令本身只作用于当前行:'<,'>&&
会作用于高亮选区g&
=:%&&
会作用于整个文件
- 假如忘记输入
-
&
用于重复上一次的替换操作,但是会忽略上次的标志位,可以用下面的映射把语义改成继承上次的标志位nnoremap & :&&<CR>
xnoremap & :&&<CR>
-
使用子匹配重排CSV文件的字段
last name,first name,email neil,drew,[email protected] doe,john,[email protected] ➾ /\v^([^,]*),([^,]*),([^,]*)$ ➾ :%s//\3,\2,\1 email,first name, last name ...
-
在替换过程中执行算术运算
例如将下面的标题都提升一级:
<h2>Heading number 1</h2> <h3>Number 2 heading</h3> <h4>Another heading</h4>
- 使用
/\v\<\/?h\zs\d
命令,可以匹配到所有出现在<h或</h之后的数字 - 在Vim脚本中,通过调用函数
submatch(0)
,可以得到当前匹配的内容。 - 因此使用
:%s//\=submatch(0)-1/g
就可以把所有的标签数字-1
- 使用
-
交换两个或更多的单词
-
通过使用表达式寄存器以及 Vim 脚本中的字典数据结构,我们可以设计一条特殊的substitute命令,用它来对两个单词进行互换
-
例如
The dog bit the man
中交换dog和man的位置➾ /\v(<man>|<dog>) ➾:%s//\={"dog":"man","man":"dog"}[submatch(1)]/g
- 在Vim脚本中,我们必须调用
submatch()
函数可以得到被捕获的子匹配,相当于之前在模式里使用的\1-\9
- 在Vim脚本中,我们必须调用
-
Abolish.vim
插件- 提供了一条名为
:Subvert
的自定义命令(或者简写为:S
) :%S/{man,dog}/{dog,man}/g
就可以实现两个单词的互换
- 提供了一条名为
-
-
在多个文件中执行查找与替换
-
散射法(不管该文件中有没有匹配都执行):
:argdo %s//{string}/ge
,加上一个e
是因为有些文件里可能没有匹配,虽然并不会中止执行,但是会影响观感 -
点射法
➾ /Pragmatic\ze Vim ➾:vimgrep /<C-r>// **/*.txt ➾:argdo %s//Practical/g ➾:argdo update
vimgrep
命令是Vim内置的搜索引擎,上面的命令会让vimgrep
在当前目录及子目录下所有txt文件中进行搜索- 每个由
vimgrep
返回的匹配都将在quickfix
列表中被记录下来 - 将下面的脚本插入
.vimrc
中,即可通过:Qargs
命令把quikfix
列表加载到参数列表里
command! -nargs=0 -bar Qargs execute 'args' QuickfixFilenames() function! QuickfixFilenames() let buffer_numbers = {} for quickfix_item in getqflist() let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr']) endfor return join(map(values(buffer_numbers), 'fnameescape(v:val)')) endfunction
:update
命令用于保存文件,但只有在文件发生改动时,才会被执行- 最后三个命令可以连起来使用
:Qargs | argdo %s//Practical/g | update
|
仅仅表示命令的分隔
-
第 15 章 global命令
-
global命令允许我们在某个指定模式的所有匹配行上运行 Ex 命令
-
语法:
:[range] global[!] /{pattern}/ [range][cmd]
-
[range]
缺省是整个文件(%
),而其他大多数 Ex 命令(包括 :delete、:substitute 以及 :normal)的缺省范围仅为当前行(.
)。 -
{pattern}
域与查找历史相互关联。如果将该域留空,Vim会自动使用当前的查找模式。 -
[cmd]
可以是除:global
命令之外的任何 Ex 命令,缺省为:print
-
:g[lobal]!
或者:v[global]
(v 表示 invert)将指示Vim在没有匹配到指定模式的行上执行[cmd]
-
-
global 和 delete 命令组合使用
-
:g//d
可以删掉特定匹配行 -
:v//d
删掉没匹配上的那些行例如下面的命令可以删掉所有带有xml标签的行
➾ /\v\<\/?\w+> ➾ :g//d
-
-
:global 和 :yank 命令结合使用
:g/{pattern}/yank A
- 注意寄存器要用大写的,表示附加而不是覆盖
- 通过这个方式,可以把一些行收集起来,比如收集所有的待办事项到一个寄存器里
- 也可以用
:g/{pattern}/t{address}
的方式,不放寄存器,直接粘贴在某个位置
-
将CSS文件中所有规则的属性按照字母排序
-
第一种方式:使用Vim 的内置命令
:sort
vi{
选中,执行:sort
将按首字母进行排序 -
第二种方式:
:g/{/ .+1,/}/-1 sort
-
这里搜索的是
{
,.+1,/}/-1
指定了Ex命令的范围,sort是实际执行的命令 -
⭐️ 总结起来就是:
:g/{start}/ .,{finish}[cmd]
即对从{start}到{finish}的所有文本行,执行[cmd] -
再给一个例子:
:g/{/ .+1,/}/-1 >
可以对花括号内的内容进行缩进- 但是它会有一个提示消息,在[cmd]前面加上
:silent
可以屏蔽这些信息,改进后就是:g/{/sil .+1,/}/-1 >
- 但是它会有一个提示消息,在[cmd]前面加上
-
-
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。