学习Emacs系列教程(八):查找替换
查找替换是任何编辑器都不可能缺少的功能,就连小小的Notepad都有快速查找,更不用说像Emacs这样的庞然大物了。其重要性这里就不多说了,接下来将一步一步给你介绍如何在Emacs中进行查找和替换。
Emacs将查找分成了四个大类,分别是:简单查找,增量查找,词组查找和正则查找。四种方式各有特点,适用范围也不同,大家根据需要熟悉一两种即可。
一、增量查找
先介绍增量查找的原因是这个是用的最多的查找方式,使用也很方便,直接C-s (isearch-forward)便进入增量查找状态,Minibuffer出现如下提示:
其实增量查找很好理解,就是边输入边查找,在输入的过程中离光标最近的匹配会用深色高亮,一屏中其它的匹配字符串会浅色高亮,参见这张图:
只要是单词中包含"to" 的都会标记出来,比如tutorial。在输入查找字符串过程中可以随时用Backspace删除输入字符,当输入完成后按下回车会定位到向前最近的匹配字符串,并出现提示“Mark saved where search started”,就是说将你开始查找时光标所在位置加入了标记环,我们可以通过C-u C-@回到那个点。如果没找到字符串提示为“Failing I-search”,后接查找内容。C-s搜索时是向前进行查找,我们还可以使用C-r (isearch-backward)向后进行查找,注意这种方式的查找光标会定位到匹配字符串的最前方。虽然查找过程只会向一个方向进行,但到了文档底部或者头部还是会回到文档的头或底继续找,这就是常说的循环查找。
在搜索的过程中并不一定第一个匹配项就是我们需要的内容,或者说我们需要找的地方不止一处,这样我们想能够快速的定位到下一个匹配项位置,解决办法也很简单,接上例我们输入"to"后不要急着按回车,而是再用C-s就会向后定位,或者用C-r 向前定位,总之停到你想要的地方为止。另外,如果你退出了这次的查找,后面又想找同一个东西,那么使用C-s C-s 即可,同理C-r C-r 。
Emacs在多次查找的过程中会维护一个查找环(search ring) ,这是我们见到的第三个环了,前面两个是mark ring、kill ring。在输入C-s后,使用M-n和M-p可以调出查找环中上一个和下一个查找的内容。虽然这个也叫环但它是个标准的队列,有头有尾不会循环。
在增量查找中还有些特殊的功能:通常查找时是忽略了大小写的,但如果我们输入的查找内容有小写字母还有小写字母,Emacs会自动进入大小写匹配查找状态中;也可以在查找的过程用M-c 快速切换是否大小写敏感,不过只对此次查找启用;如果我们想找回车符用C-j 代替;还可以使用M-Tab (isearch-complete)对查找进行自动完成,候选项是查找环中的东西,当然你用M-Tab 是没效果的,用了后连Emacs都不在了,我们需要对isearch-complete重新绑定;在增量查找状态中按C-h C-h 会进入增量查找的帮助,里面的内容大家可以自己研究;使用C-w 可以将光标处单词复制到查找区域中进行快速输入,这个功能比较常用;与其类似的是C-y 它能够把光标所在处直到行尾的内容都复制到查找区域,提醒一点这两个命令在复制的过程中会把所有字母变成小写;M-y 还能把kill 环里的内容复制到查找区域;C-M-w 用于删除查找区域中最后一个字符;而C-M-y 把光标处字符复制到查找区域最后;复制光标处的字符还有个方法,进入增量查找状态后,首先按M-e 定位到查找区域的最后,再使用C-f 就可以开始复制了。
二、简单查找
简单查找顾名思义就是简单的查找方式(其实不一定有增量查找来的快),有时候又叫它非增量查找。使用命令是C-s RET string RET ,首先用C-s调出增量查找,然后回车取消掉进入简单查找,再输入需要查找的字符串,最后回车Emacs会向后开始找,直到第一个满足的字符串后面。大家在按命令的过程中可以观察Minibuffer中的提示,按下C-s后,会出现一个蓝色的“I-search:”,回车后变为“Search:”,当找到所输入的内容后出现提示“Mark saved where search started”,其实简单查找和增量查找大部分都没什么区别,增量查找中命令基本上这儿也能用,最大的区别就是等你输入完成后它才开始搜索,还有一点就是没有高亮。向前查找的话一回事,C-r RET string RET 。向后向前分别对应命令search-forward, search-backward。
三、词组查找
词组查找一般用在大段文本编辑中,比如写新闻、写信之类的。它在查找的过程中会忽略掉一些标点符号,比如换行符,逗号,句号之类的,查找时也是进行的增量查找,边输入边找,不过对于单词来说是全词匹配。其命令是M-s w (isearch-forward-word)。查找的示例如下图:
我在找"it"的时候,红框圈出来的permitted并没高亮,而我输入"it under"时,逗号和回车符都忽略掉了,这些特点在查找大段句子的时候相当好用。词组查找也可以使用非增量搜索的方式,对应命令M-s w RET words RET (word-search-forward) ,向回找M-s w C-r RET words RET (word-search-backward),这里面的words就是你需要查找的词组。词组查找其余大部分使用方式和前面的类似就不多叙述了。
四、正则查找
这种查找方式使用正则表达式,功能相当的强大,其主要还是看各人正则表达式的水平了,Emacs中的正则语法放到后面再讲,这里只说下怎么调出正则查找。命令 C-M-s (isearch-forward-regexp),咳,用快捷键的时候把QQ关了,这个是向前找,命令可以看出来使用的是增量查找方式,向后是C-M-r (isearch-backward-regexp)。
五、替换
本次的另一半内容就是介绍Emacs中的替换功能,就是将一个字符串变成另一个字符串。
最简单粗暴的方式就是使用replace-string 这个命令,用法如下:M-x replace-string RET oldstring RET newstring RET,第一次回车输入需要替换的字符串,第二次回车输入想要变成的字符串,最后确认。它将会把所有匹配oldstring 的字符串全部替换掉,建议大家在没绝对把握前还是不要用这个的好。在替换的过程中Emacs对大小写有比较智能化的处理,比如输入M-x replace-string RET abc RET efg RET,所有的"abc"都变成了"efg",但是"ABC"会替换成"EFG",不止这样"aBc"会改为"eFg",大写对大写,小写对小写。当然我们可能不想做这样的变化,那么把变量case-replace 设为nil 即可,还有个相关的变量case-fold-search ,设为nil后,它会在查找的时候就强制匹配大小写。
暴力替换的正则版命令replace-regexp,用法和上面一样,只是oldstring改为正则表达式。
实际上,我们在替换的时候并不希望所有的东西都换掉,还是想中间有个给自己判断选择的过程,在Emacs中称其为查找替换,M-% (query-replace) ,用的时候和前面一样,先输入oldstring,再输newstring,但是它是一个一个找的,对每个找到的匹配询问用户该如何处理,根据你的回答它再做出下个动作。下面这张表列出了所有可能的回答和相应。
输入 | 响应 |
---|---|
SPC 或者 y | 替换当前匹配并前进到下一个匹配处 |
DEL 或者 n | 忽略此次匹配并前进到下一个匹配处 |
. | 替换当前匹配并退出 |
, | 替换当前匹配并停在此处,再按y后前进 |
! | 替换所有剩余匹配 |
^ | 回到前一个匹配处 |
RET 或者 q | 直接退出 |
e | 修改新字符串 |
C-r | 进入递归编辑状态 |
C-w | 删除当前匹配并进入递归编辑状态 |
C-M-c | 退出递归编辑状态,返回查找替换 |
C-] | 退出递归编辑状态,同时退出查找替换 |
C-h | 显示帮助 |
这些命令中,相对y,n,q用的会多点。但如果按了任何其它的键都会退出查找替换状态,不小心退出后还能使用C-x ESC ESC返回。
最后说下什么是递归编辑状态,简单的说就是你在查找替换的过程中突然发现需要修改点东西,但又不想直接退出查找替换,这时Emacs提供了一个临时的编辑状态可以让你先干刚想起的事,等你做完了还可以回到查找替换状态,这个临时的状态就叫递归编辑状态。
注意看,这时mode line中的模式名外面加了一层方括号"[]",标明进入了递归编辑状态。干完事后使用C-M-c 就可以回到前一个状态,实际上,在任何时候都可以用命令 recursive-edit 进入递归编辑,用abort-recursive-edit 或者top-level 退出,也可以用C-M-c 返回前一状态。
小结:
按键 | 命令 | 作用 |
---|---|---|
C-s | isearch-forward | 向前进行增量查找 |
C-r | isearch-backward | 向后进行增量查找 |
M-c | | (查找状态)切换大写敏感 |
C-j | newline-and-indent | (查找状态)输入换行符 |
M-Tab | isearch-complete | (查找状态)自动匹配 |
C-h C-h | | (查找状态)进入查找帮助 |
C-w | | (查找状态)将光标处单词复制到查找区域 |
C-y | | (查找状态)将光标处直到行尾内容复制到查找区域 |
M-y | | (查找状态)把kill 环中最后一项复制到查找区域 |
C-M-w | | (查找状态)删除查找区域最后一个字符 |
C-M-y | | (查找状态)将光标处字符复制到查找区域最后 |
C-f | | (查找状态)将光标处字符复制到查找区域最后 |
C-s RET | search-forward | 向前进行简单查找 |
C-r RET | search-backward | 向后进行简单查找 |
M-s w | isearch-forward-word | 向前进行词组查找 |
M-s w RET | word-search-forward | 向前进行词组查找(非增量方式) |
M-s w C-r RET | word-search-backward | 向后进行词组查找(非增量方式) |
C-M-s | isearch-forward-regexp | 向前进行正则查找 |
C-M-r | isearch-backward-regexp | 向后进行正则查找 |
| replace-string | 全文替换 |
| replace-regexp | 全文正则替换 |
M-% | query-replace | 查找替换 |
| recursive-edit | 进入递归编辑 |
| abort-recursive-edit | 退出递归编辑 |
| top-level | 退出递归编辑 |
变量 | 作用 |
---|---|
case-replace | 设置大小写自动替换 |
case-fold-search | 设置大小写匹配查找 |