1 Star 0 Fork 3

rookieagle/Elisp

forked from advanceflow/Elisp 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
35-搜索和匹配.org 89.08 KB
一键复制 编辑 原始数据 按行查看 历史
advanceflow 提交于 2022-05-28 10:12 . finish s2

35 搜索和匹配

GNU Emacs 提供了两种在缓冲区中搜索指定文本的方法:精确字符串搜索和正则表达式搜索。 在正则表达式搜索之后,您可以检查匹配数据以确定哪些文本与整个正则表达式或其各个部分匹配。

‘skip-chars…’ 函数也执行一种搜索。 请参阅跳过字符。 要搜索字符属性的更改,请参阅文本属性搜索函数。

35.1 搜索字符串

这些是在缓冲区中搜索文本的原始函数。 它们旨在用于程序中,但您可以交互地调用它们。 如果您这样做,他们会提示您输入搜索字符串; 参数 limit 和 noerror 为 nil,repeat 为 1。有关交互式搜索的更多详细信息,请参阅 The GNU Emacs Manual 中的 Searching and Replacement。

如果缓冲区是多字节的,这些搜索函数会将搜索字符串转换为多字节; 如果缓冲区是单字节的,它们会将搜索字符串转换为单字节。 请参阅文本表示。

Command: search-forward string &optional limit noerror count ¶

此函数从点向前搜索字符串的精确匹配。 如果成功,它会将 point 设置为找到的事件的末尾,并返回 point 的新值。 如果未找到匹配项,则值和副作用取决于 noerror(见下文)。

在以下示例中,点最初位于行首。 然后 (search-forward “fox”) 在 ‘fox’ 的最后一个字母之后移动点:



  ---------- Buffer: foo ----------
  ∗The quick brown fox jumped over the lazy dog.
  ---------- Buffer: foo ----------


  (search-forward "fox")
	   ⇒ 20

  ---------- Buffer: foo ----------
  The quick brown fox∗ jumped over the lazy dog.
  ---------- Buffer: foo ----------

参数限制指定搜索的边界,并且应该是当前缓冲区中的一个位置。 在该位置之后不接受任何匹配。 如果 limit 被省略或为零,则默认为缓冲区可访问部分的末尾。

当搜索失败时会发生什么取决于 noerror 的值。 如果 noerror 为 nil,则发出搜索失败错误信号。 如果 noerror 为 t,则向前搜索返回 nil 并且什么也不做。 如果 noerror 既不是 nil 也不是 t,则向前搜索移动指向上界并返回 nil。

参数 noerror 仅影响无法找到匹配项的有效搜索。 不管 noerror 是什么,无效的参数都会导致错误。

如果count是正数n,则搜索n次; 每个连续的搜索都从前一个匹配的结尾开始。 如果所有这些连续搜索成功,则函数调用成功,移动点并返回其新值。 否则函数调用失败,结果取决于 noerror 的值,如上所述。 如果 count 是负数 -n,则在相反(向后)方向上进行 n 次搜索。

Command: search-backward string &optional limit noerror count ¶

此函数从点向后搜索字符串。 它类似于向前搜索,只是它向后搜索而不是向前搜索。 向后搜索在匹配开始时离开点。

Command: word-search-forward string &optional limit noerror count ¶

此函数从点向前搜索字符串的单词匹配。 如果找到匹配项,则将 point 设置为找到的匹配项的末尾,并返回 point 的新值。

单词匹配将字符串视为单词序列,而忽略分隔它们的标点符号。 它在缓冲区中搜索相同的单词序列。 每个单词在缓冲区中必须是不同的(搜索单词 ‘ball’ 与单词 ‘balls’ 不匹配),但忽略标点和空格的细节(搜索 ‘ball boy’ 匹配 ‘ball.boy!’ )。

在本例中,point 最初位于缓冲区的开头; 搜索将其留在“y”和“!”之间。



  ---------- Buffer: foo ----------
  ∗He said "Please!  Find
  the ball boy!"
  ---------- Buffer: foo ----------


  (word-search-forward "Please find the ball, boy.")
	   ⇒ 39

  ---------- Buffer: foo ----------
  He said "Please!  Find
  the ball boy∗!"
  ---------- Buffer: foo ----------

如果limit是非零,它必须是当前缓冲区中的一个位置; 它指定搜索的上限。 找到的匹配不得超出该位置。

如果 noerror 为 nil,则 word-search-forward 会在搜索失败时发出错误信号。 如果 noerror 为 t,则它返回 nil 而不是发出错误信号。 如果 noerror 既不是 nil 也不是 t,它将点移动到限制(或缓冲区可访问部分的末尾)并返回 nil。

如果 count 是一个正数,它指定要搜索的连续出现的次数。 点位于最后一场比赛的末尾。 如果 count 是负数,则向后搜索,并且 point 位于最后一个匹配项的开头。

在内部,word-search-forward 和相关函数使用函数 word-search-regexp 将字符串转换为忽略标点符号的正则表达式。

Command: word-search-forward-lax string &optional limit noerror count ¶

此命令与 word-search-forward 相同,除了字符串的开头或结尾不需要匹配单词边界,除非字符串以空格开头或结尾。 例如,搜索“ball boy”匹配“ball boyee”,但不匹配“balls boy”。

Command: word-search-backward string &optional limit noerror count ¶

此函数从点向后搜索与字符串匹配的单词。 这个函数就像 word-search-forward 一样,只是它向后搜索并且通常在匹配的开头留下点。

Command: word-search-backward-lax string &optional limit noerror count ¶

此命令与 word-search-backward 相同,只是字符串的开头或结尾不需要匹配单词边界,除非字符串以空格开头或结尾。

35.2 搜索和案例

默认情况下,Emacs 中的搜索会忽略正在搜索的文本的大小写; 如果您指定搜索“FOO”,则“Foo”或“foo”也被视为匹配项。 这也适用于正则表达式; 因此,“[aB]”将匹配“a”或“A”或“b”或“B”。

如果您不想要此功能,请将变量 case-fold-search 设置为 nil。 然后所有字母必须完全匹配,包括大小写。 这是一个缓冲区局部变量; 更改变量仅影响当前缓冲区。 (请参阅缓冲区局部变量简介。)或者,您可以更改默认值。 在 Lisp 代码中,您通常会使用 let 将 case-fold-search 绑定到所需的值。

请注意,用户级增量搜索功能以不同方式处理大小写区别。 当搜索字符串只包含小写字母时,搜索忽略大小写,但当搜索字符串包含一个或多个大写字母时,搜索变为区分大小写。 但这与 Lisp 代码中使用的搜索功能无关。 请参阅 GNU Emacs 手册中的增量搜索。

User Option: case-fold-search ¶

此缓冲区局部变量确定搜索是否应忽略大小写。 如果变量为 nil,它们不会忽略大小写; 否则(默认情况下)他们会忽略大小写。

User Option: case-replace ¶

此变量确定更高级别的替换函数是否应保留大小写。 如果变量为 nil,则意味着逐字使用替换文本。 非零值意味着根据被替换的文本转换替换文本的大小写。

该变量通过将其作为参数传递给函数 replace-match 来使用。 请参阅替换匹配的文本。

35.3 正则表达式

正则表达式,或简称 regexp,是表示一组(可能是无限的)字符串的模式。 搜索正则表达式的匹配项是一项非常强大的操作。 本节介绍如何编写正则表达式; 以下部分说明如何搜索它们。

对于正则表达式的交互式开发,可以使用 Mx re-builder 命令。 它通过在单独的缓冲区中提供即时视觉反馈,为创建正则表达式提供了一个方便的界面。 当您编辑正则表达式时,它在目标缓冲区中的所有匹配项都会突出显示。 正则表达式的每个带括号的子表达式都以不同的面显示,这使得即使是非常复杂的正则表达式也更容易验证。

请注意,默认情况下 Emacs 搜索忽略大小写(请参阅搜索和大小写)。 要启用区分大小写的正则表达式搜索和匹配,请将 case-fold-search 绑定到 nil 您希望区分大小写的代码周围。

35.3.1 正则表达式的语法

正则表达式有一种语法,其中一些字符是特殊结构,其余的都是普通的。 一个普通的字符是一个简单的正则表达式,它只匹配那个字符而不是别的。 特殊字符有’.’、’*’、’+’、’?’、’[‘、’^’、’$’和’'; 将来不会定义新的特殊字符。 如果字符 ‘]’ 结束替代字符,则它是特殊的(见下文)。 字符“-”在替代字符中是特殊的。 ‘[:’ 和平衡 ‘:]’ 将字符类包含在字符替代项中。 正则表达式中出现的任何其他字符都是普通字符,除非它前面有一个“\”。

例如,’f’不是特殊字符,所以是普通字符,因此’f’是匹配字符串’f’而不匹配其他字符串的正则表达式。 (它不匹配字符串’fg’,但它匹配该字符串的一部分。)同样,’o’ 是一个只匹配’o’ 的正则表达式。

任何两个正则表达式 a 和 b 都可以连接。 结果是一个正则表达式,如果 a 匹配某个字符串的开头部分并且 b 匹配字符串的其余部分,则匹配该字符串。

作为一个简单的例子,我们可以连接正则表达式’f’和’o’来得到正则表达式’fo’,它只匹配字符串’fo’。 还是微不足道的。 为了做一些更强大的事情,你需要使用一种特殊的正则表达式结构。

35.3.1.1 正则表达式中的特殊字符

以下是正则表达式中特殊字符的列表。

‘.’ (Period) ¶

是一个特殊字符,匹配除换行符以外的任何单个字符。 使用连接,我们可以制作像 ‘a.b’ 这样的正则表达式,它匹配任何以 ‘a’ 开头并以 ‘b’ 结尾的三个字符的字符串。

‘*’ ¶

本身不是一个构造; 它是一个后缀运算符,表示尽可能多地重复匹配前面的正则表达式。 因此,“o*”匹配任意数量的“o”(包括没有“o”)。

’*’ 始终适用于尽可能小的前面表达式。 因此,“fo*”有一个重复的“o”,而不是一个重复的“fo”。 它匹配“f”、“fo”、“foo”等。

匹配器通过立即匹配尽可能多的重复来处理“*”构造。 然后它继续模式的其余部分。 如果失败了,就会发生回溯,丢弃“*”修饰结构的一些匹配项,希望这样可以匹配模式的其余部分。 例如,在匹配 ‘ca*ar’ 和字符串 ‘caaar’ 时,’a*’ 首先尝试匹配所有三个 ‘a’; 但是模式的其余部分是 ‘ar’ 并且只剩下 ‘r’ 可以匹配,所以这个尝试失败了。 下一个替代方案是让 ‘a*’ 仅匹配两个 ‘a’。 选择此选项后,正则表达式的其余部分将成功匹配。

‘+’ ¶

是一个后缀运算符,类似于’*’,但它必须至少匹配前面的表达式一次。 因此,例如,’ca+r’ 匹配字符串 ‘car’ 和 ‘caaaar’ 但不匹配字符串 ‘cr’,而 ‘ca*r’ 匹配所有三个字符串。

‘?’ ¶

是一个后缀运算符,类似于’*’,除了它必须匹配前面的表达式一次或根本不匹配。 例如,’ca?r’ 匹配 ‘car’ 或 ‘cr’; 没有其他的。

‘*?’, ‘+?’, ‘??’ ¶

是运算符 ‘*’、’+’ 和 ‘?’ 的非贪婪变体。 这些运算符匹配最大可能的子字符串(与匹配整个包含表达式一致),非贪婪变体匹配最小可能的子字符串(与匹配整个包含表达式一致)。

例如,正则表达式 ‘c[ad]*a’ 在应用于字符串 ‘cdaaada’ 时匹配整个字符串; 但是应用于同一字符串的正则表达式“c[ad]*?a”只匹配“cda”。 (这里允许整个表达式匹配的 ‘[ad]*?’ 的最小可能匹配是 ‘d’。)

‘[ … ]’ ¶

是一个替代字符,以“[”开头,以“]”结尾。 在最简单的情况下,两个括号之间的字符就是这个替代字符可以匹配的字符。

因此,’[ad]’ 匹配一个 ‘a’ 或一个 ‘d’,而 ‘[ad]*’ 匹配任何仅由 ‘a’s 和 ‘d’s 组成的字符串(包括空字符串)。 ‘c[ad]*r’ 匹配 ‘cr’、’car’、’cdr’、’caddaar’ 等。

您还可以在替代字符中包含字符范围,方法是在起始字符和结束字符之间用“-”书写。 因此,’[az]’ 匹配任何小写的 ASCII 字母。 范围可以与单个字符自由混合,如 ‘[az$%.]’,它匹配任何小写 ASCII 字母或 ‘$’、’%’ 或句点。 但是,一个范围的结束字符不应该是另一个范围的起点; 例如,应该避免使用“[amz]”。

替代字符还可以指定命名字符类(请参阅字符类)。 这是一个 POSIX 功能。 例如,’:ascii:’ 匹配任何 ASCII 字符。 使用一个字符类相当于提到该类中的每个字符; 但后者在实践中是不可行的,因为有些类包含数千个不同的字符。 字符类不应显示为范围的下限或上限。

通常的正则表达式特殊字符在字符替代中并不特殊。 一组完全不同的字符是特殊的:’]’、’-’ 和 ‘^’。 要在替代字符中包含“]”,请将其放在开头。 要包含“^”,请将其放在除开头之外的任何位置。 要包括“-”,请将其放在末尾。 因此,’[]^-]’ 匹配所有这三个特殊字符。 您不能使用 ‘' 转义这三个字符,因为 ‘' 在这里并不特殊。

范围的以下方面特定于 Emacs,因为 POSIX 允许但不要求这种行为,并且 Emacs 以外的程序可能会有不同的行为:

如果 case-fold-search 不为 nil,则 ‘[az]’ 也匹配大写字母。 范围不受语言环境的排序顺序影响:它始终表示代码点介于其边界之间的字符集,因此 ‘[az]’ 仅匹配 ASCII 字母,即使在 C 或 POSIX 语言环境之外。 如果范围的下限大于其上限,则该范围为空且不代表任何字符。 因此,’[za]’ 总是无法匹配,而 ‘[^za]’ 匹配任何字符,包括换行符。 但是,颠倒的范围应该始终是从字母“z”到字母“a”,以表明它不是拼写错误; 例如,应该避免使用“[+-*/]”,因为它只匹配“/”而不是可能的四个字符。 如果范围的端点是原始 8 位字节(请参阅文本表示),或者如果范围开始是 ASCII 并且结束是原始字节(如在 ‘[a-\377]’ 中),则范围将匹配只有 ASCII 字符和原始 8 位字节,但不是非 ASCII 字符。 此功能旨在搜索单字节缓冲区和字符串中的文本。

某些类型的字符替代不是最好的样式,即使它们在 Emacs 中具有明确的含义。 它们包括:

尽管范围的界限几乎可以是任何字符,但最好保持在 ASCII 字母和数字的自然序列内,因为大多数人没有记住字符代码表。 例如,“[.-9]”不如“[./0-9]”清晰,“[`-~]”不如“[`az{|}~]”清晰。 Unicode 字符转义在这里可以提供帮助; 例如,对于大多数程序员来说,“[ก-ฺ฿-๛]”不如“[\u0E01-\u0E3A\u0E3F-\u0E5B]”清晰。 尽管字符替代可以包含重复,但最好避免它们。 例如,“[XYa-yYb-zX]”不如“[XYa-z]”清晰。 虽然一个范围只能表示一个、两个或三个字符,但列出这些字符更简单。 例如,“[a-a0]”不如“[a0]”清晰,“[ij]”不如“[ij]”清晰,“[ik]”不如“[ijk]”清晰. 尽管“-”可以出现在替代字符的开头或作为范围的上限,但最好将“-”单独放在替代字符的末尾。 例如,虽然 ‘[-az]’ 是有效的,但 ‘[az-]’ 是更好的样式; 尽管 ‘[*–]’ 有效,但 ‘[*+,-]’ 更清晰。

‘[^ … ]’ ¶

’[^’ 开始一个补充字符替代。 这匹配除指定字符之外的任何字符。 因此,’[^a-z0-9A-Z]’ 匹配除 ASCII 字母和数字之外的所有字符。

’^’ 在替代字符中并不特殊,除非它是第一个字符。 ‘^’ 后面的字符被视为第一个字符(换句话说,’-’ 和 ‘]’ 在那里并不特殊)。

补充字符替代可以匹配换行符,除非换行符被提及为不匹配的字符之一。 这与 grep 等程序中正则表达式的处理形成对比。

您可以指定命名字符类,就像在字符替代中一样。 例如,’[^[:ascii:]]’ 匹配任何非 ASCII 字符。 请参阅字符类。

‘^’ ¶

匹配缓冲区时,’^’ 匹配空字符串,但仅在被匹配文本的行首(或缓冲区可访问部分的开头)。 否则它无法匹配任何东西。 因此,’^foo’ 匹配出现在行首的 ‘foo’。

当匹配字符串而不是缓冲区时,’^’ 匹配字符串的开头或换行符之后。

出于历史兼容性的原因,’^’ 只能用在正则表达式的开头,或者在 ‘\(‘、’\(?:’ 或 ‘\|’ 之后。

‘$’ ¶

类似于 ‘^’ 但仅匹配行尾(或缓冲区可访问部分的末尾)。 因此,’x+$’ 匹配行尾有一个或多个 ‘x’ 的字符串。

当匹配字符串而不是缓冲区时,’$’ 匹配字符串末尾或换行符之前。

出于历史兼容性的原因,’$’ 只能用在正则表达式的末尾,或者在 ‘\)’ 或 ‘\|’ 之前。

‘\’ ¶

有两个功能:它引用特殊字符(包括’'),它引入了额外的特殊结构。

因为’'引用了特殊字符,’\$’是一个只匹配’$’的正则表达式,’\[‘是一个只匹配’[‘的正则表达式,以此类推。

请注意,’' 在 Lisp 字符串的读取语法中也有特殊含义(请参阅字符串类型),并且必须用 ‘' 引用。 例如,匹配“\”字符的正则表达式是“\”。 要编写一个包含字符 ‘\’ 的 Lisp 字符串,Lisp 语法要求您用另一个 ‘' 引用每个 ‘'。 因此,匹配“\”的正则表达式的读取语法是“\\”。

请注意:为了历史兼容性,如果特殊字符在其特殊含义没有意义的上下文中,它们将被视为普通字符。 例如,’*foo’ 将 ‘*’ 视为普通的,因为没有前面的表达式可以让 ‘*’ 起作用。 依赖这种行为是不好的做法; 无论如何都要引用特殊字符,无论它出现在哪里。

由于 ‘' 在替代字符中并不特殊,因此它永远无法删除 ‘-’ 或 ‘]’ 的特殊含义。 因此,当它们没有特殊含义时,您也不应该引用这些字符。 这不会澄清任何事情,因为反斜杠可以合法地放在这些具有特殊含义的字符之前,例如 ‘[^\]’ (“[^\]” 用于 Lisp 字符串语法),它匹配除反斜杠之外的任何单个字符。

在实践中,正则表达式中出现的大多数 ‘]’ 都关闭了替代字符,因此是特殊的。 但是,有时正则表达式可能会尝试匹配文字“[”和“]”的复杂模式。 在这种情况下,有时可能需要从头开始仔细分析正则表达式,以确定哪些方括号包含替代字符。 例如,’[^][]]’ 由补码替代字符 ‘[^][]’(匹配任何不是方括号的单个字符),后跟文字 ‘]’。

确切的规则是,在正则表达式的开头,’[’ 是特殊的,而 ‘]’ 不是。 这一直持续到第一个未引用的’[‘,之后我们处于字符替代状态; ‘[’ 不再特殊(除非它开始一个字符类),但 ‘]’ 是特殊的,除非它紧跟特殊的 ‘[’ 或 ‘[’ 后跟一个 ‘^’。 这一直持续到下一个不结束字符类的特殊“]”。 这结束了字符替代并恢复了正则表达式的普通语法; 未加引号的 ‘[’ 又是特殊的,而 ‘]’ 则不是。

35.3.1.2 字符类

下表列出了您可以在字符替代中使用的类,以及它们的含义。 请注意,包含类名的 ‘[’ 和 ‘]’ 字符是名称的一部分,因此使用这些类的正则表达式还需要一对括号。 例如,匹配一个或多个字母和数字序列的正则表达式将是 ’:alnum:+’,而不是 ‘[:alnum:]+’。

‘[:ascii:]’

这匹配任何 ASCII 字符(代码 0–127)。

‘[:alnum:]’

这匹配任何字母或数字。 对于多字节字符,它匹配 Unicode ‘general-category’ 属性(请参阅字符属性)指示它们是字母或十进制数字字符的字符。

‘[:alpha:]’

这匹配任何字母。 对于多字节字符,它匹配其 Unicode ‘general-category’ 属性(请参阅字符属性)指示它们是字母字符的字符。

‘[:blank:]’

这与 Unicode 技术标准 #18 的附件 C 中定义的水平空格相匹配。 特别是,它匹配空格、制表符和其他字符,其 Unicode ‘general-category’ 属性(请参阅字符属性)表明它们是间距分隔符。

‘[:cntrl:]’

这匹配代码在 0-31 范围内的任何字符。

‘[:digit:]’

这匹配“0”到“9”。 因此,’[-+[:digit:]]’ 匹配任何数字,以及 ‘+’ 和 ‘-‘。

‘[:graph:]’

这匹配图形字符——除空格、ASCII 和非 ASCII 控制字符、代理项和 Unicode 未分配的代码点之外的所有字符,如 Unicode ‘general-category’ 属性所示(请参阅字符属性)。

‘[:lower:]’

这匹配任何由当前大小写表确定的小写字母(请参阅案例表)。 如果 case-fold-search 不为零,则它也匹配任何大写字母。

‘[:multibyte:]’

这匹配任何多字节字符(请参阅文本表示)。

‘[:nonascii:]’

这匹配任何非 ASCII 字符。

‘[:print:]’

这匹配任何打印字符——空格或由 ‘[:graph:]’ 匹配的图形字符。

‘[:punct:]’

这匹配任何标点符号。 (目前,对于多字节字符,它匹配任何具有非单词语法的内容。)

‘[:space:]’

这匹配任何具有空格语法的字符(请参阅语法类表)。

‘[:unibyte:]’

这匹配任何单字节字符(请参阅文本表示)。

‘[:upper:]’

这匹配任何大写字母,由当前大小写表确定(请参阅案例表)。 如果 case-fold-search 不为零,则它也匹配任何小写字母。

‘[:word:]’

这匹配任何具有单词语法的字符(请参阅语法类表)。

‘[:xdigit:]’

这匹配十六进制数字:“0”到“9”、“a”到“f”和“A”到“F”。

35.3.1.3 正则表达式中的反斜杠结构

大多数情况下,’' 后跟任何字符都只匹配该字符。 但是,有几个例外:某些以 ‘' 开头的具有特殊含义的序列。 这是一个特殊的“\”结构表。

‘\|’ ¶

指定替代方案。 两个带有 ‘\|’ 的正则表达式 a 和 b 在中间形成一个匹配任何 a 或 b 匹配的表达式的表达式。

因此,’foo\|bar’ 匹配 ‘foo’ 或 ‘bar’ 但不匹配其他字符串。

’\|’ 适用于最大可能的周围表达式。 只有周围的 ‘\( … \)’ 分组才能限制 ‘\|’ 的分组能力。

如果您需要完整的回溯功能来处理 ‘\|’ 的多次使用,请使用 POSIX 正则表达式函数(请参阅 POSIX 正则表达式搜索)。

‘\{m\}’

是一个后缀运算符,它恰好重复前一个模式 m 次。 因此,’x\{5\}’ 与字符串 ‘xxxxx’ 匹配,仅此而已。 ‘c[ad]\{3\}r’ 匹配字符串,例如 ‘caaar’、’cdddr’、’cadar’ 等。

‘\{m,n\}’

是一个更通用的后缀运算符,它指定最少 m 次重复和最多 n 次重复的重复。 如果省略 m,则最小值为 0; 如果 n 被省略,则没有最大值。 对于这两种形式,如果指定,m 和 n 不得大于 2**16 - 1 。

例如,’c[ad]\{1,2\}r’ 匹配字符串 ‘car’、’cdr’、’caar’、’cadr’、’cdar’ 和 ‘cddr’,仅此而已。 ‘\{0,1\}’ 或 ‘\{,1\}’ 等价于 ‘?’。 ‘\{0,\}’ 或 ‘\{,\}’ 等价于 ‘*’。 ‘\{1,\}’ 等价于 ‘+’。

‘\( … \)’ ¶

是一个用于三个目的的分组结构:

包含一组 ‘\|’ 其他操作的替代方案。 因此,正则表达式 ‘\(foo\|bar\)x’ 匹配 ‘foox’ 或 ‘barx’。 为后缀运算符 ‘*’、’+’ 和 ‘?’ 括起来一个复杂的表达式 操作。 因此,’ba\(na\)*’ 匹配 ‘ba’、’bana’、’banana’、’bananana’ 等,以及任意数量(零个或更多)的 ‘na’ 字符串。 使用 ‘\digit’ 记录匹配的子字符串以供将来参考(见下文)。

最后一个应用不是括号分组概念的结果。 它是一个单独的特征,作为第二个含义分配给同一个 ‘\( … \)’ 结构,因为在实践中,这两个含义之间通常没有冲突。 但偶尔会发生冲突,这导致引入了害羞的群体。

‘\(?: … \)’ ¶

是害羞的群体结构。 shy 组服务于普通组的前两个目的(控制其他运算符的嵌套),但它没有得到数字,所以你不能用 ‘\digit’ 引用它的值。 害羞组对于机械构造的正则表达式特别有用,因为它们可以自动添加而无需更改普通非害羞组的编号。

害羞组也称为非捕获组或未编号组。

是明确编号的组结构。 普通组会根据他们的位置隐含地获得他们的号码,这可能很不方便。 此构造允许您强制使用特定的组号。 编号没有特别的限制,例如,您可以有多个具有相同编号的组,在这种情况下,最后匹配的一组(即最右边的匹配)将获胜。 隐式编号的组总是得到大于任何前一组的最小整数。

‘\(?num: … \)’

匹配与分组 (‘\( … \)’) 构造的第 digit 次出现匹配的相同文本。

换句话说,在一个组结束后,匹配器会记住该组匹配的文本的开头和结尾。 稍后在正则表达式中,您可以使用 ‘' 后跟数字来匹配相同的文本,无论它可能是什么。

与传递给搜索或匹配函数的整个正则表达式中出现的前九个分组结构相匹配的字符串按照左括号在正则表达式中出现的顺序分配编号 1 到 9。 因此,您可以使用 ‘\1’ 到 ‘\9’ 来引用由相应分组结构匹配的文本。

例如,’\(.*\)\1’ 匹配任何由两个相同部分组成的无换行符字符串。 ‘\(.*\)’ 匹配前半部分,可以是任何内容,但后面的 ‘\1’ 必须匹配完全相同的文本。

如果一个 ‘\( … \)’ 构造匹配不止一次(这可能发生,例如,如果它后跟 ‘*’),则只记录最后一个匹配。

如果正则表达式中的特定分组结构从未匹配过——例如,如果它出现在未使用的替代项中,或者出现在重复零次的重复项中——则相应的 ‘\digit’ 结构永远不会匹配任何内容. 举一个人为的例子,’\(foo\(b*\)\|lose\)\2’ 不能匹配 ‘lose’:较大组内的第二个选择匹配它,但是 ‘\2’ 是未定义的并且可以不匹配任何东西。 但它可以匹配 ‘foobb’,因为第一个替代匹配 ‘foob’ 而 ‘\2’ 匹配 ‘b’。

‘\w’ ¶

匹配任何单词组成字符。 编辑器语法表确定这些是哪些字符。 请参阅语法表。

‘\W’ ¶

匹配任何不是单词成分的字符。

‘\scode’ ¶

匹配任何语法为代码的字符。 这里的 code 是一个表示语法代码的字符:因此,’w’ 表示单词组成,’-’ 表示空格,’(’ 表示左括号等。要表示空格语法,请使用 ‘-’ 或空格字符。有关语法代码和代表它们的字符的列表,请参阅语法类表。

‘\Scode’ ¶

匹配语法不是代码的任何字符。

‘\cc’

匹配任何类别为 c 的字符。 这里 c 是一个表示类别的字符:因此,在标准类别表中,“c”表示中文字符或“g”表示希腊字符。 您可以使用 Mx describe-categories RET 查看所有当前定义的类别列表。 除了使用 define-category 函数的标准类别之外,您还可以定义自己的类别(请参阅类别)。

‘\Cc’

匹配任何类别不是 c 的字符。

以下正则表达式构造匹配空字符串——也就是说,它们不使用任何字符——但它们是否匹配取决于上下文。 总而言之,缓冲区可访问部分的开头和结尾被视为缓冲区的实际开头和结尾。

‘\`’ ¶

匹配空字符串,但仅在要匹配的缓冲区或字符串的开头。

‘\'’ ¶

匹配空字符串,但仅在要匹配的缓冲区或字符串的末尾。

‘\=’ ¶

匹配空字符串,但仅在点。 (与字符串匹配时未定义此构造。)

‘\b’ ¶

匹配空字符串,但仅在单词的开头或结尾。 因此,’\bfoo\b’ 将任何出现的 ‘foo’ 匹配为单独的单词。 ‘\bballs?\b’ 匹配 ‘ball’ 或 ‘balls’ 作为单独的词。

‘\b’ 匹配缓冲区(或字符串)的开头或结尾,无论它旁边出现什么文本。

‘\B’ ¶

匹配空字符串,但不在单词的开头或结尾,也不在缓冲区(或字符串)的开头或结尾。

‘\<’ ¶

匹配空字符串,但仅在单词的开头。 ‘\<’ 匹配缓冲区(或字符串)的开头,仅当后面有单词组成字符时。

‘\>’ ¶

匹配空字符串,但仅在单词的末尾。 ‘\>’ 仅当内容以单词组成字符结尾时才匹配缓冲区(或字符串)的末尾。

‘\_<’ ¶

匹配空字符串,但仅在符号的开头。 符号是一个或多个单词或符号组成字符的序列。 ‘\_<’ 仅在符号组成字符后跟在缓冲区(或字符串)的开头匹配。

‘\_>’ ¶

匹配空字符串,但仅在符号的末尾。 ‘\_>’ 仅当内容以符号组成字符结尾时才匹配缓冲区(或字符串)的末尾。

并非每个字符串都是有效的正则表达式。 例如,以替代字符结尾而没有终止“]”的字符串是无效的,以单个“\”结尾的字符串也是如此。 如果将无效的正则表达式传递给任何搜索函数,则会发出无效正则表达式错误信号。

35.3.2 复杂正则表达式示例

这是一个复杂的正则表达式,以前 Emacs 使用它来识别句子的结尾以及后面的任何空格。 (现在 Emacs 使用类似但更复杂的默认正则表达式,由函数 sentence-end 构造。请参阅编辑中使用的标准正则表达式。)

下面,我们首先将正则表达式显示为 Lisp 语法中的字符串(以区分空格和制表符),然后显示计算结果。 字符串常量以双引号开始和结束。 ‘"’ 表示双引号作为字符串的一部分,’\’ 表示反斜杠作为字符串的一部分,’\t’ 表示制表符,’\n’ 表示换行符。

"[.?!][]\"')}]*\\($\\| $\\|\t\\|  \\)[ \t\n]*"
     ⇒ "[.?!][]\"')}]*\\($\\| $\\|  \\|  \\)[
]*"

在输出中,制表符和换行符显示为它们本身。

这个正则表达式依次包含四个部分,可以破译如下:

[.?!]

模式的第一部分是与以下三个字符中的任何一个匹配的替代字符:句点、问号和感叹号。 匹配必须以这三个字符之一开始。 (这是 Emacs 使用的新的默认正则表达式与旧的不同的一点。新值还允许一些非 ASCII 字符结束一个句子而没有任何后续空格。)

[]\"')}]*

模式的第二部分匹配任何右大括号和引号,其中零个或多个,可能跟在句号、问号或感叹号之后。 " 是字符串中双引号的 Lisp 语法。末尾的 ‘*’ 表示紧接在前面的正则表达式(在这种情况下是字符替代)可以重复零次或多次。

\\($\\| $\\|\t\\|  \\)

模式的第三部分匹配句子结尾之后的空格:行尾(可选带空格)、制表符或两个空格。 双反斜杠将括号和竖线标记为正则表达式语法; 括号分隔组,竖线分隔备选方案。 美元符号用于匹配行尾。

[ \t\n]*

最后,模式的最后一部分匹配超出结束句子所需的最小空格的任何额外空格。

在 rx 表示法中(参见 The rx Structured Regexp Notation),正则表达式可以写成

(rx (any ".?!")                    ; Punctuation ending sentence.
    (zero-or-more (any "\"')]}"))  ; Closing quotes or brackets.
    (or line-end
	  (seq " " line-end)
	  "\t"
	  "  ")                      ; Two spaces.
    (zero-or-more (any "\t\n ")))  ; Optional extra whitespace.

由于 rx 正则表达式只是 S 表达式,因此可以对其进行格式化和注释。

35.3.3 该 rx结构化正则表达式表示法

作为基于字符串的语法的替代方案,Emacs 提供了基于 Lisp S 表达式的结构化 rx 表示法。 这种表示法通常比正则表达式字符串更易于阅读、编写和维护,并且可以自由缩进和注释。 它需要转换为字符串形式,因为这是正则表达式函数所期望的,但是这种转换通常发生在字节编译期间,而不是在运行使用正则表达式的 Lisp 代码时发生。

这是一个匹配 C 编程语言中的块注释的 rx regexp21:

(rx "/*"                          ; Initial /*
    (zero-or-more
     (or (not (any "*"))          ;  Either non-*,
	   (seq "*"                 ;  or * followed by
		(not (any "/")))))  ;  non-/
    (one-or-more "*")             ; At least one star,
    "/")                          ; and the final /

或者,使用更短的同义词并且写得更紧凑,

(rx "/*"
    (* (| (not "*")
	    (: "*" (not "/"))))
    (+ "*") "/")

在传统的字符串语法中,它会写成

"/\\*\\(?:[^*]\\|\\*[^/]\\)*\\*+/"

rx 符号主要在 Lisp 代码中有用; 它不能用于请求正则表达式的大多数交互式情况,例如运行 query-replace-regexp 或变量自定义时。

35.3.3.1 构造 rx正则表达式

rx 正则表达式中的各种形式如下所述。 简写 rx 表示任何 rx 形式,而 rx… 表示零个或多个 rx 形式。 这些都是 rx 宏的有效参数。 在给出相应的字符串正则表达式语法的情况下,A、B、……是字符串正则表达式子表达式。 字面量

?C

从字面上匹配字符串’some-string’。 与字符串正则表达式不同,没有具有特殊含义的字符。

(seq rx…) ¶

从字面上匹配字符“C”。

顺序和替代

(sequence rx…)
(: rx…)
(and rx…)
"some-string"

按顺序匹配 rxs。 没有参数,表达式匹配空字符串。 对应的字符串正则表达式:’AB…’(按顺序排列的子表达式)。

(or rx…) ¶
(| rx…)

完全匹配其中一个 rx。 如果所有参数都是如此受约束的字符串、字符或形式,则将始终使用最长的匹配项。 否则,将使用最长的匹配或第一个(从左到右的顺序)。 没有参数,表达式根本不会匹配任何东西。 对应的字符串正则表达式:’A\|B\|…’。

unmatchable ¶

拒绝任何比赛。 相当于(或)。 请参阅 regexp-unmatchable。

重复

通常,重复形式是贪婪的,因为它们试图匹配尽可能多的次数。 有些形式是非贪婪的; 他们尝试尽可能少地匹配(请参阅非贪婪重复)。

(zero-or-more rx…) ¶
(0+ rx…)

匹配 rxs 零次或多次。 默认贪婪。 对应字符串正则表达式:’A*’(贪婪),’A*?’ (非贪婪)

(one-or-more rx…) ¶
(1+ rx…)

匹配 rxs 一次或多次。 默认贪婪。 对应字符串正则表达式:’A+’(贪婪)、’A+?’ (非贪婪)

(zero-or-one rx…) ¶
(optional rx…)
(opt rx…)

匹配一次 rxs 或一个空字符串。 默认贪婪。 对应的字符串正则表达式:’A?’ (贪婪),’A??’ (非贪婪)。

(* rx…) ¶

匹配 rxs 零次或多次。 贪婪的。 对应的字符串正则表达式:’A*’

(+ rx…) ¶

匹配 rxs 一次或多次。 贪婪的。 对应的字符串正则表达式:’A+’

(? rx…) ¶

匹配一次 rxs 或一个空字符串。 贪婪的。 对应的字符串正则表达式:’A?’

(*? rx…) ¶

匹配 rxs 零次或多次。 不贪心。 对应的字符串正则表达式:’A*?’

(+? rx…) ¶

匹配 rxs 一次或多次。 不贪心。 对应的字符串正则表达式:’A+?’

(?? rx…) ¶

匹配 rxs 或空字符串。 不贪心。 对应的字符串正则表达式:’A??’

(= n rx…)
(repeat n rx)

将 rxs 精确匹配 n 次。 对应字符串正则表达式:’A\{n\}’

(>= n rx…) ¶

匹配 rxs n 次或更多次。 贪婪的。 对应字符串正则表达式:’A\{n,\}’

(** n m rx…) ¶
(repeat n m rx…)

匹配 rxs 至少 n 次但不超过 m 次。 贪婪的。 对应字符串正则表达式:’A\{n,m\}’

一些重复形式的贪婪可以使用以下结构来控制。 但是,当需要这种匹配时,通常最好使用上面的显式非贪婪形式。

(minimal-match rx) ¶

匹配 rx,与零或多、0+、一或多、1+、零或一、选择和可选使用非贪婪匹配。

(maximal-match rx) ¶

匹配 rx,与零或多个、0+、一个或多个、1+、零或一、opt 和 optional 使用贪婪匹配。 这是默认设置。

匹配单个字符

(any set…) ¶
(char set…)
(in set…)

匹配其中一组中的单个字符。 每个集合都是一个字符、一个表示其字符集的字符串、一个范围或一个字符类(见下文)。 范围可以是连字符分隔的字符串,例如“AZ”,也可以是字符的 cons,例如 (?A . ?Z)。

请注意,连字符 (-) 在此构造中的字符串中是特殊的,因为它充当范围分隔符。 要包含连字符,请将其添加为单独的字符或单字符串。 对应的字符串正则表达式:’[…]’

(not charspec) ¶

匹配未包含在 charspec 中的字符。 charspec 可以是字符、单字符串、any、not 或 or、交集、语法或类别形式,或字符类。 如果 charspec 是一个 or 形式,它的参数具有与交集相同的限制; 见下文。 对应字符串正则表达式:’[^…]’, ‘\Scode’, ‘\Ccode’

(intersection charset…) ¶

匹配所有字符集中包含的字符。 每个字符集可以是一个字符、一个单字符串、一个没有字符类的任何形式、一个交集,或者不是其参数也是字符集的形式。

not-newline, nonl ¶

匹配除换行符以外的任何字符。 对应的字符串正则表达式:’.’ (点)

anychar, anything ¶

匹配任何字符。 对应字符串正则表达式:’.\|\n’(例如)

character class ¶

匹配命名字符类中的字符:

alpha, alphabetic, letter

匹配字母字符。 更准确地说,匹配 Unicode ‘general-category’ 属性表明它们是字母的字符。

alnum, alphanumeric

匹配字母字符和数字。 更准确地说,匹配其 Unicode ‘general-category’ 属性表明它们是字母或十进制数字的字符。

digit, numeric, num

匹配数字“0”-“9”。

xdigit, hex-digit, hex

匹配十六进制数字 ‘0’-‘9’、’A’-‘F’ 和 ‘a’-‘f’。

cntrl, control

匹配代码在 0-31 范围内的任何字符。

blank

匹配水平空格。 更准确地说,匹配其 Unicode ‘general-category’ 属性表明它们是间距分隔符的字符。

space, whitespace, white

匹配任何具有空格语法的字符(参见语法类表)。

lower, lower-case

匹配任何小写字母,由当前大小写表确定。 如果 case-fold-search 不为零,则它也匹配任何大写字母。

upper, upper-case

匹配任何大写字母,由当前大小写表确定。 如果 case-fold-search 不为零,则它也匹配任何小写字母。

graph, graphic

匹配除空格、ASCII 和非 ASCII 控制字符、代理项和 Unicode 未分配的代码点以外的任何字符,如 Unicode ‘general-category’ 属性所示。

print, printing

匹配空格或图形匹配的字符。

punct, punctuation

匹配任何标点符号。 (目前,对于多字节字符,任何具有非单词语法的东西。)

word, wordchar

匹配任何具有单词语法的字符(参见语法类表)。

ascii

匹配任何 ASCII 字符(代码 0–127)。

nonascii

匹配任何非 ASCII 字符(但不匹配原始字节)。

对应字符串正则表达式:’:class:’ (语法语法)¶

匹配具有语法语法的字符,是以下名称之一: 语法名称 语法字符 空格 - 标点符号。 w 字 象征 _ 开括号 ( 右括号) 表达式前缀 ’ 字符串引用“ 成对分隔符 $ 逃脱 \ 字符引用 / 评论开始 < 评论结束> 字符串分隔符 | 评论分隔符!

有关详细信息,请参阅语法类表。 请注意 (syntax punctuation) 不等同于字符类标点符号。 对应的字符串正则表达式:’\schar’ 其中 char 是语法字符。 (类别类别)¶

匹配类别类别中的字符,该字符可以是以下名称之一或其类别字符。 类别名称 类别字符 空格换缩进空格 根据 。 辅音 0 基元音 1 上变音符号 2 低变音符 3 音标4 符号 5 数字 6 元音修饰变音标记 7 元音符号 8 半元音低 9 不在行尾 < 不在行首 > 字母数字双字节 A 中文双字节 C 希腊两字节 G 日文平假名两字节 H 印度两字节我 日文片假名两字节 K 强从左到右 L 韩文-韩文-两字节 N 从右到左的强 R 西里尔文双字节 Y 组合变音符号^ ASCII码 阿拉伯语 b 中国语訳 埃塞俄比亚 希腊语 韩语 印度人我 日本人 日文片假名 k 拉丁语 老澳 藏族 日罗马 r 泰国 越南 v 希伯来语 w 西里尔字母 y 可以打破|

有关当前定义的类别的更多信息,请运行命令 Mx describe-categories RET。 有关如何定义新类别,请参阅类别。 对应的字符串正则表达式:’\cchar’ 其中 char 是类别字符。

零宽度断言

这些都匹配空字符串,但仅在特定位置。

线开始,bol ¶

在一行的开头匹配。 对应字符串正则表达式:’^’ 行尾,eol ¶

在行尾匹配。 对应字符串正则表达式:’$’ 字符串开始,bos,缓冲区开始,机器人¶

在要匹配的字符串或缓冲区的开头匹配。 对应字符串正则表达式:’`’ 字符串端,eos,缓冲端,eot ¶

在要匹配的字符串或缓冲区的末尾匹配。 对应字符串正则表达式:’'’ 观点 ¶

匹配点。 对应字符串正则表达式:’\=’ 单词开始,鞠躬¶

匹配单词的开头。 对应字符串正则表达式:’\<’ 词尾,eow ¶

匹配词尾。 对应字符串正则表达式:’\>’ 词边界¶

匹配单词的开头或结尾。 对应字符串正则表达式:’\b’ 非词边界¶

匹配除单词开头或结尾之外的任何位置。 对应的字符串正则表达式:’\B’ 符号开始¶

匹配符号的开头。 对应的字符串正则表达式:’\_<’ 符号结束¶

匹配符号的末尾。 对应字符串正则表达式:’\_>’

捕获组

(组 rx…) ¶ (子匹配 rx…)

匹配 rxs,使匹配的文本和位置在匹配数据中可访问。 正则表达式中的第一组编号为 1; 到目前为止,后续组的编号将比该模式中先前编号最高的组高一个。 对应的字符串正则表达式:’\(…\)’ (组-n n rx…) ¶ (子匹配-n n rx…)

与 group 类似,但明确分配组编号 n。 n 必须为正。 对应字符串正则表达式:’\(?n:…\)’ (backref n) ¶

匹配先前由组号 n 匹配的文本。 n 必须在 1–9 范围内。 对应字符串正则表达式:’\n’

动态包容

(文字表达式)¶

匹配作为评估 Lisp 表达式 expr 的结果的文字字符串。 评估发生在调用时,在当前的词汇环境中。 (正则表达式) ¶ (正则表达式)

匹配作为评估 Lisp 表达式 expr 的结果的字符串正则表达式。 评估发生在调用时,在当前的词汇环境中。 (评估表达式) ¶

匹配作为评估 Lisp 表达式 expr 的结果的 rx 形式。 在当前全局环境中,评估发生在 rx 的宏扩展时、rx-to-string 的调用时。

35.3.3.2 函数和宏使用 rx正则表达式

宏:rx rx-form… ¶

将 rx-forms 转换为字符串正则表达式,就好像它们是 (seq …) 表单的主体一样。 rx 宏扩展为字符串常量,或者,如果使用文字或正则表达式形式,则为计算结果为字符串的 Lisp 表达式。 例子:

     (rx (+ alpha) "=" (+ digit))
	⇒ "[[:alpha:]]+=[[:digit:]]+"

功能:rx-to-string rx-expr &optional no-group ¶

将 rx-expr 转换为返回的字符串正则表达式。 如果 no-group 不存在或为零,则将结果括在非捕获组中,’\(?:…\)’,如有必要,以确保附加到它的后缀运算符将应用于整个表达式。 例子:

     (rx-to-string '(seq (+ alpha) "=" (+ digit)) t)
	⇒ "[[:alpha:]]+=[[:digit:]]+"

rx-expr 中文字和正则表达式形式的参数必须是字符串文字。

pcase 宏可以直接使用 rx 表达式作为模式; 请参阅 pcase 中的 rx。

有关将用户定义的扩展添加到 rx 表示法的机制,请参阅定义新的 rx 形式。

35.3.3.3 定义新的 rx形式

可以通过根据其他 rx 表达式定义新符号和参数化形式来扩展 rx 符号。 这对于在多个正则表达式之间共享部分非常方便,并且通过将它们从较小的部分组合在一起来使复杂的部分更容易构建和理解。

例如,您可以将 name 定义为表示(一个或多个字母),并将 (quoted x) 定义为表示任何 x 的 (seq ?’ x ?’)。 然后这些形式可以像任何其他形式一样在 rx 表达式中使用: (rx (quoted name)) 将匹配单引号内的非空字母序列。

下面的 Lisp 宏提供了将名称绑定到定义的不同方式。 它们的共同点是以下规则:

内置的 rx 形式,如数字和组,不能重新定义。 这些定义存在于它们自己的名称空间中,与 Lisp 变量的名称空间分开。 因此,无需在名称上附加 -regexp 之类的后缀; 它们不能与其他任何东西发生碰撞。 定义不能递归地、直接或间接地引用自己。 如果你发现自己需要这个,你需要一个解析器,而不是正则表达式。 定义仅在对 rx 或 rx-to-string 的调用中被扩展,而不仅仅是通过它们在定义宏中的存在。 这意味着定义的顺序无关紧要,即使它们相互引用也是如此,并且语法错误仅在使用它们时出现,而不是在定义它们时出现。 任何需要任意 rx 表达式的地方都允许使用用户定义的形式; 例如,在零或一表单的主体中,但不在任何或类别表单内。 它们也可以在非和交叉形式中使用。

宏:rx-define name [arglist] rx-form ¶

在对 rx 和 rx-to-string 的所有后续调用中全局定义名称。 如果 arglist 不存在,则 name 被定义为要替换为 rx-form 的普通符号。 例子:

  (rx-define haskell-comment (seq "--" (zero-or-more nonl)))
  (rx haskell-comment)
	   ⇒ "--.*"

如果 arglist 存在,它必须是零个或多个参数名称的列表,然后将 name 定义为参数化形式。 当在 rx 表达式中用作 (name arg…) 时,每个 arg 将替换 rx-form 中相应的参数名称。

arglist 可以以 &rest 和一个最后的参数名称结尾,表示一个 rest 参数。 其余参数将扩展为 arglist 中任何其他参数都不匹配的所有额外实际参数值,并在它出现的地方拼接到 rx-form 中。 例子:

  (rx-define moan (x y &rest r) (seq x (one-or-more y) r "!"))
  (rx (moan "MOO" "A" "MEE" "OW"))
	   ⇒ "MOOA+MEEOW!"

由于定义是全局的,因此建议为 name 提供包前缀以避免名称与其他地方的定义发生冲突,这在命名非局部变量和函数时很常见。

以这种方式定义的表单仅执行简单的模板替换。 对于任意计算,将它们与 rx 形式 eval、regexp 或 literal 一起使用。 例子:

     (defun n-tuple-rx (n element)
	`(seq "<"
	      (group-n 1 ,element)
	      ,@(mapcar (lambda (i) `(seq ?, (group-n ,i ,element)))
			(number-sequence 2 n))
	      ">"))
     (rx-define n-tuple (n element) (eval (n-tuple-rx n 'element)))
     (rx (n-tuple 3 (+ (in "0-9"))))
	⇒ "<\\(?1:[0-9]+\\),\\(?2:[0-9]+\\),\\(?3:[0-9]+\\)>"

宏:rx-let (bindings…) body… ¶

使绑定中的 rx 定义在本地可用于 body 中的 rx 宏调用,然后对其进行评估。

bindings 的每个元素都在表单上 (name [arglist] rx-form),其中各部分的含义与上面的 rx-define 中的相同。 例子:

     (rx-let ((comma-separated (item) (seq item (0+ "," item)))
	       (number (1+ digit))
	       (numbers (comma-separated number)))
	(re-search-forward (rx "(" numbers ")")))

这些定义仅在主体的宏扩展期间可用,因此在编译代码的执行期间不存在。

rx-let 不仅可以在函数内部使用,还可以在顶层包含需要共享一组通用 rx 形式的全局变量和函数定义。 由于名称在正文中是本地的,因此不需要任何包前缀。 例子:

     (rx-let ((phone-number (seq (opt ?+) (1+ (any digit ?-)))))
	(defun find-next-phone-number ()
	  (re-search-forward (rx phone-number)))
	(defun phone-number-p (string)
	  (string-match-p (rx bos phone-number eos) string)))

rx-let 绑定的范围是词法的,这意味着它们在 body 本身之外是不可见的,即使在从 body 调用的函数中也是如此。

宏:rx-let-eval 绑定体… ¶

像在 rx-let 中一样评估绑定列表的绑定,并使用那些对 rx-to-string 的调用有效的绑定来评估 body。

这个宏类似于 rx-let,除了绑定参数被评估(因此如果它是一个列表文字需要被引用),并且定义在运行时被替换,这是 rx-to-string 所必需的工作。 例子:

     (rx-let-eval
	  '((ponder (x) (seq "Where have all the " x " gone?")))
	(looking-at (rx-to-string
		     '(ponder (or "flowers" "young girls"
				  "left socks")))))

与 rx-let 的另一个区别是绑定是动态范围的,因此也可以在从 body 调用的函数中使用。 但是,它们在 body 中定义的函数内部是不可见的。

35.3.4 正则表达式函数

这些函数对正则表达式进行操作。

功能:正则表达式引用字符串 ¶

此函数返回一个正则表达式,其唯一完全匹配的是字符串。 仅当缓冲区中的下一个字符是字符串时,在查看中使用此正则表达式才会成功; 如果正在搜索的文本包含字符串,则在搜索函数中使用它会成功。 请参阅正则表达式搜索。

这允许您在调用需要正则表达式的函数时请求精确的字符串匹配或搜索。

  (regexp-quote "^The cat$")
	   ⇒ "\\^The cat\\$"

regexp-quote 的一种用途是将精确的字符串匹配与描述为正则表达式的上下文结合起来。 例如,这将搜索作为 string 值的字符串,由空格包围:

(re-search-forward
 (concat "\\s-" (regexp-quote string) "\\s-"))

如果返回的字符串不包含任何特殊字符,则它可能是字符串本身。

功能:regexp-opt 字符串 &optional paren ¶

此函数返回一个有效的正则表达式,它将匹配列表字符串中的任何字符串。 当您需要尽可能快地进行匹配或搜索时,这很有用 - 例如,对于字体锁定模式 22。

如果字符串是空列表,则返回值是一个从不匹配任何内容的正则表达式。

可选参数 paren 可以是以下任何一种:

一个字符串

生成的正则表达式前面是paren,后面是’\)’,例如,使用’”\(?1:”’ 来生成一个明确编号的组。 字

生成的正则表达式被 ‘\<\(’ 和 ‘\)\>’ 包围。 符号

生成的正则表达式被 ‘\_<\(’ 和 ‘\)\_>’ 包围(这在匹配编程语言关键字等时通常是合适的)。 非零

生成的正则表达式被 ‘\(’ 和 ‘\)’ 包围。 零

如果有必要确保附加到它的后缀运算符将应用于整个表达式,则生成的正则表达式被 ‘\(?:’ 和 ‘\)’ 包围。

返回的正则表达式的排序方式使其始终匹配可能的最长字符串。

在重新排序之前,regexp-opt 的结果正则表达式等效于但通常比简化版本更有效:

    (defun simplified-regexp-opt (strings &optional paren)
     (let ((parens
	      (cond
	       ((stringp paren)       (cons paren "\\)"))
	       ((eq paren 'words)    '("\\<\\(" . "\\)\\>"))
	       ((eq paren 'symbols) '("\\_<\\(" . "\\)\\_>"))
	       ((null paren)          '("\\(?:" . "\\)"))
	       (t                       '("\\(" . "\\)")))))
	 (concat (car parens)
		 (mapconcat 'regexp-quote strings "\\|")
		 (cdr parens))))

功能:正则表达式选择深度正则表达式¶

此函数返回正则表达式中的分组结构(带括号的表达式)的总数。 这不包括害羞组(请参阅正则表达式中的反斜杠构造)。

功能:regexp-opt-charset chars ¶

此函数返回与字符列表中的字符匹配的正则表达式。

  (regexp-opt-charset '(?a ?b ?c ?d ?e))
	   ⇒ "[a-e]"

变量:regexp-unmatchable ¶

该变量包含一个保证不匹配任何字符串的正则表达式。 它作为变量的默认值特别有用,这些变量可以设置为实际匹配的模式。

脚注 (22)

请注意,regexp-opt 不保证其结果绝对是最有效的形式。 手动调整的正则表达式有时会稍微高效一些,但几乎不值得付出努力。

35.3.5 正则表达式的问题

Emacs 正则表达式实现,和许多同类实现一样,通常是健壮的,但偶尔会以两种方式中的任何一种造成麻烦:匹配可能会耗尽内部堆栈空间并发出错误信号,并且可能需要很长时间才能完成。 以下建议将降低这些症状的可能性,并有助于缓解确实出现的问题。

使用零宽度断言(’^’ 和 `)将正则表达式锚定在行、字符串或缓冲区的开头。 这利用了实现中的快速路径,并且可以避免徒劳的匹配尝试。 其他零宽度断言也可能通过导致匹配提前失败来带来好处。 避免使用或模式来支持字符替代:写 ‘[ab]’ 而不是 ‘a\|b’。 回想一下,’\s-’ 和 ‘\sw’ 分别等同于 ’:space:’ 和 ’:word:‘。 由于 or 模式的最后一个分支不会在堆栈上添加回溯点,因此请考虑将最可能匹配的模式放在最后。 例如,如果尝试匹配很长的 ‘a’ 字符串,’^\(?:a\|.b\)*c’ 将耗尽堆栈,但等效的 ‘^\(?:.b\|a \)*c’ 不会。

(这是一个折衷:成功匹配的 or 模式运行得更快,首先匹配最频繁的模式。) 尽量确保文本的任何部分只能以单一方式匹配。 例如,’a*a*’ 将匹配与 ‘a*’ 相同的字符串集,但前者可以通过多种方式进行匹配,因此如果稍后匹配失败,将导致回溯缓慢。 如果可能,使 or-pattern 分支互斥,以便匹配在失败之前不会进入多个分支。

对嵌套重复要特别小心:在存在歧义的情况下,它们很容易导致非常慢的匹配。 例如,’\(?:a*b*\)+c’ 将花费很长时间来尝试匹配中等长度的 ‘a’ 字符串,然后才会失败。 等效的 ‘\(?:a\|b\)*c’ 更快,而 ‘[ab]*c’ 更好。 除非确实需要,否则不要使用捕获组; 也就是说,使用 ‘\(?:…\)’ 而不是 ‘\(…\)’ 进行括号括起来。 考虑使用 rx(请参阅 The rx Structured Regexp Notation); 它可以自动优化一些或模式,除非明确要求,否则永远不会引入捕获组。

如果尽管遵循了上述建议,但仍遇到正则表达式堆栈溢出,请不要害怕在多个函数调用中执行匹配,每个函数调用都使用更简单的正则表达式,可以更轻松地包含回溯。

35.4 正则表达式搜索

在 GNU Emacs 中,您可以递增或不递增地搜索正则表达式的下一个匹配项(请参阅正则表达式语法)。 有关增量搜索命令,请参阅 GNU Emacs 手册中的正则表达式搜索。 这里我们只描述程序中有用的搜索功能。 主要的是重新搜索。

如果缓冲区是多字节的,这些搜索函数会将正则表达式转换为多字节; 如果缓冲区是单字节的,它们会将正则表达式转换为单字节。 请参阅文本表示。

命令:re-search-forward regexp &optional limit noerror count ¶

此函数在当前缓冲区中向前搜索与正则表达式 regexp 匹配的文本字符串。 该函数跳过任何数量的正则表达式不匹配的文本,并在找到的第一个匹配项的末尾留下点。 它返回点的新值。

如果 limit 不为零,则它必须是当前缓冲区中的一个位置。 它指定搜索的上限。 在该位置之后不接受任何匹配。 如果 limit 被省略或为零,则默认为缓冲区可访问部分的末尾。

搜索失败时 re-search-forward 的作用取决于 noerror 的值:

发出搜索失败错误信号。 吨

什么都不做,返回 nil。 还要别的吗

将点移动到限制(或缓冲区可访问部分的末尾)并返回 nil。

参数 noerror 仅影响无法找到匹配项的有效搜索。 不管 noerror 是什么,无效的参数都会导致错误。

如果count是正数n,则搜索n次; 每个连续的搜索都从前一个匹配的结尾开始。 如果所有这些连续搜索成功,则函数调用成功,移动点并返回其新值。 否则函数调用失败,结果取决于 noerror 的值,如上所述。 如果 count 是负数 -n,则在相反(向后)方向上进行 n 次搜索。

在以下示例中,点最初位于“T”之前。 评估搜索调用将点移动到该行的末尾(在 ‘hat’ 的 ‘t’ 和换行符之间)。



  ---------- Buffer: foo ----------
  I read "∗The cat in the hat
  comes back" twice.
  ---------- Buffer: foo ----------


  (re-search-forward "[a-z]+" nil t 5)
	   ⇒ 27

  ---------- Buffer: foo ----------
  I read "The cat in the hat∗
  comes back" twice.
  ---------- Buffer: foo ----------

命令:re-search-backward regexp &optional limit noerror count ¶

此函数在当前缓冲区中向后搜索与正则表达式 regexp 匹配的文本字符串,将 point 留在找到的第一个文本的开头。

此功能类似于重新搜索转发,但它们不是简单的镜像。 re-search-forward 查找起点尽可能接近起点的匹配。 如果re-search-backward是一个完美的镜像,它会找到末端尽可能接近的匹配。 然而,实际上它会找到开始尽可能接近(但在起点之前结束)的匹配。 原因是在给定位置匹配正则表达式总是从头到尾工作,并且从指定的开始位置开始。

re-search-forward 的真正镜像将需要一个特殊的功能来匹配正则表达式从头到尾。 不值得为实现它而烦恼。

功能:字符串匹配正则表达式字符串&可选开始¶

此函数返回字符串中正则表达式 regexp 的第一个匹配项的开始索引,如果没有匹配项,则返回 nil。 如果 start 不为零,则搜索从字符串中的该索引开始。

例如,

(string-match
 "quick" "The quick brown fox jumped quickly.")
     ⇒ 4

(string-match
 "quick" "The quick brown fox jumped quickly." 8)
     ⇒ 27

字符串第一个字符的索引为 0,第二个字符的索引为 1,以此类推。

如果此函数找到匹配项,则匹配项之外的第一个字符的索引可用作 (match-end 0)。 请参阅匹配数据。

(string-match
 "quick" "The quick brown fox jumped quickly." 8)
     ⇒ 27

(match-end 0)
     ⇒ 32

功能:string-match-p 正则表达式 string &optional start ¶

这个谓词函数做了字符串匹配所做的事情,但它避免了修改匹配数据。

功能:查看正则表达式¶

此函数确定当前缓冲区中紧跟 point 的文本是否与正则表达式 regexp 匹配。 “直接跟随”的意思就是:搜索是“锚定的”,只有从跟随点的第一个字符开始才能成功。 如果是,则结果为 t,否则为 nil。

此函数不会移动点,但会更新匹配数据。 请参阅匹配数据。 如果您需要在不修改匹配数据的情况下测试匹配,请使用 looking-at-p,如下所述。

在此示例中,点直接位于“T”之前。 如果它在其他任何地方,结果将为零。

  ---------- Buffer: foo ----------
  I read "∗The cat in the hat
  comes back" twice.
  ---------- Buffer: foo ----------

  (looking-at "The cat in the hat$")
	   ⇒ t

功能:回溯正则表达式限制 &optional greedy ¶

如果正则表达式匹配点之前的文本(即在点结束),则此函数返回 t,否则返回 nil。

因为正则表达式匹配只能向前工作,所以这是通过从点向后搜索以点结束的匹配来实现的。 如果它必须搜索很长的距离,那可能会很慢。 您可以通过为limit指定一个非零值来限制所需的时间,这表示在限制之前不要搜索。 在这种情况下,找到的匹配必须在 limit 处或之后开始。 这是一个例子:



  ---------- Buffer: foo ----------
  I read "∗The cat in the hat
  comes back" twice.
  ---------- Buffer: foo ----------

  (looking-back "read \"" 3)
	   ⇒ t
  (looking-back "read \"" 4)
	   ⇒ nil

如果 greedy 不为零,则此函数尽可能向后扩展匹配,当单个附加的前一个字符不能成为正则表达式匹配的一部分时停止。 当比赛延长时,允许其起始位置出现在限制之前。

作为一般建议,尽量避免使用回溯,因为它很慢。 出于这个原因,没有计划添加回顾-p 功能。

功能:查看-p regexp ¶

此谓词函数的工作方式类似于查看,但不更新匹配数据。

变量:搜索空间正则表达式 ¶

如果这个变量不是零,它应该是一个正则表达式,说明如何搜索空格。 在这种情况下,正在搜索的正则表达式中的任何一组空格都代表使用该正则表达式。 但是,诸如 ‘[…]’ 和 ‘*’、’+’、’?’ 等结构内部的空格 不受搜索空间正则表达式的影响。

由于此变量影响所有正则表达式搜索和匹配结构,因此您应该将其临时绑定到尽可能小的代码部分。

35.5 POSIX正则表达式搜索

通常的正则表达式函数在需要处理’\|’时进行回溯 和重复结构,但他们只会继续这样做,直到找到一些匹配。 然后他们成功并报告找到的第一个匹配项。

本节介绍替代搜索函数,这些函数执行 POSIX 标准为正则表达式匹配指定的完整回溯。 他们继续回溯,直到他们尝试了所有可能性并找到了所有匹配项,因此他们可以按照 POSIX 的要求报告最长的匹配项。 这要慢得多,因此仅在您确实需要最长匹配时才使用这些函数。

POSIX 搜索和匹配函数不能正确支持非贪婪重复运算符(请参阅非贪婪)。 这是因为 POSIX 回溯与非贪婪重复的语义冲突。

命令:posix-search-forward regexp &optional limit noerror count ¶

这类似于 re-search-forward,只是它执行 POSIX 标准为正则表达式匹配指定的完整回溯。

命令:posix-search-backward regexp &optional limit noerror count ¶

这类似于 re-search-backward,只是它执行 POSIX 标准为正则表达式匹配指定的完整回溯。

功能:posix-looking-at 正则表达式 ¶

这就像查看一样,只是它执行 POSIX 标准为正则表达式匹配指定的完整回溯。

功能:posix-string-match 正则表达式字符串 &optional start ¶

这类似于字符串匹配,只是它执行 POSIX 标准为正则表达式匹配指定的完整回溯。

35.6 比赛数据

Emacs 跟踪搜索过程中找到的文本段的开始和结束位置; 这称为匹配数据。 借助匹配数据,您可以搜索复杂的模式,例如邮件消息中的日期,然后在模式的控制下提取部分匹配。

因为匹配数据通常只描述最近的搜索,所以您必须注意不要在您希望返回的搜索和匹配数据的使用之间无意中进行另一次搜索。 如果您无法避免另一次介入搜索,则必须保存并恢复其周围的匹配数据,以防止其被覆盖。

请注意,所有函数都可以覆盖匹配数据,除非明确记录不这样做。 结果是在后台隐式运行的函数(请参阅延迟执行的计时器和空闲计时器)可能应该显式地保存和恢复匹配数据。

35.6.1 替换匹配的文本

此函数替换上次搜索匹配的全部或部分文本。 它通过匹配数据工作。

功能:replace-match 替换 &optional 固定大小写字符串 subexp ¶

此函数对缓冲区或字符串执行替换操作。

如果您在缓冲区中进行了最后一次搜索,则应省略字符串参数或为其指定 nil,并确保当前缓冲区是您执行最后一次搜索的缓冲区。 然后这个函数编辑缓冲区,用替换替换匹配的文本。 它在替换文本的末尾留下点。

如果您对字符串执行了最后一次搜索,请传递与字符串相同的字符串。 然后这个函数返回一个新字符串,其中匹配的文本被替换替换。

如果 fixedcase 不为 nil,则 replace-match 使用替换文本而不进行大小写转换; 否则,它会根据要替换的文本的大小写来转换替换文本。 如果原始文本全部大写,这会将替换文本转换为大写。 如果原始文本的所有单词都大写,则替换文本的所有单词都大写。 如果所有单词都是一个字母并且它们都是大写的,则它们被视为大写单词而不是全部大写单词。

如果literal 不是nil,那么替换会按原样插入,唯一的更改是根据需要更改大小写。 如果它是 nil(默认值),那么字符 ‘' 会被特殊处理。 如果替换中出现“\”,则它必须是以下序列之一的一部分:

’\&’ ¶

这代表整个文本被替换。 ‘\n’,其中 n 是一个数字 ¶

这代表与原始正则表达式中的第 n 个子表达式匹配的文本。 子表达式是分组在 ‘\(…\)’ 中的那些表达式。 如果第 n 个子表达式从不匹配,则替换为空字符串。 ‘\’ ¶

这代表替换文本中的单个“\”。 ‘\?’

这代表它本身(为了与 replace-regexp 和相关命令的兼容性;请参阅 The GNU Emacs Manual 中的 Regexp Replace)。

‘' 后面的任何其他字符都表示错误。

’\&’ 和 ‘\n’ 执行的替换发生在大小写转换(如果有)之后。 因此,它们替换的字符串永远不会进行大小写转换。

如果 subexp 不为零,则表示仅替换匹配的正则表达式的子表达式编号 subexp,而不是整个匹配。 例如,匹配 ‘foo \(ba*r\)’ 后,调用 replace-match with 1 as subexp 意味着只替换匹配 ‘\(ba*r\)’ 的文本。

功能:match-substitute-replacement 替换 &optional 固定大小写字符串 subexp ¶

此函数返回将通过替换匹配插入缓冲区的文本,但不修改缓冲区。 如果您想向用户展示实际的替换结果,使用 ‘\n’ 或 ‘\&’ 等结构替换匹配的组,这将非常有用。 参数替换和可选的固定大小写、文字、字符串和子表达式与替换匹配具有相同的含义。

35.6.2 简单匹配数据访问

本节说明如何使用匹配数据找出上次搜索或匹配操作匹配的内容(如果成功)。

您可以询问整个匹配文本,或正则表达式的特定括号子表达式。 下面函数中的 count 参数指定了哪个。 如果计数为零,则您正在询问整个比赛。 如果 count 是正数,它指定你想要的子表达式。

回想一下,正则表达式的子表达式是那些用转义括号“\(…\)”分组的表达式。 通过从整个正则表达式的开头计算 ‘\(’ 的出现次数来找到第 count 个子表达式。第一个子表达式编号为 1,第二个子表达式编号为 2,依此类推。只有正则表达式可以有子表达式——在简单的字符串搜索之后,唯一可用的信息是关于整场比赛的信息。

每次成功的搜索都会设置匹配数据。 因此,您应该在搜索后立即查询匹配数据,然后再调用可能执行另一次搜索的任何其他函数。 或者,您可以在调用可以执行另一个搜索的函数时保存和恢复匹配数据(请参阅保存和恢复匹配数据)。 或者使用明确不修改匹配数据的函数; 例如,字符串匹配-p。

失败的搜索可能会也可能不会改变匹配数据。 在当前的实现中,它没有,但我们将来可能会改变它。 不要试图在搜索失败后依赖匹配数据的值。

功能:匹配字符串计数&可选字符串内¶

此函数以字符串形式返回在上次搜索或匹配操作中匹配的文本。 如果 count 为零,则返回整个文本,如果 count 为正,则返回与第 count 个括号子表达式相对应的部分。

如果最后一次这样的操作是针对带有字符串匹配的字符串执行的,那么您应该传递与字符串中的参数相同的字符串。 在缓冲区搜索或匹配之后,您应该省略 in-string 或为其传递 nil; 但是您应该确保调用 match-string 时的当前缓冲区是您进行搜索或匹配的缓冲区。 不遵循此建议将导致错误的结果。

如果 count 超出范围,或者对于 ‘\|’ 内的子表达式,则值为 nil 未使用的替代方法或重复零次的重复。

功能:匹配字符串无属性计数&可选字符串内¶

此函数类似于匹配字符串,只是结果没有文本属性。

功能:匹配开始计数¶

如果最后一次正则表达式搜索找到匹配项,则此函数返回匹配文本或其子表达式的开始位置。

如果 count 为零,则该值是整个匹配的开始位置。 否则,count 指定正则表达式中的子表达式,函数的值是该子表达式匹配的起始位置。

’\|’ 中的子表达式的值为 nil 未使用的替代方法或重复零次的重复。

功能:比赛结束计数¶

此函数类似于 match-beginning,只是它返回匹配结束的位置,而不是开始的位置。

以下是使用匹配数据的示例,并带有显示文本中位置的注释:



(string-match "\\(qu\\)\\(ick\\)"
		"The quick fox jumped quickly.")
		;0123456789
     ⇒ 4


(match-string 0 "The quick fox jumped quickly.")
     ⇒ "quick"
(match-string 1 "The quick fox jumped quickly.")
     ⇒ "qu"
(match-string 2 "The quick fox jumped quickly.")
     ⇒ "ick"


(match-beginning 1)       ; The beginning of the match
     ⇒ 4                 ;   with ‘qu’ is at index 4.


(match-beginning 2)       ; The beginning of the match
     ⇒ 6                 ;   with ‘ick’ is at index 6.


(match-end 1)             ; The end of the match
     ⇒ 6                 ;   with ‘qu’ is at index 6.

(match-end 2)             ; The end of the match
     ⇒ 9                 ;   with ‘ick’ is at index 9.

这是另一个例子。 点最初位于线的开头。 搜索移动指向空格和单词’in’之间。 整个匹配的开始在缓冲区的第 9 个字符 (‘T’),第一个子表达式的匹配开始在第 13 个字符 (‘c’)。



(list
  (re-search-forward "The \\(cat \\)")
  (match-beginning 0)
  (match-beginning 1))
    ⇒ (17 9 13)


---------- Buffer: foo ----------
I read "The cat ∗in the hat comes back" twice.
	  ^   ^
	  9  13
---------- Buffer: foo ----------

(在这种情况下,返回的索引是一个缓冲区位置;缓冲区的第一个字符计为 1。)

35.6.3 访问整个比赛数据

函数 match-data 和 set-match-data 一次读取或写入整个匹配数据。

功能:匹配数据 & 可选整数重用 reseat ¶

此函数返回一个位置列表(标记或整数),这些位置记录了上次搜索匹配的文本的所有信息。 元素零是整个表达式匹配开始的位置; 元素一是表达式匹配结束的位置。 接下来的两个元素是第一个子表达式匹配的开始和结束的位置,依此类推。 一般情况下,元素编号 2n 对应(匹配开始 n); 元素编号 2n + 1 对应于 (match-end n)。

通常所有元素都是标记或零,但如果整数是非零,这意味着使用整数而不是标记。 (在这种情况下,缓冲区本身作为附加元素附加到列表的末尾,以便于完全恢复匹配数据。)如果最后一次匹配是在使用 string-match 的字符串上完成的,则始终使用整数,因为标记不能指向字符串。

如果重用不是零,它应该是一个列表。 在这种情况下,match-data 存储匹配数据以供重复使用。 也就是说,重用被破坏性地修改了。 重用不需要有正确的长度。 如果它不足以包含匹配数据,则将其扩展。 如果太长,重用的长度保持不变,但未使用的元素设置为零。 此功能的目的是减少垃圾收集的需要。

如果 reseat 不为零,则重用列表上的所有标记都将重新定位以指向无处。

与往常一样,在对搜索函数的调用和对旨在访问该搜索的匹配数据的匹配数据的调用之间不得有干预搜索的可能性。

    (match-data)
	   ⇒  (#<marker at 9 in foo>
		#<marker at 17 in foo>
		#<marker at 13 in foo>
		#<marker at 17 in foo>)

功能:set-match-data match-list &optional reseat ¶

此函数从 match-list 的元素中设置匹配数据,它应该是一个列表,它是之前调用 match-data 的值。 (更准确地说,任何具有相同格式的东西都可以使用。)

如果 match-list 引用了一个不存在的缓冲区,则不会出现错误; 以无意义但无害的方式设置匹配数据。

如果 reseat 不为零,则匹配列表列表上的所有标记都将重新定位以指向无处。

store-match-data 是 set-match-data 的半过时别名。

35.6.4 保存和恢复比赛数据

当您调用可能搜索的函数时,如果您想保留先前搜索的匹配数据以供以后使用,则可能需要保存和恢复该调用周围的匹配数据。 这是一个示例,显示了如果未能保存匹配数据会出现的问题:

(re-search-forward "The \\(cat \\)")
     ⇒ 48
(foo)                   ; foo does more searching.
(match-end 0)
     ⇒ 61              ; Unexpected result—not 48!

您可以使用 save-match-data 保存和恢复匹配数据:

宏:保存匹配数据体… ¶

这个宏执行主体,保存和恢复它周围的匹配数据。 返回值是正文中最后一个表单的值。

您可以使用 set-match-data 和 match-data 来模仿特殊形式 save-match-data 的效果。 方法如下:

 (let ((data (match-data)))
   (unwind-protect
	…   ; Ok to change the original match data.
     (set-match-data data)))

Emacs 在运行进程过滤函数(参见进程过滤函数)和进程哨兵(参见哨兵:检测进程状态更改)时会自动保存和恢复匹配数据。

35.7 搜索和替换

如果你想在缓冲区的一部分中找到一个正则表达式的所有匹配项并替换它们,最灵活的方法是使用 re-search-forward 和 replace-match 编写一个显式循环,如下所示:

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

有关替换匹配的说明,请参阅替换匹配的文本。

将替换限制在特定区域可能更方便。 函数 replace-regexp-in-region 就是这样做的。

功能:replace-regexp-in-region 正则表达式替换&可选开始结束¶

此函数将所有出现的正则表达式替换为开始和结束之间的缓冲区文本区域中的替换; start 默认为点的位置, end 默认为缓冲区的最后一个可访问位置。 regexp 的搜索区分大小写,并且在不改变其字母大小写的情况下插入替换。 替换字符串可以使用与替换匹配相同的以 ‘' 开头的特殊元素。 该函数返回被替换的次数,如果没有找到正则表达式,则返回 nil。 该函数保留点的位置。

(replace-regexp-in-region "foo[ \t]+bar" "foobar")

功能:replace-string-in-region 字符串替换&可选开始结束 ¶

此函数的工作方式类似于 replace-regexp-in-region,但搜索和替换文字字符串而不是正则表达式。

Emacs 还具有替换字符串中匹配项的特殊功能。

功能:replace-regexp-in-string regexp rep string &optional fixedcase literal subexp start ¶

此函数复制字符串并在其中搜索正则表达式的匹配项,并用 rep 替换它们。 它返回修改后的副本。 如果 start 不为零,则从 string 中的该索引开始搜索匹配项,并且返回的值不包括 string 的第一个开始字符。 要获取整个转换后的字符串,请将字符串的第一个起始字符与返回值连接起来。

此函数使用replace-match 进行替换,并将可选参数fixedcase、literal 和subexp 传递给replace-match。

rep 可以是一个函数,而不是一个字符串。 在这种情况下,replace-regexp-in-string 为每个匹配调用 rep,将匹配的文本作为其唯一参数传递。 它收集 rep 返回的值并将其作为替换字符串传递给 replace-match。 此时的匹配数据是匹配正则表达式与字符串子串的结果。

功能:将字符串替换为字符串中的字符串¶

此函数将 in-string 中所有出现的 from-string 替换为 to-string 并返回结果。 它可能会返回一个不变的参数,一个常量字符串或一个新字符串。 大小写很重要,文本属性被忽略。

如果您想按照查询替换的方式编写命令,您可以使用 perform-replace 来完成这项工作。

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

这个函数是查询替换和相关命令的核心。 它在开始和结束位置之间的文本中搜索出现的 from-string 并替换其中的部分或全部。 如果 start 为 nil(或省略),则使用 point 代替,缓冲区可访问部分的结尾用于 end。 (如果可选参数向后不为零,则搜索从末尾开始并向后。)

如果 query-flag 为 nil,则替换所有出现; 否则,它会询问用户如何处理每个问题。

如果 regexp-flag 为非 nil,则 from-string 被视为正则表达式; 否则,它必须在字面上匹配。 如果 delimited-flag 不为零,则仅考虑由单词边界包围的替换。

参数replaces 指定用什么替换出现的内容。 如果是字符串,则使用该字符串。 它也可以是字符串列表,以循环顺序使用。

如果replacements 是一个cons 单元格(function .data),这意味着在每次匹配后调用函数来获取替换文本。 该函数使用两个参数调用:数据和已经进行的替换次数。

如果重复计数非零,它应该是一个整数。 然后它指定在循环推进到下一个之前使用替换列表中的每个字符串的次数。

如果 from-string 包含大写字母,则 perform-replace 将 case-fold-search 绑定到 nil,并且它使用替换而不改变它们的大小写。

通常,keymap query-replace-map 定义了查询的可能用户响应。 参数映射,如果非零,则指定要使用的键映射,而不是查询替换映射。

Non-nil region-noncontiguous-p 表示 start 和 end 之间的区域由不连续的片段组成。 最常见的例子是一个矩形区域,其中的片段由换行符分隔。

此函数使用两个函数之一来搜索下一个出现的 from-string。 这些函数由两个变量的值指定:replace-re-search-function 和 replace-search-function。 当参数 regexp-flag 为非 nil 时调用前者,当它为 nil 时调用后者。

变量:查询替换映射¶

此变量包含一个特殊的键映射,它定义了 perform-replace 的有效用户响应和使用它的命令,以及 y-or-np 和 map-y-or-np。 这张地图有两个不同寻常之处:

键绑定不是命令,只是对使用此映射的函数有意义的符号。 不支持前缀键; 每个键绑定都必须用于单事件键序列。 这是因为函数不使用 read-key-sequence 来获取输入; 相反,他们阅读单个事件并“手动”查找。

以下是 query-replace-map 的有意义的绑定。 其中一些仅对查询替换和朋友有意义。

行为

一定要采取正在考虑的行动——换句话说,“是”。 跳过

不要对这个问题采取行动——换句话说,“不”。 出口

回答这个问题“否”,并放弃整个系列的问题,假设答案是“否”。 退出前缀

与退出类似,但将按下的键添加到未读命令事件(请参阅杂项事件输入功能)。 行动和退出

回答这个问题“是”,并放弃整个系列的问题,假设随后的答案将是“否”。 表演和表演

回答这个问题“是”,但显示结果——不要前进到下一个问题。 自动的

用“是”回答这个问题和该系列中的所有后续问题,无需进一步的用户交互。 备份

回到之前提出问题的地方。 撤消

撤消上次更换并返回执行更换的位置。 全部撤消

撤消所有替换并返回执行第一次替换的位置。 编辑

输入一个递归编辑来处理这个问题——而不是通常会采取的任何其他操作。 编辑替换

在 minibuffer 中编辑这个问题的替换。 删除和编辑

删除正在考虑的文本,然后输入递归编辑以替换它。 居中者 向上滚动 向下滚动 滚动其他窗口 向下滚动其他窗口

执行指定的窗口滚动操作,然后再问同样的问题。 只有 y-or-np 和相关函数使用这个答案。 辞职

立即执行戒烟。 只有 y-or-np 和相关函数使用这个答案。 帮助

显示一些帮助,然后再次询问。

变量:多查询替换映射¶

此变量包含一个键映射,该键映射通过提供在多缓冲区替换中有用的附加键绑定来扩展查询替换映射。 额外的绑定是:

自动全部

对于所有剩余的缓冲区,用“是”回答这个问题和该系列中的所有后续问题,无需进一步的用户交互。 退出电流

回答“否”这个问题,并放弃当前缓冲区的整个系列问题。 继续到序列中的下一个缓冲区。

变量:替换搜索功能¶

此变量指定一个函数,该函数执行替换调用以搜索下一个要替换的字符串。 它的默认值是向前搜索。 任何其他值都应命名为 3 个参数的函数:search-forward 的前 3 个参数(请参阅搜索字符串)。

变量:replace-re-search-function ¶

此变量指定一个函数,该函数执行替换调用以搜索下一个要替换的正则表达式。 它的默认值是重新搜索。 任何其他值都应命名为 3 个参数的函数:re-search-forward 的前 3 个参数(请参阅正则表达式搜索)。

35.8 编辑中使用的标准正则表达式

本节描述了一些变量,这些变量包含在编辑中用于特定目的的正则表达式:

用户选项:页面分隔符¶

这是描述分隔页面的行开头的正则表达式。 默认值为“^\014”(即“^^L”或“^\Cl”); 这匹配以换页符开头的行。

以下两个正则表达式不应假定匹配总是从行首开始; 他们不应该使用 ‘^’ 来锚定比赛。 大多数情况下,段落命令只在行首检查匹配,这意味着“^”是多余的。 当左边距不为零时,它们接受在左边距之后开始的匹配。 在这种情况下,’^’ 将是不正确的。 但是,在从不使用左边距的模式中,’^’ 是无害的。

用户选项:段落分隔¶

这是用于识别分隔段落的行开头的正则表达式。 (如果您更改此设置,您可能还必须更改段落开头。)默认值为“[ \t\f]*$”,它匹配完全由空格、制表符和换页符组成的行(在它之后左边距)。

用户选项:段落开始¶

这是用于识别开始或分隔段落的行开头的正则表达式。 默认值为 “\f\|[ \t]*$”,它匹配仅包含空格或以换页开头(在其左边距之后)的行。

用户选项:sentence-end ¶

如果非零,则该值应该是描述句子结尾的正则表达式,包括句子后面的空格。 (无论如何,所有段落边界也结束句子。)

如果该值为 nil(默认情况下),则函数 sentence-end 构造正则表达式。 这就是为什么你应该总是调用函数 sentence-end 来获取用于识别句子结尾的正则表达式。

功能:句尾¶

如果非 nil,此函数返回变量 sentence-end 的值。 否则,它会根据变量 sentence-end-double-space(参见 sentence-end-double-space 的定义)、sentence-end-without-period 和 sentence-end-without-space 的值返回一个默认值。

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/rookieagle/elisp.git
git@gitee.com:rookieagle/elisp.git
rookieagle
elisp
Elisp
main

搜索帮助