而要是 old 在匹配途中失败了,会发生什么事呢?举例来说,假设我们要找的模式 (pattern)是 "abac" ,而输入文件包含的是 "ababac" 。输入会一直到第四个字符才发现不匹配,也就是在模式中的 c 以及输入的 b 才发现。在此时我们可以将原本的 a 写至输出文件,因为我们已经知道这里没有匹配。但有些我们从输入读入的字符还是需要留着: 举例来说,第三个 a ,确实是成功匹配的开始。所以在我们要实现这个算法之前,我们需要一个地方来储存,我们已经从输入读入的字符,但之后仍然需要的字符。

    一个暂时储存输入的队列 (queue)称作缓冲区 (buffer)。在这个情况里,因为我们知道我们不需要储存超过一个预定的字符量,我们可以使用一个叫做环状缓冲区 ring buffer 的资料结构。一个环状缓冲区实际上是一个向量。是使用的方式使其成为环状: 我们将之后的元素所输入进来的值储存起来,而当我们到达向量结尾时,我们重头开始。如果我们不需要储存超过 n 个值,则我们只需要一个长度为 n 或是大于 n 的向量,这样我们就不需要覆写正在用的值。

    在图 7.1 的代码,实现了环状缓冲区的操作。 buf 有五个字段 (field): 一个包含存入缓冲区的向量,四个其它字段用来放指向向量的索引 (indices)。两个索引是 startend ,任何环状缓冲区的使用都会需要这两个索引: start 指向缓冲区的第一个值,当我们取出一个值时, start 会递增 (incremented); end 指向缓冲区的最后一个值,当我们插入一个新值时, end 会递增。

    另外两个索引, used 以及 new ,是我们需要给这个应用的基本环状缓冲区所加入的东西。它们会介于 start 与 之间。实际上,它总是符合

    你可以把 usednew 想成是当前匹配 (current match) 的 startend 。当我们开始一轮匹配时, used 会等于 startnew 会等于 end 。当下一个字符 (successive character)匹配时,我们需要递增 used 。当 usednew 相等时,我们将开始匹配时,所有存在缓冲区的字符读入。我们不想要使用超过从匹配时所存在缓冲区的字符,或是重复使用同样的字符。因此这个 new 索引,开始等于 end ,但它不会在一轮匹配我们插入新字符至缓冲区一起递增。

    要插入一个新值至缓冲区,我们将使用 buf-insert 。它将 end 递增,并把新的值放在那个位置 (译注: 递增完的位置)。相反的 buf-pop 返回一个缓冲区的第一个数值,接着将 start 递增。任何环状缓冲区都会有这两个函数。

    图 7.1 环状缓冲区的操作

    接下来我们需要两个特别为这个应用所写的函数: buf-next 从缓冲区读取一个值而不取出,而 buf-reset 重置 usednew 到初始值,分别是 与 end 。如果我们已经把至 new 的值全部读取完毕时, buf-next 返回 nil 。区别这个值与实际的值不会产生问题,因为我们只把值存在缓冲区。

    最后 buf-flush 透过将所有作用的元素,写至由第二个参数所给入的流,而 buf-clear 通过重置所有的索引至 -1 将缓冲区清空。

    在图 7.1 定义的函数被图 7.2 所使用,包含了字符串替换的代码。函数 file-subst 接受四个参数;一个查询字符串,一个替换字符串,一个输入文件以及一个输出文件。它创建了代表每个文件的流,然后调用 stream-subst 来完成实际的工作。

    变数 pos 指向我们想要匹配的字符在寻找字符串的所在位置。如果 pos 等于这个字符串的长度,我们有一个完整的匹配,则我们将替换字符串写至输出流,并清空缓冲区 (3)。如果在这之前匹配失败,我们可以将缓冲区的第一个元素取出,并写至输出流,之后我们重置缓冲区,并从 pos 等于 0 重新开始 (4)。

    图 7.2 字符串替换

    下列表格展示了当我们将文件中的 "baro" 替换成 "baric" 所发生的事,其中文件只有一个单字 "barbarous" :

    第一栏是当前字符 ── c 的值;第二栏显示是从缓冲区或是直接从输入流读取;第三栏显示需要匹配的字符 ── old 的第 posth 字符;第四栏显示那一个条件式 (case)被求值作为结果;第五栏显示被写至输出流的字符;而最后一栏显示缓冲区之后的内容。在最后一栏里, usednew 的位置一样,由一个冒号 ( : colon)表示。

    在文件 "test1" 里有如下文字:

    为了使这个例子尽可能的简单,图 7.2 的代码只将一个字符串换成另一个字符串。很容易扩展为搜索一个模式而不是一个字面字符串。你只需要做的是,将 char= 调用换成一个你想要的更通用的匹配函数调用。