VIM正则表达式再研究

使用正则表达式已经有好几年了,本不想细读VIM的pattern.txt,不过发现好些东西不明白,回过头来边看pattern.txt,边总结一下。

1、vim的正则表达式的构成:若干atom组成piece,若干piece组成concat,若干concat组成branch,若干branch组成pattern

1)atom是基础单位,比如单个字符(如a)就是一个atom,字符集(如\w[a-z])也是一个atom,甚至用括号把一个pattern变成atom,如\(\w\+\)

2)piece一般由atom加上重复次数组成,如a\+,匹配至少一个a

3)concat由多个piece拼接而成,如a*b\+[cd]*。匹配顺序从左到右,先匹配a*,匹配成功后再匹配b\+,最后匹配[cd]*

4)branch由多个concat组成,concat间用\&分隔。这个分隔符很有意思,必须前面的concat匹配上了,才会匹配下一个concat。而且下个匹配也是从上一个匹配到的地方开始匹配(这也有zero width的味道,下一节会说明)。匹配的结果以最后一个concat匹配到的内容为准。例如:

      foobar\&...当匹配上了foobar后,才会匹配\&后面的...,这也是从foobar匹配到的地方开始匹配,并且匹配结果是...对应的字符串,即foo

5)pattern由多个branch组成,branch间用\|分隔。\|代表或的关系,如果有多个branch都匹配上了,优先用第一个匹配上的。

      例如,/foo$\|foo[^b]匹配以foo结束的行或者foo后不是b的行。


2、零宽度匹配:\@=\@!\@<=\@<!\zs\ze\@>

       \@=表示以零宽度(zero width)匹配\@=前的atomatom上一节已经介绍过了,那么零宽度是什么意思呢?零宽度是指本次匹配不改变搜索本文的偏移位置,下次匹配还从这次匹配的位置开始匹配。还不明白?看个例子:

       foo\(bar\)\@=..首先匹配foo,然后匹配bar,再匹配..的时候是从bar的位置开始匹配的,所以..的匹配结果是ba,整个pattern的匹配结果是fooba。所以这个pattern匹配的是foobar中的fooba

      \@=的用法跟\&非常相似。上面这个例子也可以用\&来匹配:foobar\&.\{5}


      \@!就是\@=的反面,即\@!表示以零宽度不匹配前面的atom。例如有如下文本:

foobar
foo
footest
bar
barfoo
bartest
test for sth.

      foo\(bar\)@!...表示匹配foo后面跟了三个不是bar的字符串,也就是匹配footest中的footes,其它都匹配不上。

      \@=\@!有个限制,在它们前面必须先有其它atom匹配上了。比如用\(foo\)\@=bar匹配上面的文本,什么都匹配不了。 


      于是,bram为我们带来了\@<=\@<!。这两个可以放到pattern的第一个atom的后面。例如:

       \(foo\)\@<=bar匹配foobar中的bar

       \(foo\)\@<!bar匹配barbarfoobartest

       这两个符号有个奇怪的用法,可以把\1放到最前面:

       \1\@<=,\([a-z]\+\)匹配foo,foo中的,foo

       这从一个侧面验证了我的猜想:\@<=\@<!并不是最开始匹配的,而是等到后面的piece匹配上了,再回过头要看\@<=\@<!对应的piece能否匹配。

       于是第二个问题就出现了:反向搜索要搜索要哪里才算完?如果一直要搜索到文本的第一个字符,搜索效率肯定会下降很大。有矛盾,就有妥协。默认反向搜索是从\@<=\@<!后面的piece匹配所在行开始,到上一行结束。当然,这也是可以定制的,比如<\@1<span当匹配到span后,只会向前检查一个字节,看看是不是<。好像是从7.4才开始支持指定反向搜索的字节数。

        

       鉴于效率问题,bram又给了一个替代方案:\zs\ze\zs表示匹配的开始start\ze表示匹配的结束end

       foo\(bar\)\@=的替代方案是foo\zebar,都是匹配foobar中的foo

       \(foo\)\@<=bar的替代方案是foo\zsbar,都是匹配foobar中的bar


       \@>则走的完全不同的路线,它表示更贪婪。什么意思?请看下文分解:

       a*ba*ab都能匹配上aaab,但是两个a*匹配的内容不同,前者匹配的是aaa,后者匹配的是aa*的贪婪是有节制的,是顾全大局的。

       但是\(a*\)\@>ab匹配不了aaab。为什么?因为\@>的贪婪是没有节制的,是不顾全大局的。\(a*\)\@>会匹配aaa,把aba抢了过去,所以ab就匹配不上了。


3、指定搜索的范围

1)按标记(mark)指定范围

       \%'abar从标记a处匹配bar,指定的位置不是范围,必须在标记a处就能匹配上。例如在foobar中的第二个o处执行ma,将此处标记为a。前面的pattern匹配不了。如果在foobar中的b处标记为a,这个pattern就能匹配上。

       如果要指定范围,可以用\%<'a\%>'a。如上将foobar的第二个o标记为a,要匹配bar,可以用\%>'abar,指定的搜索范围是从标记a处开始到文本结束。如果要匹配标记a以前的bar,可以用\%<'abar。要匹配标记ab之间的bar,可以用\%>'abar\%<'b

2)按行指定范围

      也有三种:

       在某一行内搜索:\%5lbar,即在第五行内匹配bar。

       在某行前搜索:\%<5lbar,即从文本开始到第四行内(包括第四行)匹配bar

       在某行后搜索:\%>5lbar,即从第六行到文本结束内(包括第六行)匹配bar

       在某两行之间搜索:\%>3lbar\%<6l,即在第四行和第五行内(包括第四行和第五行)匹配bar

3)按列指定范围

       同样也有三种:

       在某一列内搜索:\%3cr,即在第3列匹配r。虽说是按列搜索,其实是按字节数搜索,前面的例子其实是在每行的第三个字符处搜索。这就有一个问题,每个字符是不是等宽的呢?字母和数字都是等宽的,但是不要忘了还有\t这样的异类。如果有些行有\t,有些行没有\t,列内搜索的结果就有点怪异了。所以按列搜索适用在格式表格内搜索。还有一点要注意的,如果搜索超过一个字符会怎么样呢?比如\%4cbar,可以匹配foobar中的bar。也就是说,如果搜索超过一个字符,它会自动向后搜索。

       在某列前搜索:\%<3cbar,即从第一列和第二列搜索bar。大家一定会问:在两列中怎么能搜索3个字符呢?就像\%4cbar一样,这里也会自动延长匹配范围,它能匹配barfoo中的bar。但是延长是有条件的,比如用\%<3crf匹配第二节中的7行文本,结果什么都匹配不了。为什么?因为在第一列和第二列中没有r。所以延长范围的条件是,必须在指定的列范围里匹配上pattern的前一部分。

        在某列后搜索:\%>3ctest,即从第四列开始搜索test(包括第四列)。这里只需要注意一点,即不包括第三列。

        在某两列间搜索:用\%>3cba\%<7c搜索第二节的文本,即从第四列和第六列之间(包括这两列)匹配到ba。很可怪的是,\%>3cba\%<6c,什么都匹配不了。也就是说,如果要在N列间搜索长度为M字符串S,列数和字符串长度必须满足以下条件:N>=M+1

        除了在真实列搜索后,还可以在虚拟列(virtual column)上搜索。把上面的c改成v,就可以在虚拟列上搜索。虚拟列的用法可以参考《vim用户手册》25.5节。

4、匹配后的替换动作

      :%s/pattern/stringstring除了是字符串外,还可以是函数。如%s/ba/\="\r#".submatch(0)."#\r",可以把foobar变成:

foo
#ba#
r

5、括号与submatch

定义如下函数,将submatch的结果写到match.txt中。

function! RecordSubmatch()
	redir! >> match.txt
	echo "submatch(0):" submatch(0)
	echo "submatch(1):" submatch(1)
	echo "submatch(2):" submatch(2)
	echo "submatch(3):" submatch(3)
	redir END
	return submatch(0)
endfunction
文本如下:

fooxxxbarbar

执行如下命令

%s/\(\(foo\)[^bar]\+\|\(bar\)\+\)/\=RecordSubmatch()/

       |<---------------------------->|   第一个括号

          |<--->|                                  第二个括号

                                   |<--->|         第三个括号
match.txt内容如下:

submatch(0): fooxxx
submatch(1): fooxxx
submatch(2): foo
submatch(3): 
submatch(0): barbar
submatch(1): barbar
submatch(2): 
submatch(3): bar

共匹配了两次,submatch(0)是每次匹配到的全部字符串,submatch(1)是第一个括号内匹配的字符串,submatch(2)是第二个括号内匹配的字符串,submatch(3)是第三个括号内匹配的字符串。

submatch(0)就是\0submatch(1)就是\1submatch(2)就是\2。。。





未完待续。。。

4、指定搜索行的范围

5、指定搜索mark的范围

6、指定搜索列的范围

7、指定匹配的开始和结束位置

8、贪婪匹配和非贪婪匹配

9、匹配后调用函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值