Emacs 进阶编辑指令

前面的章节谈论 EMACS 的基本用法,现在讨论 EMACS 进阶 的用法。现在先谈 EMACS 的搜寻 search 与字串 string 的取代功能。


文件的搜寻

搜寻特定的字串,并非 EMACS 所特有的功能,相信大多数的 编辑器都具有如此的功能。但 EMACS 所采取的搜寻方法是, 每键入一个字元就展开搜寻,EMACS 称此种方式的搜寻为 Incremental Search 。 当然, EMACS 也提供非 Incremental Search} ,称为 Nonincremental SearchEMACS 对於所要搜寻的字串,仍是利用 minibuffer 来输入所欲搜寻的字串。 此时,输入 minibuffer 的搜寻字串,若全由小写的英文字母 (lower case)组成,则 EMACS 在展开搜寻的行动时,不论字母 是否有大小写的差别,会将所有与 minibuffer 具有相同 英文字母的字串都找寻出来。例如在 minibuffer 处输入 abcEMACS 会找寻abcAbcaBcabCABcaBCABC 等字串。 所以在 minibuffer 处输入小写的英文字母,就表示所要找寻 的字串包括大写的字母在内。 如何直接找寻上述例子的 ABC,而不需经过 abcAbcaBcabCABcABC 等字串呢?欲达如此的效果, 必需在 minibuffer 处,给予大写字母(upper case)的字串。 例如,在 minibuffer 处,给予 ABC 的字串,此时,缓冲区 的内容若为abcAbcaBcabCABcaBCABC ,则会 直接搜寻 ABC 。所以在 minibuffer 处输入大写的英文字串时, EMACS 所找寻字串的大小写,就会与minibuffer 字串的大小写 完全一样。 此种对大小写极度敏感的作法,称为 case sensitiveEMACS 对於大小写出现的位置也有差别,现在就先来讨论 case sensitive 的问题。

如果希望  minibuffer 输入什麽,缓冲区就找到什麽时,
例如,在 minibuffer中输入 abc,所要找寻的字串就是 abc。
此时就必需修改 EMACS ``case-fold-search'' 变数的值为 ``nil''。
因为EMACS 对此变数的预设值是 t,它的意思就是使搜寻成为
case sensitive。若将此变数改成非 case sensitive 时,在
minibuffer} 输入 abc,就只会找寻 abc。

知道了 EMACS 对大小写的处理程序後,现在就来谈谈 
 incremental searchnoincremental search。键入
第一个字母至minibuffer 时,搜寻的序幕就展开,是为 
incremental search。
使用  incremental search时,当  minibuffer 
收到第一个搜寻字母时,搜寻行动就从游标所在位置向下开始搜索。
此时的游标,会从原先游标所在位置移至其下第一个出现此字母的
地方;当 minibuffer 出现两个字元时,游标也移至其下出现
此两个字元的地方。当然,这些都必需要以缓冲区中有这些文字为前题,
若找不到任何合适的文字时,
 echo area 会出现 ``Failing I-search:'' 的警示语。
在 EMACS 中执行  incremental search的方法有两种,
一种是往前的搜寻( forward search),另一种是回头的搜寻
( backward search。如下,就是二种搜寻所使用的指令:

当使用 Ctrl-s Ctrl-r指令时, EMACS echo area 会出现 ``I-search:''``I-serach backward:''。当出现这些
提示字时,就表示其後是要输入搜寻字串。只要键入第一个字元,
搜寻的行动就立即展开了。 虽然如此,仍有几个有关搜寻的关键字
必需先行讨论,它们是 ``RET''``DEL'' ``Ctrl-g''
要使用搜寻的指令,只需键入 ``Ctrl-s'' 。此时minibuffer会出现 
``I-search:''。若键入 Ctrl-r 後,则出现 ``I-search backward:''。

使用搜寻的指令,会有若干情形出现:
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''  的变数值是:

        killCtrltexCtrl-htext search
最新的搜寻字串则位於最前端,此时为 ``kill'' 。往前(右)所得
的字串则为``Ctrl'',往回(左)所得的字串则为 ``search''。
今以此例,将使用`` search-ring'' 得取使用过的搜寻资料的步骤
说明如下:
除了  search-ring 的资料可再使用外,将拷贝技巧运用在搜寻上,
也是资料再使用的另一项运用。

拷贝在搜寻上的运用,就是将缓冲区的资料拷贝至  echo area 出现
I-search: 後的  minibuffer 处。经此拷贝的过程,任何大小
的字串,都可使它轻易的出现在  minibuffer 处。

以下就是使用的方法:
到此为止,所谈的搜寻都是  incremental search的搜寻,现在来谈谈
 nonincremental searchnonincremental 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 searchnonincremental search,完全取决於键入  minibuffer 的内容
而定。若为只键入 RET ,就是选择  nonincremental serach

在启动了  nonincremental search之後,在 ``Search:'' ``Search backward:''之後键入 ``Ctrl-w'',则表示要执行字的搜寻
(Words Search)。此功能可以用来搜寻一组的字,只要在 minibuffer 
中将所要搜寻字群以一个空白隔开,就表示所要搜寻的是一个
字群了。以下将  nonincremental serach 的相关指令整理如下:

还有一种搜寻不论是  incremental search nonincremental search
都有的,那就是  regular expression,简称为Regexp。
所谓的  regular expression就是以最少的字元组合来表示最大可能的巨集。
现先不讨论如何来表达 regular expression ,只讨论如何使用  
regular expression 的搜寻。
因为有关  regular expression 会有专节(6.3 节)来讨论。

搜寻缓冲区的内容,除了要参考其内容外,有很大的机率是希望
能将找到的内容以它种内容取代。取代( Replacement)的方法,
就是下一节讨论的主题。

文件的取代

从事编辑工作的时候,常会为了某种需求而将某一共同的用语 以另外一种语辞替换,此情形最常发生在撰写程式的时候。 当然,英文字串由大写改为小写或小写改为大写也是经常可见的。 编辑文件在处理这些事时,就如如下的若干问题产生。

  1. 所有要修改的文件,都能如愿以偿的得到适当的修改,不会 有漏网之鱼发生。
  2. 修改过文件的一致性,是必要的条件。
因为有这些问题的考量,所以使用  replacement 来完成如是的工作,
就成为最佳的解决之道。现在就来谈谈 EMACS 如何处理 replacement
的问题。

EMACS 处理  replacement 的方法有一气呵成的取代,
与选择性的取代二种。
所给予的被取代文字也有二种,
一、被取代的字串完全与所给予的字串一样;
二、以 Regexp 来做为取代的依据。试分述之。
以下就是使用取代( replacement)的方法。
字串大小写( case sensitive)的问题与取代也有很大
的相关性。因为大小写的问题,对於  unconditional replace query replace 均适用。所以,只举 query-replace
为例说明,至於  unconditional replace 就如法泡制。
当启动 `` ESC-x query-replace RET  string RET  newstring RET''
时, string newstring 的大小写,关系不同字串的取代。
其规则如下所述:
讨论至此,取代的部份应该可以告一段落了。接下来,就是要将
一直未正式讨论的 Regular Expression 做一详尽的说明。

Regular Expression

所谓的 Regular Expression(以下简称 Regexp) 是用来表达一连续字元的组合,或是用来描述字样(pattern) 的一种方法,它在概念上不同於字串。 字串是指由一连续字元(character)所形成的字元组, 一个字元组就只代表一个特定的字串,而 Regexp 通常所代表 的是一组具有共通特性的字串集,此共通特性称为 patternRegexp 的精神,就是希望以最少的字元,来表达最大的巨集。 所以 Regexp 所能表达的字串数应不只於一个,而是一个字串 的集合。这也是字串与 Regexp 最大不同处。当然, 也可将字串视为 Regexp 的一个特例。 本文一直没有讨论撰写程式的问题,但不可否认的, 程式撰写使用到 Regexp的机会不胜数。举凡变数、常数的 重新命名,同类字串的搜寻,数个程式合并後的整理工作等等, 若能灵活使用 Regexp ,则可达事半功倍之效。这是一篇 讨论 EMACS 使用入门的文章,并不是讨论程式语言的文章, 所以只是将 Regexp 的用法告知。

撰写程式常有的经验,就是要将数个函式合并後,必需将 使用这些函式的地方,以合并後的新名称取代。例如 read_i.cread_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 的符号 有九个。为了讨论上的方便,将其分成若干类: