Saturday, April 14, 2012

使用VIM搜索小技巧:不含有模式的实例

现代的技术给我们的生活提供了许多方便,自从苹果和微软推出窗口用户使用界面以来,在电脑上进行文字编辑更为直观,你所看到的就是你得到的(WYSIWYG: What You See Is What You Get)成为窗口上文字编辑软件的潮流,这股潮流席卷冲垮了过去在非窗口上使用的文字编辑软件,其中有不少是十分优秀和深思熟虑的软件,VI或VIM就是其中之一。我曾和朋友聊天谈到这一话题,我的朋友告诉我这么一个故事,他在英国读博士写论文的时候,当时要求所有的文章必须是通过VI编辑,这是强制的要求,尤其上论文,不能使用微软的Word来写。

如果我们认真思考一下,虽然窗口的WYSIWYG给我们带来了直观的便利,但是,这也使得我们的大脑在无意识之中迟钝了,因为我们不会考虑到使用记忆、逻辑来进行我们的工作。VIM所具有非常强的文字编辑功能和高效率是建立在一个体系和各种运作指令基础之上,因此使用起来就必须要学习和熟悉这套系统。在学习的过程中,我们的大脑需要不断重复地练习从而增强了记忆,同时这套系统也要求我们按照其规范的逻辑的方式来处理和解决我们实际编辑过程中的问题。在这个意义上,VIM是一个可以很好锻炼我们大脑的一个出色文字编辑软件。

对于许多对VIM还不了解的人来说,刚刚开始使用会觉得不知所措。但是如果你要是作出你的努力,一步一步学起,坚持下来,我相信你会非常喜欢这个工具的。实际上我最初非常不喜欢这个软件,对VIM是不屑一顾。但是数位对我非常有影响的编程技术高手曾竭力推荐VIM,我想这些高手都如此痴迷,想必必有其道理。于是我开始学习了VIM,我现在对VIM了解非常之少,只是入门水平,但是我已经尝到这个工具的甜头,它曾给我的工作带来极大的便利,我认为花点功夫认真学习是十分值得的。

下面让我接着介绍如果使用不含有模式的实例。

两个简单的实例


所谓不含有模式是指搜索内容中要求有不含有某些内容。让我们首先用两个最简单的例子来理解不含有模式的搜索。

这里我用上文的pseudo码文字作为搜索的内容,第一个例子是搜索所有的s字符,但这个字符之后必须是t字符。例如string中的s就满足这个条件,而result中的s就不满足我们搜索的条件。

我们搜索的结果是所有满足这个整个条件的st就是一之后的zero width的条件:在s之后必须是字符的t,而t不作为搜索的结果。这就是所谓的look ahead zero width搜索。这个搜索的指令是:

/st\@=

在VIM中,指令是在剪辑窗口的下端输入。指令的第一字符/表示是搜索,之后是搜索的内容,而\@=是表述一种look ahead 作为 zero width搜索。因此上面指令的意思是:s是搜索的结果,在s\@=之间的t是zero width附加条件。或者用通俗的表述:搜索所有的s,之后的字符必须是t 。下面是搜索的结果(红色标出):



这里再详细分析其搜索的逻辑过程:这一指令告诉VIM搜索所有的s,当一个s找到之后,搜索记下这个位置,\@=告诉搜索引擎从这个位置之后的文字做为zero width搜索的内容,如果发现有t,整个搜索条件满足,这个s放在结果之中,接着从这个位置继续下面的搜索,直到全部文字的搜索完毕。

第二个例子是非常类似的搜索,但是采用反向模式:look behind。指令是:

/s\@<=t

这里\@<=是表述look behind 作为 zero width搜索:搜索的目标或结果是t,之前的s是zero width附加条件搜索,即:搜索所有的t,其前面的字符必须是s。下面是其结果:



比较复杂的搜索案例


下面是一段文字,其中有一些类似程序的foo...bar块:


foo
  test baz
  something for you
  gave me your beer
bar

foo
  test ba
  something for you
  gave me your beer
bar

foo
  test bae
  foo
  something for you
    gave me your beer
  bar
bar

这次我要进行搜索的目标是foo...bar块,但是块中含有baz。搜索指令是:


/foo\(\_.\{-}baz\)\@=\_.\{-}bar

VIM中有许多特殊的表述,上面的指令中就使用了数个。你也许注意到在foo...bar之间有多行文字,VIM中用\_.表述搜索多行文字;\{-}是一种非常有用的表述,表述之前的内容可以有零到多次重复,但越少就足以(matches 0 or more of the preceding atom, as few as possible)。上面的指令可以表述为:

foo作为开始,下一个是zero width搜索:零或多行文字,直到baz为止;之后为零或多行,直到遇到bar为止。

满足上面的条件将作为match的结果,下面红色为第一块,满足全部条件:



上面的指令可以理解为两部分搜索的组合,其中第一部分是:

/foo\(\_.\{-}baz\)\@=



如果搜索foo...bar块中不含有baz,采用这个指令:

/foo\(\_.\{-}baz\)\@!\_.\{-}bar



如果搜索多层中最里面的foo...bar块,搜索指令:

/foo\(\_.\{-}foo\_.\{-}bar\)\@!\_.\{-}bar



下面是一些更为实用的例子,我写博文时,经常在拷贝一些文字,这些文字都不是HTML格式,因此我需要在那些有文字的行结尾加上<br/>,而没有文字的行则不处理。下面是我所用的指令,举手之劳,片秒即成:

:%s:.\@<=$:<br/><br/>:g

注:这里:%s是表示替换,替换的指令是:%s:xxx:yyy:g,搜索的xxx结果,替换为yyyg是表示全部替换。



最后一个实用的例子是处理多个空格,在HTML中,多于一个以上的空格只是作为一个空格来显示,如果需要在HTML中显示多个空格,这时可以采用特殊的字符&nbsp;来表示一个空格。下面是搜索一个以上连续空格的指令:

/\(\s\)\@<=\(\s\)\+

注:\(xxx\)表示一个单元组,组里可以有多个字符表示;\s表示一个空格;\+表示之前的内容有一个以上的重复。



确认结果之后,可以采用下面的指令进行一个以上的空格替换,将空格替换为HTML的空格特殊字符&nbsp;:

:%s:\s\@<=\s:\&nbsp;:g



No comments:

Post a Comment