`
iamxi
  • 浏览: 191785 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

大家來學VIM(一個歷久彌新的編輯器)[十]

 
阅读更多

規則表示式的運用

 

在本系列文章一開始就說明了學 vi(m) 可以順便學規則表示式(regular expression,以下簡稱 regexp),那為什麼到現在才來講呢?因為 regexp 說簡單也算不很難,但您要深入去使用的話,有時會馬上看不出一個複雜的 regexp 在說些什麼的,就曾有人形容 regexp 為「有字天書」!而且在 vi(m) 整體都還沒一個概念就加入 regexp 的話,那後面的單元恐怕就沒人看了!而 regexp 各家有各家的 extensions,這也是大家視為畏途的原因之一,不過總是大同小異,只需注意一下就可以了。目前先不必管別家怎麼說,就讓 vim 暫時先成為我們的「標準」,以後碰到其它程式的 regexp 應該就可以觸類旁通。以下我們盡量由實例去瞭解。當然,小小的一篇文章是沒有辦法詳盡介紹,只能撿重點來說明了。如有疑問,可 :h pattern 或在 Un*x 系統中可 man 7 regex,甚至 man ed,man sed,man grep,man awk,man perlre 裡面也是會說些 regexp,但要注意和 vim 差異的地方!其中 perl 的 regexp 應該是最完整的了,如果您的系統沒有 perl 那應該是「稀有動物」了!:-) ㄟㄟㄟ!vim 只是一個編輯器,可不是獨立的程式語言!

 

 

基本的匹配

*  指前所綁住的字元或字元集合,出現 0 次或 0 次以上。
\+ 和 * 作用相同,但不包括出現 0 次。
\= 指前所綁住的字元恰好出現 0 或 1 次。
\| 這是多選,就是 or 的意思,被 \| 隔開的 pattern,任一個符
   合的話就算符合。


  • \+, \=, \| 會加上一個 \,是因原字元在 vi(m) 就具有特殊意義,在一般的 regexp 中是 +,?,| 就可以了,只是提醒您一下,以免搞混了!
  • 記住 * \+ 是不可數的!用辭不是是精確,只是幫助您記憶啦!
  • 在 elvis 及 ed 中是使用 \? 來匹配出現 0 或 1 次,而不是 \=,這裡要非常小心!
[實例] dg*
指 * 前所綁住的字元 g 出現 0 次或 0 次以上。也就是說 d(出現 0 次),dg,dgggg,dgggggggg 都是符合這個 pattern。如果您下尋找指令 /dg*,那符合這個 pattern 的字串都會被找出來。如果用在代換就要非常小心了,像 extended 中的 d 也是會被置換掉的。例如您下 :%s/dg*/test/g 的話,那 extended 這個字會換成 extentestetest。
  • shell 中使用的通用字元為 pattern matching notation 和 regexp 不同的。dg* 在 shell 中是解為以 dg 開頭的任意字串,這就不包括 d 在內了,也就是說在 shell 中,* 是代表任一字元或字串。
[實例] dg\+
dg, dgg, dgggggg 皆符合,但 d 則不符合。如果是 dg\= 的話,就只有 d、dg 這兩個符合了。


[實例] :%s/The\|All/test/g
全文中只要是 The 或 All 都會被替換成 test。注意,如果文中有 There 也是會被替換成 testre!要如何避免這種情形呢?下面會另述及限定使用法。


[實例] /123-\=4567
這樣會找出,123-4567 及 1234567。當然 123-456789 也是會被找出來。
[...]  字元集合,表示中括號中所有字元中的其中一個。
[^..]  這是上述 [...] 的補集,表非中括號內字元的其中一個。
.      除換行字元外的任一單一字元。指本身,非指前所綁之字元。
       就好像 shell 中的 ? 一樣。如果要指定真正的英文句點,要
       用 \ 來 escape,就是說 \. 這時的 . 是代表真正句點,而不
       是 regexp 中的特殊意義。其他如 \* 亦同。


[實例]
[Aa]
A 或 a 其中的一個。
[12345]
12345 其中的一個數目字。可用 [1-5] 來表示。連續性的數目字或字元可用 - 來隔開,寫出頭尾來代表就可以了。[0-9] 就表 0 到 9 的數目字,[a-d] 就代表 abcd 四個英文字母


[實例] W[0-9]*\.cc
這個例子是說以 W 開頭,後接 0-9 其中一個或多個數目字或不接什麼,然後是一個句點,最後是 cc。所以 W.cc,W1.cc,W2.cc,W345.cc,W8976543287.cc 皆符合。如果要表示 W 及 .cc 間夾一個以上的數目字,要寫成 W[0-9][0-9]*\.cc。


[實例] .*
這代表任意字元或字串,或什麼都沒有,腦筋急轉彎,對照前面的定義想一下。當然這是不包括換行字元的。


[實例]
[^M] 表除 M 以外的任意字元。
[^Tt] 表 T 及 t 以外的任意字元。
[^0-9] 表非數目字之字元。
[^a-zA-Z] 表非英文字母之字元。
  • 注意,^ 要在中括號內,且在最開頭的地方,否則另有含意。
^  匹配行首,指其後綁住的字串,出現在行首才符合。
$  匹配行尾,指其前綁住的字串,出現在行尾才符合。含換行字元。


  • 不是在行首的 ^ 指的是 ^ 這個字元。不是在行尾的 $ 是指 $ 本身這個字元。
[實例] /^What
這樣只有在行首的 What 才會被找出來。注意! Whatever, What's 也是會被找出來。如果是 /What$ 則是在行尾的 What 才會被找出來。


[實例] ^$
這是什麼東東?行首也是行尾的行。ㄚ,就是空白行嘛!當然也不能說這個行是沒有什麼東東啦!空白行至少也是會有個換行字元。在後面會詳述如何消除全文的空白行。
\(...\)  記憶 pattern,可由 \1, \2...\9 來叫出。


[實例] :%s/\([a-z]\)\1/test/g
這樣 aa, bb, cc, dd, ..., zz 都會被 test 替換掉。這和 :%s/[a-z][a-z]/test/g 是不一樣的意思,後者會把 aa, ab, ac... ba, bb, bc...zz 都換成 test。也就是說 \(...\) 由 \1 叫出時會有對稱性的配對出現。


[實例] :%s/\(.\)\(.\)r\2\1/test/g
會將中間為 r,前有二個任一字元,後有兩個具對稱性的字元所組成的字串替換成 test。\2 是呼叫第二組 \(.\),而 \1 是呼叫第一組 \(.\)。例如:12r21,cfrfc,7grg7 等都會被替換成 test。
\<  匹配字(word)首。所謂 word 包括文數字及底線。
\>  匹配字尾。這就是前所提及的限定用法,被 \<,或 \> 括住的
    pattern 就會被限制住,使 regexp 不能再向右(左)擴充解釋。


  • ed 及 perl 中可以 \b 來表示這兩個符號,perl 中只支援 \b,ed 則 \b 及 \<, \>皆支援。但在 perl 可多加個 ? 來限制 regexp 的擴充解譯。
  • 功能上而言,這是和 ^ $ 一樣的定位樣式(anchor pattern)指所綁住的字串必須是單字邊界(word boundary),前或後或前後除了空白字元及標點符號外不可再有其它字元。
  • 在 vim 中 \b 是表示 <BS> 即 backspace 鍵。
[實例] :%s/\<abbbc\>/test/g
這樣只有 abbbc 才會被替換成 test。如果沒有這樣限定,:%s/abbbc/test/g,那 deabbbcly 中的 "abbbc" 亦會被替換成 test。所以前面 :%s/The\|All/test/g 可換成 :%s/\<The\>\|\<All\>/test/g 這樣一來,There 就不會被替換成 testre 了!


[實例] :%s/\<abbbc/test/g
這樣的話,只要是以 abbbc 為首的字(word),其中的 abbbc 的部份都會被 test 所替換。注意!是指字首,而不是指行首。所以 abbbc,abbbcerd,abbbckijuds 都符合。
\{n,m}  指前所綁住的字元或字元集合最少出現 n 次,最多出現 m 次。


  • 這在一般的 regexp 表示成 \{n,m\}。vim 及 elvis 兩種表示法皆支援。perl 則直接使用 {}。以下會舉四種不同的例子,請大家發揮一下想像力。:-)
[實例] \{最小值,最大值}
如 [0-9]\{3,4} 匹配至少三位數,但不可多於四位數的數目字。如:
  123
  12
  1
  123456
  1234567
  12345678
  1234
  12345


如果下 :%s/[0-9]\{3,4}/test/g 的話,那 1,12 這兩組不會被替換,因為不滿 3 位數。而 12345,則會換成 test5。123456,則會換成 test56。12345678,則會換成 testtest。1234567 也是會換成 testtest。123,1234 這兩組則會被替換成 test。您可以親自操作一次就知道怎麼一回事了。操作時最後加 gc 來 confirm,這樣您會更瞭解實際替換的內容。ㄟ,別忘了 u 可以回複您的編輯動作。


[實例] \{數目字}
xy\{20} 表示 x 後接 20 個 y。
e[x-z]\{4} 表示 e 後接有四個字元,是 x,y,z 的其中一個的
      組合。如:exxxx, exyyz, ezzyz, exyzz 皆符合。


[實例] \{最小值,}
xy\{2,} 表 x 後接至少二個的 y。相當於 xyyy* 或 xyy\+ 。


[實例] \{,最大值}
xy\{,4} 表 x 後接至多四個或更少的 y (可能沒有)。
     因此 x, xy, xyy ,xyyy, xyyyy 皆符合。


 

中介字元(metacharacter, or character classes)

主要是簡化 regexp 的書寫。

\s  表空白字元,即 <Space> 或 <Tab>。


  • 不含換行字元,這是編輯器的特性使然。在 perl 的 \s 是包含換行字元的。而且 vim 及 elvis 皆不支援 \n 這種換行中介字元。
\S  表非空白字元。
\d  表數目字(digits),即 [0-9]。
\D  表非數目字,即 [^0-9]。
\w  表一般字元(word character),包括底線。即 [0-9a-zA-Z_]。
\W  表非一般字元,即 [^0-9a-zA-Z_]。
\a  表英文字母(alphabetic character),即 [a-zA-Z]。
\A  表非英文字母,即 [^a-zA-Z]。
\l  表小寫字母(lowercase character),即 [a-z]。
\L  表非小寫字母,即 [^a-z]。
\u  表大寫字母(uppercase),即 [A-Z]。
\U  表非大寫字母,即 [^A-Z]。


  • 原始 vi 不支援此種中介字元。
  • 使用中介字元的比對速度將會比使用字元集合 [] 的快。


 

全域性的指令

:[range]g/pattern/[cmd]


cmd 是 ed 可用的指令,預設是 p(print),您可查一下 man ed,就可以知道有什麼指令可用。這個小節裡主要是說明 d(delete) 的功能。因為是要說明如何消除空白行。需注意的是,d 是行刪除指令,凡含 pattern 的整行都會被刪掉,而且 range 不指定的話,預設是全篇文章,因為 g 就是代表 globe。

  • 在 vim 的 help 檔裡說的是 ex 指令,但 ex 實際上是和 vim 連結的,因此這裡特別指出 ed。但 ed 的指令少數可能會和 vim 的 ex 不同,這是因為 ed 和 vim 並非同步在發展,作者也非同一人。
:g/^$/d


這樣就會刪除全文的空白行。前面已提過 ^$ 代表的是空白行。但這裡有個問題,如果空白行裡包含了其它空白字元(即 Space 或 Tab)的話。表面看起來是和一般空白行一模一樣,但卻暗藏玄機,用上面的方法就無法刪除這種空白行了!怎麼辦?來!看招!

:g/^[<Space><Tab>]*$/d


在 vim 或 elvis 裡您可以如此照打,也就是 <Space> 代表空白字元,<Tab> 代表按 Tab 鍵的結果。在原始 vi 則不行,得自行按出特殊字元出來,就是 Ctrl-v Space 及 Ctrl-v Tab。或採更簡單的打法:

:g/^\s*$/d


還記得中介中元嗎?好用吧!少打了不少字。:-) 意思就是刪除含 0 或 1 個以上空白字元的行。

有些書中寫成 :%s/^$//g 可以刪除空白行,這是錯誤的,因為 :s 這個指令只更動一行裡的內容物,但不會做刪除一行的動作。


 

&替代變數

代表置換時合於 patern 的字元或字串。


[實例] :%s/\u\d\d\d\d\d\d\d\d\d\>/ID:&/g
這樣全文中的身份證字號前就會加上 ID: 字樣,也就是說 T123456789 會被換成 ID:T123456789。還記得嗎? \d 就是 [0-9],\u 代表大寫的英文字母。加個 \> 是防止 T12345678999 也被換掉。當然前面再加個 \< 更保險。ID: 字樣您用中文也行!
另一個好用的例子是電話號碼前加上 TeL:,就請您自行練習了!


[實例] 將檔案 3 至 7 行的資料向右移 2 個空白
  :3,7s/.*/  &/


但這樣連空白行也是會插入空白字元,較高明的做法是:
  :3,7s/.\+/  &/


這樣空白行就不會去動它了!想通了 .* 及 .\+ 的意思了嗎?往前翻一下 . * \+ 的定義。


[實例] 將檔案 3 至 7 行的資料向左移 2 個空白
  :3,7s/^  //


就是刪去行首的二個空白啦!


[實例] 將全文的 Edward 這個單字,前後加上中括號
  :%s/\<Edward\>/[&]/g



[實例] 將全文的 Edward 這個單字,改成大寫的。
  :%s/\<Edward\>/\U&/g


  • ㄟ!\U 不是代表非大寫字母嗎?喔!您搞錯位置了。\U 在 pattern 的位置的時候是指非大寫字母的樣式,即 [^A-Z],但如果是在置換字串位置的時候是指將其後的字串通通改成大寫。與其相對的是 \L,會將其後的字串改為小寫。詳細請 :h sub-replace-special。


[實例] 將全文每行最後加上 <BR> 這個 HTML tag。
  :%s/.*/&<BR>/g


怎麼樣,是否已感覺到 regexp 威力無窮了呢?還是您已經快睡著了呢?:-) 不過也請您想想,如果是在沒有 regexp 功能的編輯器裡,範例中的一些動作您會怎麼做呢?一個一個去改?


 

greedy 陷阱

regexp 會有貪心的傾向,什麼意思呢?就是說在同一行內,如果有多個符合 pattern 的情形,會找最長的那一個。

  • 注意!greedy 的特性是針對會反覆比對的 regexp 而言,例如:*, \=, \+, \{} 等。前面所舉的 .* 的例子,由於 greedy 的關係,在整篇文章中做替換時,會被當成是每一行整行,因為 regexp 會去找每一行最長符合的那一個。


[實例] This is a test. Test for regexp.
如果您下 :%s/[Tt].*t/program/g 原意是想把所有的 Test 或 test 換成 program 的,結果由於 regexp 的貪心,整個 "This is a test. Test" 會換成 program。結果原文就變成了 program for regexp. 因此在全文替換時要非常小心,避免使用彈性太大的 regexp。像此例,只要下 :%s/\<[Tt]est\>/program/g 就可以了!


最後提醒您,這可不是 regexp 的全部,礙於篇幅及在下功力的問題,當然是沒辦法全面詳盡的向各位做介紹,在下只是將各位領進門,修行就得看各位了!如果還想更深入的研究 regexp,可參考: Mastering Regular Expressions(O'Reilly & Associates) 一書。

分享到:
评论

相关推荐

    vim帮助文档-大家来学VIM(一个历久弥新的编辑器)

    大家来学VIM(一个历久弥新的编辑器)[一] 前言 监於仍有许多人还没找到顺手的编辑器,而许多想学 vi 的人又觉得无从下手,因此在此提出一些个人的心得,希望能对这些朋友有点帮助。或许也减少一点 FAQ 吧! ...

    大家来学VIM

    - **多文件编辑**:使用`:next`切换到下一个文件,使用`:prev`切换到上一个文件。 - **加密文件**:使用`:X`命令对文件进行加密。 - **紧急恢复**:如果编辑过程中发生意外退出,VIM会自动创建备份文件,使用`:e #`...

    《大家来学vim》-李果正

    Vim,全称Vi Improved,是从早期的Vi编辑器发展而来的一个高度可定制且功能丰富的文本编辑器,广泛应用于Linux、Unix和类Unix系统中,同时也支持Windows平台。 1. Vim的基本操作: - 启动与退出:学习如何启动Vim...

    学习vi和vim编辑器_第7版_中文+英文_PDF

    《学习vi和Vim编辑器(中文版第7版)》将说明使用这两种程序编辑文档的基础技巧,并讨论高级工具,例如交互式宏与扩展编辑器的脚本——我们的内容编写成容易遵循步骤操作的风格,成就本书的经典地位。读者将学到: ·...

    大家来学vim(简体-05).pdf

    大家来学vim(简体-05).pdf 鉴于仍有许多人还没找到顺手的编辑器,而许多想学 vi 的人又觉得无从下手,因此在此提出一 些个人的心得,希望能对这些朋友有点帮助。或许也减少一点 FAQ 吧! 真要深入的话,大多数的前辈...

    大家来学 vim pdf 中文

    "大家来学 Vim pdf 中文" 是一个专为中文用户设计的教程,旨在帮助初学者快速掌握Vim的基本操作和高级技巧。 1. **基础操作** - 启动与退出:了解如何启动Vim编辑器,以及在编辑模式和命令模式之间切换。例如,按...

    学习vi和vim编辑器.pdf_vim实用_vi_vim_

    1. **打开文件**:`vim 文件名` 命令用于打开一个文件进行编辑。 2. **移动光标**:`h`左移,`j`下移,`k`上移,`l`右移;数字与移动键组合可快速移动,如`4j`表示向下移动4行。 3. **删除文本**:`x`删除当前字符,...

    学习VI和VIM编辑器第7版中文

    **VI和VIM编辑器详解** VI(Visual Editor)是一款强大的文本编辑器,最初在1976年由Bill Joy为UNIX系统开发。随着时间的发展,VI的增强版本VIM(Vi Improved)应运而生,由 Bram Moolenaar 开发,并在1991年首次...

    基于C/C++语言的简易vim编辑器 仿vim编辑器

    # 基于C/C++语言的简易vim编辑器 仿vim编辑器 1. 编辑器下有normal和insert两种模式。默认为normal模式,按i键进入insert模式,按esc退出insert模式。 2. normal模式下按 h j k l键向左上下右移动光标。 3. normal...

    学习vim 编辑器 第7版

    "学习vim编辑器 第7版"显然是针对这个版本的Vim进行深入学习的资源,对于想要提升文本编辑效率的人来说,是非常有价值的。 在Vim中,用户可以快速地在命令模式、插入模式和可视模式之间切换,以执行不同的操作。...

    学习VI和VIM编辑器第7版中文

    将近三十年的时间里,vi一直是Unix与Linux采用的标准编辑器,从1986年开始,《学习vi和Vim编辑器》一直是第一线的vi导引手册。但三十年来,Unix已不再是三十年前的样子,这本书也不能一成不变。第7版的《学习vi和Vim...

    学习vi和Vim编辑器_CN.pdf

    - **利用Fugitive.vim进行Git集成**:Fugitive.vim是一个Vim插件,提供了丰富的Git命令集成,如提交、查看历史、分支管理等。 #### 结语 学习vi和Vim编辑器是一项非常有价值的投资,尤其是在从事软件开发工作时。...

    一个仿vim的文本编辑器

    【标题】:“一个仿vim的文本编辑器” 在IT领域,文本编辑器是开发者们日常工作中不可或缺的工具,它们用于创建、查看和修改各种类型的文本文件。Vim(Vi Improved)是一个高度可定制且功能强大的命令行文本编辑器...

    vim编辑器的rpm包

    在本案例中,"vim编辑器的rpm包"指的是一个打包为RPM格式的Vim软件,方便在支持RPM的Linux系统上进行安装。 RPM包的工作原理是将软件及其依赖关系打包成单一文件,以便于分发和管理。当用户运行`rpm`命令来安装这个...

    vim编辑器用户手册

    Vim编辑器的定制是一个很大的话题,用户可以学习如何通过vimrc文件来配置Vim,包括简单映射的设置、插件的安装、帮助文件的增加、选项设置等。此外,用户手册中还包括使用语法高亮的方法,这对于编程工作尤其重要。 ...

    笨方法学 Vim Script

    例如,你可以定义一个函数来自动缩进代码,然后通过快捷键轻松调用。 Vim Script 中的命令是其强大之处。它们允许你控制文本的操作,比如移动光标、删除文本、复制和粘贴等。例如,`:global` 命令可以用于在整个...

Global site tag (gtag.js) - Google Analytics