VIM正则表达式查替换
0. 一些需要注意的不同
VIM中的正则表达式和其他的有点不一样
(1) 有些符号要用\转义,比如\+表示重复一次或以上,其他的还有一些,:h pattern查看
(2) 非贪婪匹配用\{-},如.*\{-}匹配尽量短的任意字符
(3) \i匹配标识符字符[a-zA-Z0-9_],其大写形式表示不包括数字在内的标识符[a-zA-Z_],这两个不是互补的意思,类似的还有\k\f\p,但是\s匹配空白字符,\S匹配非空白字符,这两个是互补的.
tips:按/然后再按方向键的向上,可以到上次查的表达式,这样对测试正则表达式方便了不少
1. 查C语言的所有函数定义
试了无数次,终于写了一个查C语言中所有函数定义的正则表达式,写这个的目的其实是因为Notepad++的一个插件function list里面允许自定义正则表达式,然后可以把这个正则表达式匹配到的内容作为一个列表列出来,这个插件已经自带了C语言的例子,但是这个例子里面有个小错误,就是会把else if(...)这种格式的也认为是函数,我就想自己修改一下,让这个插件能排除这种情况,
因为同时我也在研究VIM,所以就直接在VIM里面测试正则表达式了.
/\s*\<\(return\|else\)\@!\w\+\s\+\w\+\s*([^)]*)\s*;\@!\s*$
解释一下,不然怕以后自己也看不懂了
/ 这个是向下查的命令
\s* 匹配0或多个空白(比如空格,Tab等,不匹配换行)
顺便说一下,VIM里面,如果要连换行一起匹配,则加个下划线,比如\_s匹配包括换行在内的空白,而\_.匹配包括换行在内的任意字符(注意,后面有个小数点)
\< 这个是个"零长度匹配",表示单词开头,这种"零长度匹配"只是指定匹配结果需要满足的条件,不匹配实际内容,类似的有很多,用:h /zero-width可以进入VIM的正则表达式帮助,然后用/zero-width查,再不停的按n,可以到所有"零长度匹配"的作用
\(和\) 其实就是划定一个范围,这个范围内的内容作为一个整体来看,后面可以跟\+表示这个整体重复1次或以上,另外这个整体还会保存在寄存器里面,根据出现的先后顺序,分别寸在1~9号寄存器,在同个正则表达式里面,就可以用\1到\9来指代前面的这个整体,这个用法相当的重要
\| 表示"或"的意思,也就是说,只要满足两边任意一个匹配都行,需要注意的是,在括号里面,是把左右两个部分作为整体,而不是只有一个字符,所以不用再加括号了
这里排除了return和else,暂时没有发现其他需要排除的,如果发现了,再添上去就好了
\@! 又是一个"零长度匹配",这个的要求是他前面的内容必须不存在,据说类似与Perl的(?!),因为Perl没学过,这个我也不太确定.在这个正则表达式里面,就是表示前面括号里面的return不允许出现,这个地方是我弄这个正则表达式费时最久的地方,一开始这个用法了半天,我一直在想,怎么能表示不包含某个单词,本来打算用\&,但是没实现,后来终于到这个东西,才实现了去掉对return的匹配,然后发现,居然匹配到了r
eturn的后5个字母,所以又在前面加了一个表示单词开头的\<,终于实现了这个功能,为啥我要跟这个retur n过不去呢?因为有些比如return aaa();(其中aaa是个函数名,也就是说,把aaa()这个函数的返回值作为本函数的返回值)这样的语句也被匹配上了,排除else的原因在本文一开始就说了.
\w\+ 匹配一个或一个以上的字母,数字,或下划线,其实就是C语法里面规定能作为函数名,变量名等的字符,相当于[0-9a-zA-Z_],因为大部分的编程语言也都是这么规定的,为了简单起见,就可以用\w来代替了,后面的\+表示一个或一个以上
\s\+ 匹配一个或一个以上的空白
( 这个是匹配左括号本身,因为前面没有斜杠,后面有两个不带斜杆的右括号也是同样的,表示本身[^)]* 匹配0个或0个以上的非右括号的内容,这个其实就是为了到右括号前面的所有内容
) 匹配右括号
到这里,函数就匹配完了,后面的部分是为了去掉带分号的内容,因为函数定义后面不可能有分号,有分号的要么就不是函数,比如return aaa();这种,要么就是函数的预先声明,所以都要排除
有了上面的解释,后面这段基本就没啥解释的了,只要注意最后一个$,这个匹配一行结束,也是个"零长度匹配",如果没有这个符号,那是不能实现去掉分号的功能,因为可以匹配到分号前面的一个字符
本来这篇文章的标题是VIM点滴的,不过写着写着,为了个正则表达式就写了这么长,干脆就独立成一篇文章吧.不得不感叹,正则表达式实在是太博大精深了,为了解释的稍微明白点,要花费相当多的笔墨,即使这样,还是不能保证所有的人都能看懂.
这个正则表达式还有没考虑充分的地方(主要原因的C语言实在太灵活了)
1. 没考虑注释,/**/这种方式的注释,可以出现在任何地方,还有在分号前后,可能也会有注释符号(//或者/**/)
2. 没考虑分行,函数定义是可以分多行来写的,这个也没考虑
3. 没考虑类,如果是在类外部,用::定义的函数,就不能匹配到了
2. 给指定的行添加递增的数字
正则表达式任意内容要求是将
<par type="I" flags="RO">
</par>
<par type="I" flags="RO,H">
</par>
变成
<par type="I" flags="RO" id="0">
</par>
<par type="I" flags="RO,H" id="1">
</par>
也就是每次到一行,在后面增加一个递增的数字.博主给了两种解决方法,我研究了好半天才大概理解
方法一:
分两步实现
第一步,增加id="":
:g/^<par type/s/>$/ id="">/g
第二步,增加数字:
:let i=0 | g/^<par type/s/id="\zs\ze">$/\=i/| let i=i+1
第一步解释
前面的g/没到是啥意思
08.08.19 这个是global命令,详见:h :g
^<par type/ 到这里是设定查和替换的范围,是每个以<par type开始的行
s/ 开始替换
>$ 这是被替换的内容,也就是一个在行末的右尖括号
/ 后面是要替换成的内容
id=""> 替换成这个内容,其实就是在尖括号前面加了一个空格和id=""这段内容
/g 在整个文件进行替换,还有其他选项, /i表示忽略大小写, /c表示每次替换要确认,如果需要用到两个或三个选项,只能有一个斜杆,如/gi或/gic
第二步解释
这是用|号(逻辑或的符号,不是字母)连接的三个语句,前后两个就是给i赋初值和递增,没啥好解释的,主要看中间一句
g/^<par type/s/ 对所有以<par type开头的行进行替换
id="\zs\ze">$ \zs和\ze是"零长度匹配",在这两个中间的才作为匹配内容,这个语句就是只匹配双引号
中间的内容,这样不会把其他有用的地方替换掉了
\=i \=是把后面的字符串当成表达式来对待,在这里就是i的值
方法二:
这是用一步解决的方法:
:let i=0 | g/^<par type/s/>$/\=substitute(" id = \"0\">", "0", i,"")/| let i = i+1
和上面的方法基本相同,就是替换右尖括号,不过这次是替换为substitute(" id = \"0\">", "0", i,"")
这是一个替换函数,就是在id=0中查第一个0,并替换为i的值,最后一个参数是{flag},一般为空.
08.08.19 我自己也写了一个,和上面的基本一样,就是不用substitute函数而已
:let i = 1 | g/^<par type=/s/>$/\=" id = " . i . ">"/ | let i += 1
其中\=表示后面是个表达式,小数点用以连接字符串的几个部分,中间用了i的值
3. 每行前面加上行号
:
g/^/exec "s/^/".strpart(line(".")." ", 0, 4)
:%s/^/\=strpart(line(".")." ", 0, 4)/g
上面一句是我看别人的博客里面写的,有些地方没理解,下面一句是我自己写的
先解释两个函数:
line()返回一个行数,特别的,line(".")返回当前光标所在行的行数,其他的参数见:h line()
strpart()相当于VB中的mid函数,具体见:h strpart(),需要注意的是,在这个表达式里面的第一个参数是line(".")." ",据测试,应该是相当于line(".")和" "连接起来,中间的小数点相当于字符串连接符
再看上面一句,g/^/exec这个是个命令,g/表示全局,^/是个正则表达式,exec表示执行后面的命令,总的意思就是,对所有满足条件(在这个语句的条件是行起始位置)的地方,执行后面的语句
s应该是替换的意思,但是前面的双引号不知道什么意思,我查了"s,表示的是后面的替换使用s寄存器,不过这个也解释不通.还有strpart前面的".,这个我也查了,表示上一次匹配到的内容,可是仍然解释不通.
(08.8.7 终于知道是怎么回事了,首先,上面已经说了g/^/exec是对所有行执行一个或多个命令,具体可以:
h exec来查看,每个命令必须用字符串,也就是要放在双引号里面,多个命令间用空格分割.在上面的例子中,后面只有一个命令,但是这个命令是用小数点连起来的一个字符串,这样就可以完全解释了,相关内容可以:h 41.5,里面还说到了exec这个命令的其他用法.另外,关于开头的g/,这个也是一个挺有用的用法,详见global的用法)
因为上面的我解释不了,而且感觉也没必要这么麻烦,所以我自己写了那下面的一句.同样实现了效果.就不知道还有其他的不同没有
我自己写的那句基本没啥特别的
%s 表示全文查替换
/^ 查内容为行起始
/\= 后面是一个表达式,这个在前面的第2点也介绍过
后面两个函数都解释过了
/g 所有到的匹配都进行替换
4. 指定查和替换的范围
:'a,'bg/fred/s/dick/joe/igc
'a和'b 指定范围(注意,前面是单引号,不是1左边那个).这个范围是用ma和m b指定的,可以用m{a-z}指定26个位置,以后用'{a-z}就可以直接跳到这个位置,其实就是书签的功能,查看所有的书签,可以用:marks
后面就不需要详细解释了
就是查所有包含fred的行,然后替换行里面的dick为joe,忽略大小写(i gnore),替换所有满足的位置(g lobal),每次替换前提示(c onfirm)
5. C语言中等号位置整理
如下的赋值
firstline=1 //comment line 1
secondline=2 /*comment block 1*/
thirdline = 3//comment line 2
forthline= 4/*comment block 2*/
fifthline =5
希望整理成
firstline = 1 //comment line 1
secondline = 2 /*comment block 1*/
thirdline = 3 //comment line 2
forthline = 4 /*comment block 2*/
fifthline = 5
下面是我写的语句,比较长,应该还有更好的写法,以后有改进再补充
:
g/=/s#\v(.*)\=\s*(((//|/\*)@!.)*)\ze(//|/\*)?#\=strpart(submatch(1) . " ",0,13) . "= " . strpart(submatch(2) . " ",0,10)# | s/\s*$//
前面紫部分,查所有包含等号的行,然后执行后面两个命令,第一个命令是橘黄的部分,第二个命令是蓝绿部分,第二个命令就是去掉行末的空格,这个不需要多解释,重点解释第一个命令
s#a#b#是一个替换的命令,这里用#做分隔符,是因为后面的表达式中有斜杆/,如果用斜杆作为分隔符,则表达式中的斜杆需要转义,稍显累赘
\v表示后面的正则表达式中,除了字母和数字和下划线和斜杆,其他的都作为特殊字符对待,有这个设置的好处,是后面可以省掉好几个用来转义的反斜杆,像\(\)\+这些都可以简写成()+
\= 匹配一个等号
\s* 匹配0个或0个以上的空白符
加粗的部分是一个比较重要的地方,一共三层括号,最外层括号使这个括号内部的内容成为一个子匹配,在后文中的sub match(2)就是指的这一部分,后面的一个*号,表示第二个括号内的内容可以匹配0次或0次以上.第二个括号内部,@!是个"零长度匹配",表示前面第三层括号内的内容不能出现,小数点匹配除了换行符外的任意字符,第三层括号里面,就是c 语言注释的两种形式,//和/*,中间用|连接,表示"
或"的关系.整个粗体部分的意思就是,匹配尽量长的,且不是C语言注释的内容.
\ze 匹配结束,后面的所有内容只是作为限制条件,在替换的时候,只会替换\ze之前的内容
后面的括号和粗体部分的第三层括号内容是一样的,后面的问号表示匹配0次或1次,因为不是所有的行都有注释的
后面就是替换成的内容了,strpart函数相当于VB中的mid函数,就是取字符串从某个位置开始的一段内容,这里用了一个小技巧(虽然写起来挺长的,但是想法简单),就是比如要把一个字符串处理成13个字符的长度,不足的位置补空格, 我们就先在这个字符串后面加上13个空格,然后截取整段内容的前13个字符,这样就满足要求了,还有其他的方法,比如用
print函数
另外一个函数submatch()表示的是前面用查的时候的子匹配,也就是在括号中的内容,submatch(0)对应这个匹配的内容,submatch(1)对应(.*)的内容,submatch(2)对应粗体部分的内容
剩下的需要注意的地方,就是这里的小数点相当于VB中的&,是作为字符串的连接符号.
这第一个命令的作用,就是到等号,将等号前面的部分,用空格补齐到13个字符,然后在等号后面空
两格,等号后面原有的空格无论多少都去掉,然后把后面的内容,到注释符号前面都整理成10个字符(不足补空格),最后才是注释
这样第一个命令执行后,对于没有注释的行,会多出来10个空格,不太好看,所有就加了第二条命令,去掉行末的空白
%s/([^)]*)//g
清掉掉()里的所有的内容,包括()
VIM中常用的替换模式总结。
0,:g/null/d
到null的行并且删掉
1,简单替换表达式
替换命令可以在全文中用一个单词替换另一个单词:
:%s/four/4/g
“%”范围前缀表示在所有行中执行替换。最后的“g”标记表示替换行中的所有匹配点。如果仅仅对当前行进行操作,那么只要去掉%即可
如果你有一个象“thirtyfour”这样的单词,上面的命令会出错。这种情况下,这个单词会被替换成”thirty4″。要解决这个问题,用“\<”来指定匹配单词开头:
:%s/\<four/4/g
显然,这样在处理“fourty”的时候还是会出错。用“\>”来解决这个问题:
:%s/\<four\>/4/g
如果你在编码,你可能只想替换注释中的“four”,而保留代码中的。由于这很难指定,可以在替换命令中加一个“c”标记,这样,Vim 会在每次替换前提示你:
:%s/\<four\>/4/gc
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论