| ||
前面的章节谈论 EMACS 的基本用法,现在讨论 EMACS 进阶 的用法。现在先谈 EMACS 的搜寻 search 与字串 string 的取代功能。
文件的搜寻
搜寻特定的字串,并非 EMACS 所特有的功能,相信大多数的 编辑器都具有如此的功能。但 EMACS 所采取的搜寻方法是, 每键入一个字元就展开搜寻,EMACS 称此种方式的搜寻为 Incremental Search 。 当然, EMACS 也提供非 Incremental Search} ,称为 Nonincremental Search。 EMACS 对於所要搜寻的字串,仍是利用 minibuffer 来输入所欲搜寻的字串。 此时,输入 minibuffer 的搜寻字串,若全由小写的英文字母 (lower case)组成,则 EMACS 在展开搜寻的行动时,不论字母 是否有大小写的差别,会将所有与 minibuffer 具有相同 英文字母的字串都找寻出来。例如在 minibuffer 处输入 abc, EMACS 会找寻abc、 Abc、aBc、 abC、ABc、aBC、ABC 等字串。 所以在 minibuffer 处输入小写的英文字母,就表示所要找寻 的字串包括大写的字母在内。 如何直接找寻上述例子的 ABC,而不需经过 abc、Abc、aBc、 abC、 ABc、ABC 等字串呢?欲达如此的效果, 必需在 minibuffer 处,给予大写字母(upper case)的字串。 例如,在 minibuffer 处,给予 ABC 的字串,此时,缓冲区 的内容若为abc、 Abc、aBc、 abC、ABc、aBC、ABC ,则会 直接搜寻 ABC 。所以在 minibuffer 处输入大写的英文字串时, EMACS 所找寻字串的大小写,就会与minibuffer 字串的大小写 完全一样。 此种对大小写极度敏感的作法,称为 case sensitive。 EMACS 对於大小写出现的位置也有差别,现在就先来讨论 case sensitive 的问题。
minibuffer 的字母都是小写,则可能找到的字串,包含 所有大小写的字串。 原始文件:abc、 Abc、aBc、 abC、ABc、aBC、ABC 输入 minibuffer 的字串: abc search string:abc、 Abc、aBc、 abC、ABc、aBC、ABC
minibuffer中的字母,若有任一个以上的字母是大写,则可能 找到的字串,就如同 minibuffer 所示的一样。 原始文件:abc、Abc、aBc、 abC、ABc、aBC、ABC
输入 minibuffer 的字串: Abc search string:Abc
输入 minibuffer 的字串: aBc search string:aBc
输入 minibuffer 的字串: ABc search string:ABc
输入 minibuffer 的字串: ABC search string:ABC
如果希望 minibuffer 输入什麽,缓冲区就找到什麽时,
例如,在 minibuffer中输入 abc,所要找寻的字串就是 abc。
此时就必需修改 EMACS ``case-fold-search'' 变数的值为 ``nil''。
因为EMACS 对此变数的预设值是 t,它的意思就是使搜寻成为
case sensitive。若将此变数改成非 case sensitive 时,在
minibuffer} 输入 abc,就只会找寻 abc。
知道了 EMACS 对大小写的处理程序後,现在就来谈谈
incremental search和noincremental search。键入
第一个字母至minibuffer 时,搜寻的序幕就展开,是为
incremental search。
使用 incremental search时,当 minibuffer
收到第一个搜寻字母时,搜寻行动就从游标所在位置向下开始搜索。
此时的游标,会从原先游标所在位置移至其下第一个出现此字母的
地方;当 minibuffer 出现两个字元时,游标也移至其下出现
此两个字元的地方。当然,这些都必需要以缓冲区中有这些文字为前题,
若找不到任何合适的文字时,
echo area 会出现 ``Failing I-search:'' 的警示语。
在 EMACS 中执行 incremental search的方法有两种,
一种是往前的搜寻( forward search),另一种是回头的搜寻
( backward search。如下,就是二种搜寻所使用的指令:
Ctrl-s (isearch-forward)
游标所在处向前(右)搜寻。
Ctrl-r (isearch-backward)
游标所在处往回(左)搜寻。
当使用 Ctrl-s 或 Ctrl-r指令时, EMACS 的
echo area 会出现 ``I-search:''或 ``I-serach backward:''。当出现这些
提示字时,就表示其後是要输入搜寻字串。只要键入第一个字元,
搜寻的行动就立即展开了。 虽然如此,仍有几个有关搜寻的关键字
必需先行讨论,它们是 ``RET''、 ``DEL'' 与``Ctrl-g''。
RET
当 minibuffer收到 RET 时,游标会停留在最後一个满足搜寻 条件之处,且结束搜寻的动作。这种做法的好处是,若所找的资料 就是所要的,且又是必需修改的资料,游标停留在该处,即可立即处理。 因为,有些离开搜寻状态的方法,会使游标到第一次执行搜寻的位置, 这就是下面要谈的`` Ctrl-g'' 的处理原则。
Ctrl-g
``Ctrl-g'' 的用法有二种,其一、是离开搜寻,承如上面所言; 其二、就是当所键入的搜寻字串,无法找到完全符合的对应字串时, 可利用 ``Ctrl-g'' 将minibuffer中所能找到的子字串留下, 而将不能找到的资料除去。 所以,若已经找到所要的字串而想离开搜寻时,键入 ``Ctrl-g'' 除了可以离开搜寻外,还可以将游标带回原先的出发点。根据 minibuffer 处所给予的资料,完全无误的在缓冲区中找到相对应 的文字後,以 ``Ctrl-g'' 可将游标归回原位。但若所给予的资料在缓冲区 中无法找到完全对应的字,此时,若想离开搜寻的状态,就必需用「两次」 的 ``Ctrl-g'' 才可完全脱离搜寻的状态。
此时,键入的第一个 ``Ctrl-g'' minibuffer 所找到的字串留下,而将 无法兑现的字串删除。此时再键入一次 ``Ctrl-g'' ,则会完全离开搜寻的 状态,而游标也会自动移回展开搜寻时的位置。
DEL
想要修改 minibuffer 内的字串时,请用DEL(delete-backward-char) 。 还记得它吗?它是用来删除字元的,此删除的动作是往回的删除。每执行 一次的DEL,就是一个新字串的诞生, 也就是一个新搜寻的展开。值得注意 的是,此新展开的搜寻与原先的搜寻方向相反,这个设计是合理的。 因为, 只要minibuffer 输入任何一个字,搜寻就己展开了,此时,若以DEL 来 删除错误的输入,则表示先前所找到的字串也不符需要,所以只能重新来过 才是可行之路。这也就是为何每删掉一个字元,搜寻的行动会回走的理由了。
要使用搜寻的指令,只需键入 ``Ctrl-s'' 。此时minibuffer会出现
``I-search:''。若键入 Ctrl-r 後,则出现 ``I-search backward:''。
使用搜寻的指令,会有若干情形出现:
首次搜寻,即觅得所要的字串。
使用者此时可根据所需来做适当的处理。例如,离开搜寻,修改搜寻到 的资料等等。
搜寻不利,必需一而再,再而三的努力,才能找到所要的字串。
此时只要重复使用 `` Ctrl-s'' ,游标就移至下一个符合的字串。所以, 只要所找寻的字串不变 就不需要再给予搜寻的字串。重复使用 `` Ctrl-s'' 的结果,仍无法找到所要的字串,echo area 会回应出 ``Failing I-search:'' 的讯息。
若重复使用 `` Ctrl-s'' 的结果,已使 echo area 出现
``Failing I-search''後,则表示缓冲区已到了极限。若想再查看已阅 过的资料,只要再使用一次 `` Ctrl-s'' ,就会迫使搜寻从原来的启始点 再进行一次的搜寻。
搜寻的过程中,若想回头再查阅已看过的资料时,只要交替使用 `` Ctrl-s'' 与 `` Ctrl-r'' ,就可以了。
EMACS 可以将搜寻过的字串,再拿出来重复使用。因为,EMACS
将使用过的搜寻字串,都放在一个名为 search ring的变数中。
它与先前讨论过的 kill-ring 类似,都为变数。既为变数,其值就可以
增减与参阅的。至於如何查阅变数的值,请再行参考 4.4 节
(EMACS buffer and windows)。
再使用搜寻过的字串,只要将 `` serach-ring''此一变数的
值取出即可。取出其值的方法有两种,一种是往前(右)的取出,
另一种是往回(左)的撷取,其分界点是以最新使用过的搜寻字串
为分野。因为 `` search-ring'' 为一个ring,所以撷取的方式,
不是采顺时锺的方向,就是采反时锺的方向。今举一实例说明之。
以下是以 `` Ctrl-h v'' 所得到有关 `` search-ring'' 的资料。
search-ring's value is ("kill" "Ctrl" "tex" "Ctrl-h" "text" "search")
Documentation:
List of search string sequences.
此时 `` search-ring'' 的变数值是:
kill、 Ctrl、 tex、 Ctrl-h、 text 、 search
最新的搜寻字串则位於最前端,此时为 ``kill'' 。往前(右)所得
的字串则为``Ctrl'',往回(左)所得的字串则为 ``search''。
今以此例,将使用`` search-ring'' 得取使用过的搜寻资料的步骤
说明如下:
首先键入 Ctrl-s 或 Ctrl-r
ESC-p
当 echo area 出现 I-search: 後,再键入``ESC-p'', 则可得到 ``Ctrl''的字串。若想继续往前寻找使用过的字串, 只要重复键入``ESC-p'' 即可。
ESC-n\indexESC-n
当 echo area 出现 I-search: 後,再键入ESC-n,则可得到 ``search'' 的字串。若想继续往回寻找使用过的字串,只要重复 键入 ``ESC-n'' 即可。
除了 search-ring 的资料可再使用外,将拷贝技巧运用在搜寻上,
也是资料再使用的另一项运用。
拷贝在搜寻上的运用,就是将缓冲区的资料拷贝至 echo area 出现
I-search: 後的 minibuffer 处。经此拷贝的过程,任何大小
的字串,都可使它轻易的出现在 minibuffer 处。
以下就是使用的方法:
键入 Ctrl-y
若要将游标所在位置到列尾的所有资料,放入 minibuffer 处, `` Ctrl-y'' 可完成此一任务。
键入 Ctrl-w
若只想拷贝游标所在位置之後的字(word),使用 `` Ctrl-w'' 是一个很好的选择。 `` Ctrl-w'' 是拷贝缓冲区上的一个字(word) 到 minibuffer 。其拷贝的范围是以游标所在地为准则, 若游标位於一字的第一个字元时,则拷贝整个的字到minibuffer 处, 若游标所在位置不在字首,则将游标之後的次字串拷贝至 minibuffer 处。
键入 ESC-y(yank-pop)
向 kill-ring 借资料也是可行的方法之一。使用 `` Ctrl-s'' 或`` Ctrl-r'' 指令後,可使用 `` ESC-y'' 将原先放於kill-ring 的资料拷贝於minibuffer 处。可惜的是,此方法只能将最新被删除 的资料来出用。
到此为止,所谈的搜寻都是 incremental search的搜寻,现在来谈谈
nonincremental search。 nonincremental search
是一般编辑器处理搜寻最常用的方法,所以又将其称为传统的用法。
nonincremental search的使用,必需从 incremental search开始。
当以 `` Ctrl-s'' 或 `` Ctrl-r'' 启动 incremental search後,待 echo area
出现 ``I-search:'' 或``I-search backward:'' 後,只键入 RET 而不给予
任何其它的字串,此时就启动了 nonincremental search.当然echo area
出现的讯息会不一样,此时 echo area 出现的讯息 ``Search:'' 或
``Search backward:''。所以要使用 incremental search或
nonincremental search,完全取决於键入 minibuffer 的内容
而定。若为只键入 RET ,就是选择 nonincremental serach。
在启动了 nonincremental search之後,在 ``Search:'' 或
``Search backward:''之後键入 ``Ctrl-w'',则表示要执行字的搜寻
(Words Search)。此功能可以用来搜寻一组的字,只要在 minibuffer
中将所要搜寻字群以一个空白隔开,就表示所要搜寻的是一个
字群了。以下将 nonincremental serach 的相关指令整理如下:
nonincremental 字串(string)的搜寻
Ctrl-r RET string RET
Ctrl-s RET string RET
nonincremental 字(word)的搜寻
Ctrl-r RET Ctrl-w words RET
Ctrl-s RET Ctrl-w words RET
还有一种搜寻不论是 incremental search或 nonincremental search
都有的,那就是 regular expression,简称为Regexp。
所谓的 regular expression就是以最少的字元组合来表示最大可能的巨集。
现先不讨论如何来表达 regular expression ,只讨论如何使用
regular expression 的搜寻。
因为有关 regular expression 会有专节(6.3 节)来讨论。
Regexp 的 incremental search
ESC-Ctrl-s(isearch-forward-regexp)
使用Regexp 的搜寻方法: 先按下 ``ESC'' 之後再将其放掉,随後再按下 ``Ctrl-s'', echo area 处就会出现 Regexp I-search: 这时就可以输入 Regexp 的表示字串了。 当然还有若干事与 Regexp 的搜寻有关,讨论如下:
若要继续以 Regexp 的方法搜寻,只需继续键入 ``Ctrl-s'' 就可找到下一笔相关的资料。
Regexp 的搜寻,也有其独立的 search-ring。 这也意谓著可以再使用 Regexp 的 search ring 。 其使用法与前所讨论的方法一样,就是以 `` ESC-p'' 与 `` ESC-n'' 来再使用Regexp 的 search-ring。
ESC-Ctrl-r(isearch-backward-regexp)
往回(左)的 Regexp 搜寻,若想继续的往回找寻想要的字串, 只需键入 `` Ctrl-r'' 即可。当然,也可以 `` ESC-p'' 与 `` ESC-n'' 来再使用其 search-ring。
nonincremental search
以下是以 Regexp 来执行 nonincremental search的方法。
ESC-x re-search-forward
ESC-x re-search-backward
搜寻缓冲区的内容,除了要参考其内容外,有很大的机率是希望
能将找到的内容以它种内容取代。取代( Replacement)的方法,
就是下一节讨论的主题。
文件的取代
从事编辑工作的时候,常会为了某种需求而将某一共同的用语 以另外一种语辞替换,此情形最常发生在撰写程式的时候。 当然,英文字串由大写改为小写或小写改为大写也是经常可见的。 编辑文件在处理这些事时,就如如下的若干问题产生。
所有要修改的文件,都能如愿以偿的得到适当的修改,不会 有漏网之鱼发生。
修改过文件的一致性,是必要的条件。
因为有这些问题的考量,所以使用 replacement 来完成如是的工作,
就成为最佳的解决之道。现在就来谈谈 EMACS 如何处理 replacement
的问题。
EMACS 处理 replacement 的方法有一气呵成的取代,
与选择性的取代二种。
所给予的被取代文字也有二种,
一、被取代的字串完全与所给予的字串一样;
二、以 Regexp 来做为取代的依据。试分述之。
二种的取代方法
一气呵成的取代
所谓一气呵成的取代,是一鼓作气将游标之後所有符合条件的 字串,全部以新字串取而代之。 此作法不会一一徵求是否要取而代之的意见,而是自动、全盘 且无条件的取代。 此作法称为 Unconditional Replacement。
选择性的取代
选择性的取代,是会先徵求取代的意见,只有在取得同意权时, 才会采取取代的行动。这种取代的行为称为 query replacement。
二种被取代字串的表示法
被取代的字串完全与所给予的字串一样。
此方法所要取代的文字,与 minibuffer 中所给予的文字一样。 所以,其可能符合条件的选择最多也只有一种。
Regexp 来做为取代的依据。
此方法就是以 Regexp 来表示所要找寻的字串集。前已论及,所谓的 Regexp 就是以最少的字元组来表达最多的巨集。此时的取代, 就不是单一字串的取代而是某一集合中的所有字串的取代。 所以,以此所得的取代字串就有多重的选择。如何表达正确的 Regexp 会在下一节中详细讨论。
以下就是使用取代( replacement)的方法。
Unconditional Replace
进行取代时不事先徵求意见,迳行将缓冲区中游标所在位置(包括 游标所在位置本身)之後,所有符合的字串都以新字串取而代之。
ESC-x replace-string RET string RET newstring RET
此指令是将缓冲区中,所有出现 string 的字串以newstring 取代。 其详细的步骤如下:
键入 ``ESC-x replace-string'',当按下 RET 时,
echo area 会出现
Replace string:
此时可利用 ``Replace string:'' 後的 minibuffer , 将所要被取代的字串输入,按下 RET 则表示已完成输入的工作 。
按下 RET 时, echo area 会出现如下讯息:
Replace string string with: 此时可利用 minibuffer 给予所要取代的新字串。 键入 RET 时,游标所在位置之後的所有string 都会转换成newstring。
当转换完成後, echo area 会出现 ``done''。此时就已 大功告成了。
ESC-x replace-Regexp RET Regexp RETnewstring RET 此方法与上一个方法雷同,不同之处在於所要取代的资料不是 某一个特定的字串,而是某一巨集的字串组。从指令的表示法, 也可以看出所给予的被取代字串是 ``Regexp'' 而非 ``string''。 其详细的执行步骤与字串的取代雷同,只是在 echo area 将所有 的 string 换成 Regexp罢了。
Query Replace
Query Replace顾名思义就是在取代时会徵询取代的意见, 使用者可根据需要来取决是否要进行取代。以下就介绍字串和 Regexp的取代。
ESC-x query-replace RET string RET newstring RET
使用 query replace 的方法及步骤与 unconditional replace的方法雷同,不同处只在於所引用的 指令有异,以及多增了询问的选择。其详细步骤如下 (斜体字表示使用者所输入的资料,粗体字表示系统自行根据 输入资料的回应):
ESC-x query-replace RET
Query replace: Regexp RET
Query replace Regexp with: newstring RET
Query replacing Regexp with newstring:(? for help)
此步骤是 query replace 与 unconditional replace 最大不同之处。因为所有徵询的工作都是从此展开。 不知如何使用徵询的使用者可键入 ``?'' 来得到线上的求助。 以下就是键入 ``?'' 系统给予的资讯。 Query replacing Regexp with string.
Type Space or `y' to replace one match, Delete or `n' to skip to next,
RET or `q' to exit, Period to replace one match and exit,
Comma to replace but not move point immediately,
C-r to enter recursive edit (M-C-c to get out again),
C-w to delete match and recursive edit,
C-l to clear the screen, redisplay, and offer same replacement again,
! to replace all remaining matches with no more questions,
^ to move point back to previous match.
使用 query replace 时可有多种选择项。现只介绍常用的几个选项:
Space 或 y
当决定以新的字串取代原来的字串时,以 Space 或 y 来表示。 执行 query replace 时,游标会移至下一个合适的字串处, 此时若决定将其取代,则键入 ``Space'' 或 ``y''。当取代完成後, 游标会自动移至下一个合适的字串处。当然也可以放弃所找到 的字串,这就是下一个要讨论的选项了。
Delete 或 n
放弃字串的取代,使游标移至下一个目的地,是 Delete 或n 所做的事情。
(Period)
若已找到合适的字串,而想终止所有进一步的取代行为时, 键入``.''可使目前游标所在处的字串以新的字串取代,并且 在取代後立即离开query replace 的状态。
!
`` !'' 可使 query replace 恢复为unconditional replace。 因此,若想放弃询问的权利而恢复 unconditional replace的 状态,键入 ``!'' 就可将游标所位置及其以後所有合适的字串, 都以新字串取而代之。
RET 或 `` q''
若想就此离开 query-replace 而不再做进一步的取代动作, 只需按下RET 或 ``q'' 即可。
ESC-x query-replace-Regexp RET Regexp RET newstring RET
此方法与 `` ESC-x query-replace RET string RET newstring RET'' 相似,但此时所取代的不是特定的字串,而是某一字串的巨集。
字串大小写( case sensitive)的问题与取代也有很大
的相关性。因为大小写的问题,对於 unconditional replace 与
query replace 均适用。所以,只举 query-replace
为例说明,至於 unconditional replace 就如法泡制。
当启动 `` ESC-x query-replace RET string RET newstring RET''
时, string 与 newstring 的大小写,关系不同字串的取代。
其规则如下所述:
当 string 与 newstring 都以小写的形式出现时,
取代工作的进行,就有三种情形:
缓冲区原始字串的第一个字母是以小写为开端时,不论 此字串是否有其它的大写字母,经取代後全转换成小写。
若原始字串的每个字母都是大写时,经取代後也维持大写的形式。
若原始字串以大写为开端,不论此字串是否还有其它的字母 为大写,只要不是全为大写的情形,取代後只有字串的第一个 字母为大写,其余一律为小写。
以下举一实例,供参考: string :abc , newstring: xyz 原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xyz Xyz xyz xyz Xyz Xyz xyz XYZ
当 newstring 部份为大写,而 string 依然维持小写 的情形:
若原始字串的第一个字母为大写时,被取代後的第一个字母 仍维持大写的形式。其余原始字串的大小写,就视 newstring的大小写而定,与原始字串本身的大小写无关。
newstring 的字母若以大写出现,则被取代的原始字串 也会在相对应的位置以大写的形式出现。
若原始字串全为大写时,取代後仍维持大写的形式。
以下为若干实例,供参考:
string :abc , newstring: Xyz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: Xyz Xyz Xyz Xyz Xyz Xyz Xyz XYZ
string :abc , newstring: xYz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xYz XYz xYz xYz XYz XYz xYz XYZ
string :abc , newstring: xyZ
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xyZ XyZ xyZ xyZ XyZ XyZ xyZ XYZ
string :abc , newstring: XYz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: XYz XYz XYz XYz XYz XYz XYz XYZ
string :abc, newstring: xYZ
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xYZ XYZ xYZ xYZ XYZ XYZ xYZ XYZ
string :abc , newstring: XYZ
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: XYZ XYZ XYZ XYZ XYZ XYZ XYZ XYZ
只要 string 有大写的字母出现时,取代後字串的大小写, 就完全依照 newstring 的大小写。换言之, newstring 为大写的地方依旧为大写,为小写的地方依然为小写。 以下为若干实例,供参考:
string :Abc , newstring: xyz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xyz xyz xyz xyz xyz xyz xyz xyz
string :aBc , newstring: xyz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xyz xyz xyz xyz xyz xyz xyz xyz
string :ABc , newstring: xyz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xyz xyz xyz xyz xyz xyz xyz xyz
string :ABC , newstring: xyz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xyz xyz xyz xyz xyz xyz xyz xyz
string :aBC , newstring: Xyz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: Xyz Xyz Xyz Xyz Xyz Xyz Xyz Xyz
string :Abc , newstring: xYz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: xYz xYz xYz xYz xYz xYz xYz xYz
string :aBc , newstring: XYz
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为:XYz XYz XYz XYz XYz XYz XYz XYz
string :ABC , newstring: XYZ
原始文件为: abc Abc aBc abC ABc AbC aBC ABC 经过取代为: XYZ XYZ XYZ XYZ XYZ XYZ XYZ XYZ
讨论至此,取代的部份应该可以告一段落了。接下来,就是要将
一直未正式讨论的 Regular Expression 做一详尽的说明。
Regular Expression
所谓的 Regular Expression(以下简称 Regexp) 是用来表达一连续字元的组合,或是用来描述字样(pattern) 的一种方法,它在概念上不同於字串。 字串是指由一连续字元(character)所形成的字元组, 一个字元组就只代表一个特定的字串,而 Regexp 通常所代表 的是一组具有共通特性的字串集,此共通特性称为 pattern。 Regexp 的精神,就是希望以最少的字元,来表达最大的巨集。 所以 Regexp 所能表达的字串数应不只於一个,而是一个字串 的集合。这也是字串与 Regexp 最大不同处。当然, 也可将字串视为 Regexp的一个特例。 本文一直没有讨论撰写程式的问题,但不可否认的, 程式撰写使用到 Regexp的机会不胜数。举凡变数、常数的 重新命名,同类字串的搜寻,数个程式合并後的整理工作等等, 若能灵活使用 Regexp ,则可达事半功倍之效。这是一篇 讨论 EMACS 使用入门的文章,并不是讨论程式语言的文章, 所以只是将 Regexp 的用法告知。
撰写程式常有的经验,就是要将数个函式合并後,必需将 使用这些函式的地方,以合并後的新名称取代。例如 read_i.c、 read_c.c与read_f 等三个函式合并成 read.c 时,所有使用 这三个函式的地方,都必需改成 read.c。 此时,若以传统的修改方法,恐会漏一挂万,若采取代法, 则必需执行三次的取代,才可以大功告成。但使用 Regexp , 则可以一举成功。既方便又快速。 又则,编辑文件或程式时,注解的功夫是不可或缺的。 不同的文件与语言,各有其不同的注解表示法。例如 LISP 语言是以 ``;'' 表示注解, LaTex 则以``'' 为注解。有时为了 将注解分类,极H不同数目的注解符号来做区分。此时遇到的问题 是,当重新分类注解时,相对应的注解符号也要调整。以 LISP语言为例, 重新分类後,可能将拥有一个 ``;'' 及二个 ``;''的注解符号,以三个 ``;'' 注解符号取而代之,这时若使用Regexp ,问题就可迎刃而解。
程式档名的转换也可以 Regexp 来轻松完成。 因为不同的程式各有其不同的副名,例如 C 语言的副名为 ``.c'' , C++ 语言的副名为 ``.C'' ,而FORTRAN 语言则为 ``.f'' 等等。 此时,若想将 FORTRAN 语言所发展软体,转换成 C 语言, 则必需将所有 ``.c'' 的档名做适当的调整。
例如,将所有``filename.f'' 的档案,改为 ``filename.c'' 的档名。 此时,若使用 Regexp ,可能在极短的时间就可将所有的 档名转换成功。 Regexp 的运用非常的广泛,这里所举的例子 只是凤毛麟角。以下就开始简介 Regexp 的使用法,至於 更详细的介绍,请参照另一篇 94019 的技术报告。
Regexp 并非是 EMACS 的专利,而是 UNIX 系统下 的产物。 UNIX 系统下的许多工具程式都使用 Regexp , 虽然彼此在表达 Regexp的方法上,略有差别,但在概念上 却是一致的。 UNIX 系统上使用 Regexp 的工具,除了EMACS 外,尚有ed 、ex 、vi 、sed 、awk 、grep 、egrep 等等。 这里所介绍的 Regexp,在概念上与其它地方是共通的, 但在实际的运作上可能会有所出入。
Regexp 是由字元所组成,此字元分为一般字元与特殊字元 两种。一般字元所组成的 Regexp ,是最简单的 Regexp 的表示法。因为它所要表达的字串与 Regexp 完全一模一样 。 由特殊字元所组成的 Regexp就较为复杂了。因为不同的特殊 字元,各有特殊的代表意义。EMACS 中Regexp 的特殊字元有 $、 ^ 、 .、 * 、 + 、 ? 、 [、] 及 \等九个。例如, ``a'' 是一般 的字元,它也只代表 ``a'',别无其它。但是 ``a.''、 ``a*'' 、 ``a?'' 与 ``a+''等所代表的意义,除了 ``a'' 外,尚有其它的意涵。 特殊字元之外的字元,都是一般字元,但有些特殊字元是由 一般字元加上 \ 而形成的。 现在来谈谈几个简单的 Regexp 的表示法。 想进一步多了解 Regexp,仍请参考编号为 94019 的技术报告。 以下就开始讨论,代表 Regexp 的符号 有九个。为了讨论上的方便,将其分成若干类:
不在乎所出现的字母为何,可以『.』 来表示。
所以『.』 代表除了 newline 之外的任何一个字元。 例如 ``a.b'' 表示任何一个由三个字元所组成的字串, 此三个字元必需符合第一个字元是 ``a'', 第三个字元 是 ``b'',但中间的字元只要不是 newline, 任何字元都可以。所以 ``a.b'' 可以为 aab、abb、acb、axb、a1b、a2b 等等, 甚至可以是一些特殊字元,只要这些特殊字元在此时没有 特别的意义。例如 ``a^b''。
以特殊字元,来表示重复出现的一般字元。这些特殊字元称为
postfix character。其表示符号有 『*』、『+』 与『?』 等三种。
『*』
任何字元之後加上『*』,可表示字元重复出现的次数, 从零次到无限多次。因此 ``ab*'',可表示, a、ab、abb、abbb、abbb以及 ab...b 到无限个 b。
『+』
任何字元之後加上『+』,可表示字元重复的出现次数, 从一次到无限多次。因此 ``ab+'',可表示为 ab、abb、abbb、abbb 以及 ab...b 到无限个 b。
『?』
任何字元之後加上『?』,可表示字元重复的出现次数, 不是零次就是只有一次。因此 ``ab?'' 不是表示为 ``a'' , 就是表示为 ``ab''。
若字元为属於某一特定集合中的元素时,可以字集(character set) 来表示。
此时的代表符号是 [...]。中括号内就是放置字集的地方。如下讨论 字集的若干变化。
[...]
最简单的字集表示法,是将所有符合的字元放於在括号内。 例如,[a@!d13]。此时,符合的字元就只有 a、@、!、1 和 3 而已。 若所表示的字集,具有一定范围的连续性,为了方便表达, 可以 [a-g] 来代表[abcdefg] 。 这一类的表示法,将会有更 详细的讨论。
[...] 与其它的特殊字元的配合。>
[...] 与 「*」 的配合
c[ab]d 代表 ``cad'' 或 ``cbd''。 c[ab]*d 代表头尾为 ``c'' 与 ``d'' 的字串 , 头尾间的组合,则由 [ab]* 来决定。 [ab]* 可表示为 cd、cad、cbd、caabd、cabbaar 等形式。
[...] 与 + 的配合
c[ab]+d 表示头尾为 ``c'' 与 ``d'' 的字串 , 头尾间的组合,则由[ab]+ 来决定。 [ab]+ 可表示为 cad、cbd、caabd、cabbaad 等形式。
[...] 与 ? 的配合
c[ab]?d 表示头尾为 ``c'' 与 ``d'' 的字串 , 头尾间的组合,则由 [ab]? 来决定。 [ab]? 可表示为 cd、cad、cbd 等形式。
特殊字元,为字集内容时的解释
任何 Regexp 的特殊字元,出现在中括号内都 可将其视为一般的字元,除了二个符号之外。这二个 例外的符号,也要看其出现的位置而做不同的解释。 这两个符号是 ``^'' , ``-''。
``^''
``^'' 若出现在括号的第一个位置时,表示「以下皆非」 的意思。所以, [a-zA-Z] 表示所有英文字母的集合, 而[^a-zA-Z] 则表示除了英文字母之外的所有集合。
``-''
简化范围性的字集的表示法。其用法是将字集的起始点 放於 ``-''的右边,而终点放於 ``-'' 的右边。 例如,[abcdefg]可以 [a-g] 来表示, [a-z] 则表示二十六个字母的集合。[0-9] 则表示数字的集合, 要表示二位数字的集合,则可以 [0-9][0-9] 来表示。
指定 Regexp 出现的地方为列首或列尾。
``^'',指定 Regexp 出现在列首的符号。
在 Regexp 前加上 ``^'' 的符号,即表示此 Regexp 必需出现在列首。例如, ``^ The'' ,则找寻每列以``The'' 为首的字串。
``$'', 指定 Regexp 出现在列尾的符号。
指定 Regexp 必需出现在列尾时,只要在 Regexp 後加上一个 ``$'' 即可。例如, xxxx+$ 会将列尾以 ``x'' 结束的字串找出。
``\'' 的用法
\ 在 Regexp 中有二种涵意:一、使特殊字元变为 普通字元,二、使普通字元转为特殊字元。
特殊字前加上 \ ,此特殊字元就不具特殊意义, 只是一个普通字元而已。例如,列首要以 ^ 为开端, 则以 \^ 来表示,此时的 ^ 则为普通字元。
将如下的普通字元
|、 (、 )、 d、 '、 `、 b、 B、< 、> 、 w、 W、 sc、 Sc 之前加上 \ 之後,则此普通字元就已特殊化了。现在 只讨论几个常用的字元(| 、(、)、d、< 、> )。
\| ,(表示选择的用法)
欲在二个 Regexp 中择一而用,可以 \| , 将二个 Regexp 放於其左右来表示。 例如,the\| it 为二选一的 Regexp 的表示法。可能的符合字串为大小写穿插其间的 the 或 it。例如,The、tHe、thE、THe、tHE、THE、iT、IT 等。为何会有不同的大小写表示法,请再一次注意 EMACS 对於 case sensitive 的处理方式。若不清楚者, 请参照 6.1 节。 以 \| 所表示的 Regexp 有一个特色, 那是在找寻合适的字串时,与 Regexp 从开始至结尾 都符合的字串。所以,若想找出read 或 get 其後立即接上 file 的字串,就必需以 `` readfile\| getfile 来表示。乍看之下,似乎没有任何的疑问。事实上, 这种表示法也没有不对,可是各位是否还记得 Regexp 的真谛,就是要以最少的字元来表示最大的字集,在这个例子 中的重复性很高,似乎不太能符合 Regexp 的精神。 下面就是改进的方法。
利用 \(...\) 将 Regexp 的范围规范出来。
利用此方法,上例就可以``\(read\| get\)file'' 来找寻 readfile 或 getfile。这样是不是简捷多了吗? \(...\) 还可以配合 *、+、? 等特殊字元 使用。 *、+、? 等特殊字元在处理字元的重复性时, 只适用在特殊字元之前的一个字元,所以若想重复一组 字元时,就必需以\(...\) 将其组合起来。例如,``ba\(na\)*, 可以表示 bana、banana 与bana.......na 等无数个 na 的组合。
除了以上所述的表示法外,若干普通字元加上 \ 还可以 有特别的用法,以下讨论常用的几个用法。
\ d
在 Regexp 的表示法中,可以只撷取部份的符合 egexp 。其做法是将要保留的 Regexp 暂存在缓冲区 内之後,再将其拿出来使用。例如,要将所有副名为 .c 的档名,换成为 .f 的档名,其做法如下: ESC-x replace-Regexp RET \(file[0-9]\)\.c RET \1\.f 如此,会将所有名为 file1.c file2.c file3.c ...... file9.c 的档名,改为 file1.f file2.f file3.f ........ file9.f。
\< ,寻找一字的开头
\> ,寻找一字的结尾 例如,\< b[a-z]g\> 会将 beg、 big、 bag 等字串找出。