我用下列的代码替换字符串中的子字符串,同时收集被替换的字符串,
(let ((mystring "This is a three")
(start 0)
(mylist nil))
(while (string-match "th" mystring start)
(setq start (match-end 0))
(push (match-string 0 mystring) mylist)
(setq mystring (replace-match "ha" nil nil mystring 0))
)
(mapconcat #'identity (nreverse mylist) "\n\n")
;; mystring
)
问题是如果替换后的字符和原来的长度不一样,会导致字符长度改变,搜索的起始位置可能超出范围,然后 string-match
会报错,请问大家这个应该怎样处理?我能想到比较 dirty 的办法,是算一下两个字符串的长度差,然后修正起始位置。
隐约感觉会是这个问题:
不过我对代码也不敏感,除非你直接给个一键执行的出错例子。
(let ((mystring "This is a three")
(start 0)
(mylist nil))
(while (string-match "three" mystring start)
(setq start (match-end 0))
(push (match-string 0 mystring) mylist)
(setq mystring (replace-match "two" nil nil mystring))
)
(mapconcat #'identity (nreverse mylist) "\n\n")
;; mystring
)
第一组代码正常执行,mystring
中的 th
被替换,并且被替换的 th
被收集到一个列表里;
第二组代码无法正常执行,执行过程中报错。
噢,看到了。那应该是 save-match-data
无关,应该是不能在循环中 (setq mystring ...)
修改原字符串本身。
是的,然后我现在的做法是根据替换字符的长度调整起始位置:
(let ((mystring "This is a three")
(start 0)
(mylist nil))
(while (string-match "three" mystring start)
(setq start (+ (match-end 0) (- 3 (length (match-string 0 mystring))))) ; 这一句调整起始位置
(push (match-string 0 mystring) mylist)
(setq mystring (replace-match "two" nil nil mystring))
)
(mapconcat #'identity (nreverse mylist) "\n\n")
mystring
)
最好是不修改原字符串,可以参考一下 replace-regexp-in-string
函数的实现。精简掉不需要的步骤,加入收集被替换字符的操作。
用 replace-regexp-in-string
的话,比如想把 "1-3-2"
中的数字替换成对应的英文单词,并且搜集这些数字:
(let (matches)
(list
(replace-regexp-in-string
"[0-9]"
(lambda (s)
(let ((match (match-string 0 s)))
(push match matches)
(pcase match
("1" "one")
("2" "two")
("3" "three"))))
"1-3-2")
(nreverse matches)))
;; => ("one-three-two" ("1" "3" "2"))
3 个赞
et2010
10
非常赞!
我看了一下函数文档, 不用 match-string
函数也可以,因此可以再稍微简化一点:
(let (matches)
(list
(replace-regexp-in-string
"[0-9]"
(lambda (match)
(push match matches)
(pcase match
("1" "one")
("2" "two")
("3" "three")))
"1-3-2")
(nreverse matches)))
;; => ("one-three-two" ("1" "3" "2"))
2 个赞