re-search-forward 死循环

想写一个将文件中类似 _{abc}, ^{abc} 这样的字符串替换成相应 unicode 的函数,比如:

  • ^{abc} 替换成 ᵃᵇᶜ
  • _{abc} 因为字母 b, c 没有对应的下标形式的 unicode,所以就保持原样不替换。
(setq superscript-subscript-map
      ;;     sup sub
      '(("a" "ᵃ" "ₐ") ("b" "ᵇ" nil) ("c" "ᶜ" nil) ("d" "ᵈ" nil)))

(defun convert-to-unicode (str subsup)
  (let ((converted-str ""))
    (progn
      (dolist (c (delete "" (split-string str "")))
        (cond ((string-equal subsup "^")
               (setq converted-str (concat converted-str (nth 0 (cdr (assoc c superscript-subscript-map))))))
              ((string-equal subsup "_")
               (setq converted-str (concat converted-str (nth 1 (cdr (assoc c superscript-subscript-map))))))
              (t "default")))
      (if (eq (length str) (length converted-str))
          converted-str))))

上面 convert-to-unicode 函数的作用是将字符串替换成对应 unicode 的具体函数,如果有字符没有对应的 unicode,那么返回 nil。

我遇到问题的地方是在替换当前文件里字符串时,陷入了死循环中,陷入死循环的函数是下面这个

(defun latex-to-unicode ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "\\([_^]\\){\\([[:word:]ɑβθ+-=()]+\\)}" nil t)
    (setq converted-str (convert-to-unicode (match-string 2) (match-string 1)))
    (if converted-str
        (replace-match converted-str))))

当前文件内容是

_{abc}
^{abc}

调用这个函数后会发生死循环,然后 C-g 终止函数后,文件内容是

_ᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜᵃᵇᶜ{abc}
^{abc}

while 的 body 部分换成 (replace-match "xxx") 都没啥问题,想不明白哪里出了问题。

你的 convert-to-unicode 使用了 string-split,间接修改了 match-data ,导致 replace-match 替 换错了位置。

需要手动 save/restore match-data: https://www.gnu.org/software/emacs/manual/html_node/elisp/Saving-Match-Data.html

(defun latex-to-unicode ()
  (interactive)
  (goto-char (point-min))
  (let ((counter 0) )
    (while (and (< counter 2)
                (re-search-forward "\\([_^]\\){\\([[:word:]ɑβθ+-=()]+\\)}" nil t))
      (let ((data (match-data)))
        (unwind-protect
            (setq converted-str (convert-to-unicode (match-string 2) (match-string 1)))
          (set-match-data data)))
      (if converted-str
          (replace-match converted-str)))))

2 个赞