浅说VIM中的一些技巧2

纪念Bram Moolenaar

上一期的漏掉的点以及错误

错误

上一次说的d2w和2dw都是删除两个单词,区别在于d2w是告诉d要删除两个单词,而2dw是执行两次dw,总体结果一样但是有细微区别。

% 能操作用于跳转到匹配的括号处,可以匹配(),{},[]和xml标签,没有<>。

漏掉的重要点

正常模式

.可以重复执行上一次操作,或者重复编写你上一次插入模式下干了什么。

这个非常有用,比如

你本来想删除两行,结果你执行了dd只删除了一行,你就可以按.再删除一行。

或者你想写6个test,你还可以这样写,下面是从正常模式下开始键入字符的,其中<ESC>代表esc键

itest <ESC>Atest <ESC>....

这样你就写了5个test

dd可以删除当前行

yy可以复制当前行

={motion} 可以自动匹配缩进,不知道motion有什么作用的可以看看上期。(此操作可以用在可视模式下,下面有说)

== 自动匹配当前行缩进。

可视模式

除了i(,a(,i[,a[,i{,a{的操作,还有i<,a<,i",a",i',a'和i`,a`,这些东西的用法如出一辙,可以看上一期(注意这是可视模式下的操作,不要搞错了)。

选择一段内容后可以按=自动匹配一块代码的缩进

正文

第四部分命令行模式

命令行模式可以在正常模式或者可视模式下键入:(冒号)进入。

如果说移动光标是vim的入门,那么我认为操作命令行模式将是你进阶vim的必经之路!

正常模式下的命令行模式

使用:进入命令行模式可以调用vim内置命令或函数,当然你也可以自己写命令和函数,因为vim有自己的脚本语言叫vimscript!

常用的命令有:

09df546e600f4e2e8515ed90ead34302.jpg

 此图摘自《vim实用技巧》

老样子先说说新的参数有什么作用,[range]用于指定一个范围。比如:1,3指定1到3行的数据。当然还有一些特别的变量,比如

%用于指定整个文件的范围

. 用于代表当前行数

$ 用于代表文件最后一行

更多详情请执行:h range

参数[x]是寄存器,由于还没有详细讲寄存器所以知道一下就行,如果没有指定寄存器参数,默认会存进vim特殊的寄存器中,可以在正常模式下用p直接粘贴出来(可以看看上一期)。

参数{file}指定一个文件

参数{address}指定一个地址,在这里是行地址。

参数{pattern}用于正则表达式的匹配

[flags]是一些参数用于改变某个命令的行为

{string}就是简简单单的字符串辣。

此外这里所有的命令,除了copy和normal,copy可以简写为co,normal简写为norm,剩下的都可以用它们的首字母来代替整个命令,比如:delete,你可以直接写成:d,不必写出全名,极大的方便了操作!

比如你可以试着执行以下命令,看看都有什么效果!其中<CR>代表回车

:%d<CR>

:.,$y<CR>

:.m.-2<CR>

:1,5j<CR>

:%normal I//<CR>

:%normal A;<CR>

接下来解释这几行命令

:%d<CR> %指定整个文件d代表删除,所以这个命令清空了这个文件的缓冲区。

:.,$y<CR> .代表当前行,$代表缓冲区最后一行,y是复制,所以这条命令复制了当前行到最后一行的所有内容

:.m.-2<CR> .是当前行,m用于移动,所以这条命令是把当前行移动到.-2行的下面,也就是把这一行挪到上一行。举着个例子的主要意图就是说一下这些特殊的变量可以进行运算。

:1,5j<CR> 是将1到5行内容和并成一行用空格隔开。

:%norm I//<CR> 很好的指令,我们来解读一下,%表示整个缓冲区,norm用于将范围中的每一行都执行一个正常模式的命令,I(大写i)//这个东西不是一个指令,正常模式中I是跳到本行行首并进入插入模式,然后插入//,所以这个指令的最终效果是注释整个文件

:%norm A; 把每一行的末尾都加上一个;

这个命令试着自己分析一下!

现在有一个需求试着自己写一下,有这样一个代码

enum Test{

  TEST_1 = 0,

  TEST_2 = 1,

  TEST_3 = 2,

  TEST_4 = 3,

  TEST_5 = 4,

  TEST_6 = 5

};

 

typedef enum Test Test;

Test t;

.....; //t经过一系列操作

switch (t){

}

你需要将这些枚举成员全都在switch里面列出来,尝试思考一下怎样才能最快的完成这个任务!其实这个任务使用substitute比较简单,但是你可以试试在使用其他的指令(比如normal)完成。

关于substitute和global指令,因为需要学习完正则表达式才能发挥最大威力,所以这里只是让大家知道一下。

可视模式下的命令行模式

其实和正常模式下的命令都是一样的,区别在于从可视模式下进入命令行模式(键入:)会自动指定range。比如你现在有一些这样的文本。

I am vimmer.

I am currently studying Vim.

Input : used to enter Ex commands.

在正常模式下

现在你的光标在第一行(不在第一行键入gg跳到第一行),键入V(大写v),再键入:进入命令行模式,你会发现此时的命令行模式多了一些内容,没错就是:'<,'>,

其中'<用于指定可视块的开头行(对于linewise-visual),

或者指定可视块的开始字符(对于characterwise-visual),

或者指定可视块的第一行的最后一个字符(对于blockwise-visual),

这里全都用官方的名字,对于上一期我自创的名字深感抱歉。

由于刚才是使用V(大写v)进入的linewise-visual所以'<表示块开头行,'>表示块末尾行,此时你可以进行一些操作,比如d删除,y复制,或者m移动,等等…。从可视模式进入命令行模式会自动指定范围,这种操作可以说是非常的方便!

现在你用可视模式指定了第一行的文本,接下来可以用m命令进行移动,输入:m'<-2<CR>。可以让内容向上移动一行,输入:m'>+1<CR>可以让内容向下移动一行。

看起来不错,但是太费劲了!移动一些代码还要写这么多东西。不过你可以讲以下两行内容放到~/.vimrc的最末尾

vnoremap J :m'>+1<CR>gv=gv

vnoremap K :m'<-2<CR>gv=gv

这样你就把可视模式下的J和K映射成了移动代码的操作,你可能有疑问gv=gv是什么意思?回顾上一期,gv是重新选中上一次的可视块=用来自动匹配缩进,最后再gv选中上一次的可视块。这样整个操作就形成了闭环,不仅可以连续的上下移动,还可以根据代码自动调整缩进!

第五部分搜索字符串和替换字符串

在众多内容中搜索特定的字符串,岂不美哉?

搜索命令是在正常模式下按/或者?区别如下

/{pattern} 正向搜索

?{pattern} 反向搜索

比如/test,在当前缓冲区中正向搜索test字符串,如果有多个匹配可以使用n跳到下一个,N跳到上一个匹配的位置。

替换字符串使用substitue指令,缩写为s。

比如

:%s/int/long long int/g 这个指令会把缓冲区所有的int全部换成long long int,后面这个g代表全局,不指定g默认只会替换每一行的第一个。

最简单的搜索肯定是没有意思的,这里所有的{pattern}参数都可以传入一个正则表达式,这样搜索和替换命令可以达到非常大的威力!

第六部分vim正则表达式

vim正则表达式语法风格更接近POSIX版本

1.基本正则表达式字符

  •  . :匹配任意单个字符。
  •  * :匹配前一个字符0次或多次。
  •  ^ :匹配行的开始。
  •  $ :匹配行的结束。
  •  [abc] :匹配方括号内的任意一个字符(在这个例子中是a、b或c)。
  •  [^abc] :匹配不在方括号内的任意一个字符。
  • [a-z0-9]: 匹配a到z所有的小写字母和所有数字
  •  \ :转义字符,用于匹配特殊字符或字面意义上的正则表达式符号

2. 特殊字符类

  •  \d :匹配任意数字,等同于 [0-9] 。
  •  \D :匹配任意非数字。
  •  \w :匹配单词字符(字母、数字和下划线),等同于 [A-Za-z0-9_] 。
  •  \W :匹配非单词字符。
  •  \s :匹配空白字符(空格、制表符等)。
  •  \S :匹配非空白字符。

3. 量词

  •  + :匹配前一个字符1次或多次。
  •  ? :匹配前一个字符0次或1次。
  •  {n} :匹配前一个字符恰好n次。
  •  {n,} :匹配前一个字符至少n次。
  •  {n,m} :匹配前一个字符至少n次,但不超过m次。

4. 分组和引用

  •  (abc) :将abc看作一个整体进行匹配。
  •  \1 :引用第一个匹配的分组。
  •  \2 :引用第二个匹配的分组,以此类推。

学过正则表达式的肯定会觉得非常的轻松。

现在有这么一个搜索任务,

body { color: #3c3c3c; } 
a { color: #0000EE; } 
strong { color: #000; } 

内容摘自《vim实用技巧》。

这是一个css片段代码,你的任务是搜索到所有的16进制颜色代码,聪明的人肯定会说,那我就/color 搜索color我就能找到16进制颜色代码了。确实,但是这样不是我们想要的,我们要用正则表达式直接匹配16进制颜色代码。

众所周知css颜色代码有6位,3位和8位16进制代码,所以我们可以写出如下表达式

/#\([0-9a-fA-F]\{8}\|[0-9a-fA-F]\{6}\|[0-9a-fA-F]\{3}\)

好长啊!我们来解读一下,#在这里只是匹配普通的#字符,后面使用\(转义,意思是这个括号不是正常的括号,而是要进行分组了,[0-9a-fA-F]这个不难看出这是用来匹配一位16进制数。\{8}这也是一个转义,转义成一个量词匹配这个16进制数恰好出现三次,\|转义为或。整体意思就是说,匹配8位或者6位或者3位十六进制数。

这下理解了,但是这也太长了,特别是\用了7次!,那么如何缩短呢?正好vim提供了一种very magic搜索模式,进入这种搜索模式可以简化上面的表达式

/\v#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})

看起来舒服一些了,\v就是用于进入very magic模式,这样可能又有人问了,进入这种模式我该怎么匹配正常的(字符呢?很简单用\(转义,实际上有点反过来的意味,但是并不都是。具体了解请:h /\v。

那么这个表达式还能再精简,因为vim提供了一个特殊字符类\x用于匹配16进制数,也就是说\x就是[0-9a-fA-F]。那么最后的表达式会变成这样

/\v#(\x{8}|\x{6}|\x{3})

太棒了!非常简约的表达式,一看就知道什么意思。

说完正则表达式搜索,可以想想替换了,还记得在第4部分留的练习题吗,我们就拿它来下手!

enum Test{

  TEST_1 = 0,

  TEST_2 = 1,

  TEST_3 = 2,

  TEST_4 = 3,

  TEST_5 = 4,

  TEST_6 = 5

};

 

typedef enum Test Test;

 

Test t;

.....; //t经过一系列操作

switch (t){

}

假设switch在第50行,enum在第1行,我们可以这样做

正常模式下

:2,7co50<CR>

:51,56s/\v(\w+)\s\=\s\d+,?/case \1: break;/<CR>

简单快捷,首先:2,7co50<CR>就把2到7行的内容复制到了50行下面,也就是switch下面,然后替换51行到56行的字符串,搜索规则是

\v(\w+)\s\=\s\d+,? 首先\v代表进入very magic模式,(\w+) \w是[0-9a-zA-Z_],+代表1个以上,所以整体就是匹配一个标识符,外面的()代表加入到分组,现在是加入到分组1,\s\=\s,\s是匹配空格或者制表符,注意这里的\=匹配的是普通的=,在very magic中=是有特殊意义的。然后\d+,\d是[0-9]的意思,这就是在匹配一个数字,这里的+用的很巧妙,因为数字可能不止一位。最后的,?意思是: 逗号出现了0次或者1次,因为在enum中最后一个元素可能没有逗号所以这么写。

再看替换规则

case \1: break;

这里面\1是引用刚才的分组,也就是把每行的标识符给拿了过来,其他的都是普通的字符串。最后完成了这个枚举成员全部写到switch里面的操作。非常快速,说vim是神之编辑器可以说是一点也不夸张的。

那么这次的技巧就写到这里了,正则表达式可以千变万化,试试针对你的需求自己也写一个练习一下吧!

 

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值