14.2 什么算作一个单词或符号?

Emacs 将不同字符归入不同的 语法类别(syntax categories)。例如,正则表达式 ‘\\w+’ 是匹配一个或多个 单词构成字符的模式。单词构成字符属于一个语法类别。其他语法类别包括标点符号类(如句号、逗号) 和空白字符类(如空格、制表符)。(更多信息参见 see Syntax Tables in The GNU Emacs Lisp Reference Manual。)

语法表规定了哪些字符属于哪些类别。通常,连字符并不被指定为单词构成字符,而是被归入属于符号名 一部分但不属于单词的字符类别。这意味着 count-words-example 函数将其当作单词间空白符 处理,这也是它把 ‘multiply-by-seven’ 计为三个单词的原因。

有两种方法可以让 Emacs 将 ‘multiply-by-seven’ 计为一个符号:修改语法表,或修改正则表达式。

我们可以通过修改 Emacs 为每种模式维护的语法表,将连字符重新定义为单词构成字符。这能满足需求, 但问题在于,连字符只是符号中最常见的非典型单词构成字符,还有其他类似字符。

另一种方法是重新定义 count-words-example 中使用的正则表达式,使其包含符号。 这种方法更清晰,但实现起来略有些技巧性。

第一部分很简单:模式必须匹配至少一个单词或符号构成字符。如下:

"\\(\\w\\|\\s_\\)+"

\\(’ 是分组结构的开头,将 ‘\\w’ 与 ‘\\s_’ 作为由 ‘\\|’ 分隔的候选项。 ‘\\w’ 匹配任意单词构成字符,‘\\s_’ 匹配任意属于符号名但非单词构成的字符。 分组后的 ‘+’ 表示必须至少匹配一次单词或符号构成字符。

不过,正则表达式的第二部分设计起来更困难。我们希望在第一部分后接可选的一个或多个非单词、 非符号构成字符。起初我以为可以这样定义:

"\\(\\W\\|\\S_\\)*"

大写的 ‘W’ 与 ‘S’ 匹配单词或非符号构成字符。遗憾的是,该表达式会匹配 任意“非单词构成 或 非符号构成”的字符——这等价于匹配任意字符!

后来我发现,测试区域中的每个单词或符号后都跟着空白符(空格、制表符或换行)。于是我尝试在 单词或符号构成模式后添加匹配一个或多个空格的模式,但同样失败。单词与符号通常由空白分隔, 但实际代码中括号可能跟在符号后,标点可能跟在单词后。最终,我设计出一种模式:单词或符号构成 字符后接可选的非空白字符,再后接可选的空白字符。

完整的正则表达式如下:

"\\(\\w\\|\\s_\\)+[^ \t\n]*[ \t\n]*"