string substitution

来源:互联网 发布:蓝可儿死亡 知乎 编辑:程序博客网 时间:2024/06/07 03:02
(defstruct buf  vec (start -1) (used -1) (new -1) (end -1))(defun bref (buf n)  (svref (buf-vec buf)         (mod n (length (buf-vec buf)))));;;扩展setf方法,注意下面调用的方式(defun (setf bref) (val buf n)  (setf (svref (buf-vec buf)               (mod n (length (buf-vec buf))))        val))(defun new-buf (len)  (make-buf :vec (make-array len)))(defun buf-insert (x b)  (setf (bref b (incf (buf-end b))) x))(defun buf-pop (b)  (prog1     (bref b (incf (buf-start b)))    (setf (buf-used b) (buf-start b)          (buf-new  b) (buf-end   b))))(defun buf-next (b)  (when (< (buf-used b) (buf-new b))    (bref b (incf (buf-used b)))))(defun buf-reset (b)  (setf (buf-used b) (buf-start b)        (buf-new  b) (buf-end   b)));;;都设置成-1,为了实现对buffer的清空(defun buf-clear (b)  (setf (buf-start b) -1 (buf-used  b) -1        (buf-new   b) -1 (buf-end   b) -1));;只有一种情况buf-used为-1,说明到文件结束时,刚读入到buffer的字符都匹配,未出现pop逻辑,故-1+1=0,也就是从头开始princ.(defun buf-flush (b str)  (do ((i (1+ (buf-used b)) (1+ i)))      ((> i (buf-end b)))    (princ (bref b i) str)))
(setf bref) 它重载了setf方法,为了是mod n 然后得到ring buffer。只有在insert的时候才调用了这个setf方法。n是buf-end +1之后的值,length最后的结果是buffer的长度,是个定值。比方说只剩下第二个b的时候,buf-end = 3. (3+1) mod 4 = 0,所以接下来的a就加入到了第一个位置。
buf-insert 关键就是里面的扩展的setf 方法。
buf-next只有在输入的时候是从buffer里面来的时候才用buf-next。它是先比较当前位置used是否小于buf-new,如果是就把下面那个输出来。所以说第二b仍旧会读出来进行匹配测试,当它执行完以后,然后再call buf-next的话,发现used跟new已经相等了,所以只能是read里面读取数据了。
buf-pop    它是从栈出来,只有执行了princ才会输出到out里面。所以说你看zerop pos首先是princ,然后才是出栈。

buf-flush  是为了解决一种情况:假设pattern为baro,现在文本中最后几个字符为bar。因为整个循环是到文件尾结束,现在bar还在buffer里面就结束了,所以将会显示不出来。其他情况的话buf-flush要不要没影响。

(defun file-subst (old new file1 file2)  (with-open-file (in file1 :direction :input)     (with-open-file (out file2 :direction :output                                :if-exists :supersede)       (stream-subst old new in out))))(defun stream-subst (old new in out)  (let* ((pos 0)         (len (length old))                     (buf (new-buf len))           ;buffer的长度跟pattern串长度一样,         (from-buf nil))    (do ((c (read-char in nil :eof)    ;初始时读取一个字符            (or (setf from-buf (buf-next buf))   ;因为有短路现象,要么从buffer中读取,要不从流中。                (read-char in nil :eof))))        ((eql c :eof))                   ;循环结束条件是碰到了结尾      (cond ((char= c (char old pos))    ;如果读入的字符等于old中的一个字符             (incf pos)                  ;pos+1,因为凡是匹配成功的话,pattern中的指标就要加+指到下一位             ;;内部cond判断两次             (cond ((= pos len)            ; 3  因为上面pos已经加1了,所以说只需相等就行了。                    (princ new out)     ;把它输出来                    (setf pos 0)        ;pos 变为0                    (buf-clear buf))    ;buffer 清空                   ((not from-buf)         ; 2                     (buf-insert c buf))))            ((zerop pos)                   ; 1             (princ c out)             (when from-buf               (buf-pop buf)               (buf-reset buf)))            (t                             ; 4             (unless from-buf               (buf-insert c buf))             (princ (buf-pop buf) out)             (buf-reset buf)             (setf pos 0))))    (buf-flush buf out)))
start : 当我们pop一个元素,他就会增加,增加buf-stard。
end   :当我们插入一个元素的时候他会增加,先增加buf-end,然后再往这个buf-end里面添加值。
used  : 当我们pop完一个元素,然后就会调用bufnext,他会在里面累加。
new   : 当试着匹配buffer里面的字符时,开始调用used/new,一旦buffer中匹配开始,new就会绑定到end,因为end就相当于是buffer里面最后一个元素,然后调用buf-next,进行迭代匹配,直到used=new。也就是说一旦检测buffer的话,会把buffer里面所有的都试着匹配一遍,然后才会开始接受从read中读取。

1:ring buffer空间只要跟pattern一样就行。
2:短路现象中,在b-a-r的过程中因为虽然这个时候buf-used< buf-new当时取到的值为nil,所以仍旧是从流中读取
from-buf意思就是从buf里面读取数据成功。
3:什么时候第一次给used/new赋值的呢?也就是在第一次pop的时候进行reset的时候才会给used/new进行赋值。就像图5,因为前面的时候used/new都是-1,所以也都是按流读入的。

Case1:zerop pos 首先表示的是一个新的匹配,并且前面没有字符匹配成功的话,就把他输出来。如u/s直接输出来了。都没有到buffer里面还有一种情况就是这个字符已经在buffer里面了,但是前面没有字符匹配成功,他就需要从buffer中出来,并且重新设置pos为0(指向pattern的第一个位置)
Case2:当匹配开始,也就是说前面有字符匹配已经匹配上了。当读入的字符不是来自于buffer(from-buf 为假),就将字符输入到buffer里面。pos 指定到old string上面。当字符与在pattern string的pos +1值一样时,说明整个串匹配成功。
Case3:pos + 1 跟new string的长度一样,并且匹配在这个位置的元素匹配成功,就输出一个新的string。重新把pattern匹配的起始位置设置为0,因为你已经匹配成功了,下一个再进行匹配的话,只能是从头开始。把buffer里面的东西请空。  因为先处理完buffer里面的东西,才会继续从读入新的值。比如当你匹配a弹出的时候,他会再(setf from-buf (buf-next buf))
Case4:匹配到中途在这个位置不匹配了。pop第一个字符出栈,然后reset,并且设置pos从pattern头开始重新匹配。因为你输入的时候就是两种情况输入的,一个是读进去,一个是from-buf.所以在你处理的时候也同样是两种情况处理。

首先按照匹配是否成功,然后再按照是否来自于文件输入或者是buffer部分读取。


CL-USER> (char "string" 1)#\tCL-USER> (new-buf 6)#S(BUF :VEC #(0 0 0 0 0 0) :START -1 :USED -1 :NEW -1 :END -1)CL-USER> (length (buf-vec (new-buf 6)))6CL-USER> (file-subst " th" "z" "d:/emacs/string.txt" "d:/emacs/stringold.txt")NIL