35.7 搜索与替换

若需要在缓冲区的某一区域内查找正则表达式的所有匹配项并进行替换,最灵活的方式是编写显式循环,结合使用 re-search-forwardreplace-match,示例如下:

(while (re-search-forward "foo[ \t]+bar" nil t)
  (replace-match "foobar"))

有关 replace-match 的说明,参见 See Replacing the Text that Matched.

若需要将替换操作限定在特定区域内,可使用函数 replace-regexp-in-region

Function: replace-regexp-in-region regexp replacement &optional start end

该函数在缓冲区文本的 startend 区域内,将所有 regexp 匹配项替换为 replacementstart 默认为光标位置,end 默认为缓冲区最后一个可访问位置。对 regexp 的搜索区分大小写,replacement 插入时不改变大小写。replacement 字符串可使用与 replace-match 相同的以 ‘\’ 开头的特殊转义元素。函数返回被替换的匹配次数,若未找到 regexp 则返回 nil。该函数会保留光标位置。

(replace-regexp-in-region "foo[ \t]+bar" "foobar")
Function: replace-string-in-region string replacement &optional start end

该函数功能与 replace-regexp-in-region 类似,但搜索与替换的对象为字面字符串 string,而非正则表达式。

Emacs 同样提供专门用于在字符串中替换匹配项的函数。

Function: replace-regexp-in-string regexp rep string &optional fixedcase literal subexp start

该函数复制 string,并在其中搜索 regexp 的匹配项,将其替换为 rep,返回修改后的副本。若 startnil,则从该索引位置开始在 string 中搜索匹配项,且返回值不包含 stringstart 个字符。若需要获取完整的转换后字符串,可将 stringstart 个字符与返回值拼接。

该函数使用 replace-match 执行替换,并将可选参数 fixedcaseliteralsubexp 传递给 replace-match

rep 也可以是一个函数而非字符串。此时 replace-regexp-in-string 会对每个匹配项调用该函数,并将匹配文本作为唯一参数传入。函数会收集 rep 的返回值,并将其作为替换字符串传递给 replace-match。此时的匹配数据为 regexpstring 子串匹配的结果。

Function: string-replace from-string to-string in-string

该函数将 in-string 中所有出现的 from-string 替换为 to-string,并返回结果。函数可能直接返回参数本身、常量字符串或新字符串。替换区分大小写,且忽略文本属性。

若需要编写类似 query-replace 的命令,可使用 perform-replace 完成核心工作。

Function: perform-replace from-string replacements query-flag regexp-flag delimited-flag &optional repeat-count map start end backward region-noncontiguous-p

该函数是 query-replace 及相关命令的核心实现。它在 startend 位置的文本中搜索 from-string 的出现位置,并替换部分或全部匹配项。若 startnil(或省略),则使用光标位置;end 默认为缓冲区可访问部分的末尾。(若可选参数 backwardnil,则从 end 开始反向搜索。)

query-flagnil,则替换所有匹配项;否则逐一向用户询问处理方式。

regexp-flagnil,则 from-string 被视为正则表达式;否则按字面字符串匹配。若 delimited-flagnil,则仅替换位于单词边界之间的匹配项。

参数 replacements 指定替换内容。若为字符串,则直接使用该字符串;也可以是字符串列表,按循环顺序依次使用。

replacements 为 cons 单元格 (function . data),则表示每次匹配后调用 function 获取替换文本。该函数接收两个参数:data 与已完成的替换次数。

repeat-countnil,则应为整数,指定 replacements 列表中每个字符串在循环切换至下一个前被使用的次数。

from-string 包含大写字母,则 perform-replace 会将 case-fold-search 绑定为 nil,并直接使用 replacements 而不修改其大小写。

通常,按键映射 query-replace-map 定义了交互查询时的有效用户响应。若参数 mapnil,则使用该按键映射替代 query-replace-map

nilregion-noncontiguous-p 表示 startend 之间的区域由不连续的片段组成,最常见的例子是矩形区域,各片段由换行符分隔。

该函数使用两个变量指定的函数之一搜索下一个 from-stringreplace-re-search-functionreplace-search-function。前者在参数 regexp-flagnil 时调用,后者在其为 nil 时调用。

Variable: query-replace-map

该变量保存一个专用按键映射,定义了 perform-replace 及其调用命令、y-or-n-pmap-y-or-n-p 的有效用户响应。该映射有两个特殊之处:

  • 按键绑定并非命令,而是对使用该映射的函数有意义的符号。
  • 不支持前缀键;每个按键绑定必须对应单事件按键序列。因为相关函数不使用 read-key-sequence 获取输入,而是读取单个事件并 “手动(by hand)” 查找绑定。

以下是 query-replace-map 中有意义的绑定,其中部分仅对 query-replace 及其同类命令有效。

act

执行当前考虑的操作——即 “是(yes)”。

skip

不对当前项执行操作——即 “否(no)”。

exit

对当前问题回答 “否(no)”,并终止整个问答序列,后续均按 “否(no)” 处理。

exit-prefix

exit 类似,但将按下的按键加入 unread-command-events(see 事件输入的杂项功能)。

act-and-exit

对当前问题回答 “是(yes)”,并终止整个问答序列,后续均按 “否(no)” 处理。

act-and-show

对当前问题回答 “是(yes)”,但显示结果,暂不进入下一个问题。

automatic

对当前及后续所有问题自动回答 “是(yes)”,无需用户进一步交互。

backup

回到上一个询问过的位置。

undo

撤销上一次替换,并回到该替换执行的位置。

undo-all

撤销所有替换,并回到第一次替换执行的位置。

edit

进入递归编辑处理当前问题,替代原本应执行的操作。

edit-replacement

在迷你缓冲区中编辑当前项的替换内容。

delete-and-edit

删除当前考虑的文本,然后进入递归编辑进行替换。

recenter
scroll-up
scroll-down
scroll-other-window
scroll-other-window-down

执行指定的窗口滚动操作,然后重新询问当前问题。仅 y-or-n-p 及相关函数使用该响应。

quit

立即执行退出操作。仅 y-or-n-p 及相关函数使用该响应。

help

显示帮助信息,然后重新询问。

Variable: multi-query-replace-map

该变量保存一个按键映射,在 query-replace-map 基础上扩展了多缓冲区替换场景下的额外按键绑定,新增绑定如下:

automatic-all

对当前及后续所有缓冲区中的所有问题自动回答 “是(yes)”,无需用户交互。

exit-current

对当前问题回答 “否(no)”,终止当前缓冲区的全部问答序列,继续处理下一个缓冲区。

Variable: replace-search-function

该变量指定 perform-replace 用于搜索下一个待替换字符串的函数,默认值为 search-forward。其他取值应为接收 3 个参数的函数名,参数与 search-forward 的前三个参数一致(see 字符串搜索)。

Variable: replace-re-search-function

该变量指定 perform-replace 用于搜索下一个待替换正则表达式的函数,默认值为 re-search-forward。其他取值应为接收 3 个参数的函数名,参数与 re-search-forward 的前三个参数一致(see 正则表达式搜索)。