3

我在 elisp 中工作,我有一个代表项目列表的字符串。字符串看起来像

"apple orange 'tasty things' 'my lunch' zucchini 'my dinner'"

我试图把它分成

("apple" "orange" "tasty things" "my lunch" "zucchini" "my dinner")

这是一个熟悉的问题。我解决它的障碍不是关于正则表达式,而是更多关于 elisp 的细节。

我想要做的是运行一个循环,如:

  • (while (< (length my-string) 0) do-work)

do-work是:

  • 将正则表达式\('[^']*?'\|[[:alnum:]]+)\([[:space:]]*\(.+\)应用于my-string
  • 附加\1到我的结果列表
  • 重新绑定my-string\2

但是,我不知道如何获得split-stringreplace-regexp-in-string做到这一点。

如何将此字符串拆分为我可以使用的值?

(或者:“我还没有找到哪个内置的 emacs 函数呢?”)

4

4 回答 4

5

类似的东西,但没有正则表达式:

(defun parse-quotes (string)
  (let ((i 0) result current quotep escapedp word)
    (while (< i (length string))
      (setq current (aref string i))
      (cond
       ((and (char-equal current ?\ )
             (not quotep))
        (when word (push word result))
        (setq word nil escapedp nil))
       ((and (char-equal current ?\')
             (not escapedp) 
             (not quotep))
        (setq quotep t escapedp nil))
       ((and (char-equal current ?\')
             (not escapedp))
        (push word result)
        (setq quotep nil word nil escapedp nil))
       ((char-equal current ?\\)
        (when escapedp (push current word))
        (setq escapedp (not escapedp)))
       (t (setq escapedp nil)
        (push current word)))
      (incf i))
    (when quotep
      (error (format "Unbalanced quotes at %d"
                     (- (length string) (length word)))))
    (when word (push result word))
    (mapcar (lambda (x) (coerce (reverse x) 'string))
            (reverse result))))

(parse-quotes "apple orange 'tasty things' 'my lunch' zucchini 'my dinner'")
("apple" "orange" "tasty things" "my lunch" "zucchini" "my dinner")

(parse-quotes "apple orange 'tasty thing\\'s' 'my lunch' zucchini 'my dinner'")
("apple" "orange" "tasty thing's" "my lunch" "zucchini" "my dinner")

(parse-quotes "apple orange 'tasty things' 'my lunch zucchini 'my dinner'")
;; Debugger entered--Lisp error: (error "Unbalanced quotes at 52")

奖励:它还允许使用“\”转义引号,如果引号不平衡(到达字符串末尾,但未找到打开的引号的匹配项),将报告它。

于 2012-10-11T10:00:13.437 回答
3

这是使用临时缓冲区实现算法的简单方法。我不知道是否有办法使用replace-regexp-in-stringor来做到这一点split-string

(defun my-split (string)
  (with-temp-buffer
    (insert string " ")     ;; insert the string in a temporary buffer
    (goto-char (point-min)) ;; go back to the beginning of the buffer
    (let ((result nil))
      ;; search for the regexp (and just return nil if nothing is found)
      (while (re-search-forward "\\('[^']*?'\\|[[:alnum:]]+\\)\\([[:space:]]*\\(.+\\)\\)" nil t)
        ;; (match-string 1) is "\1"
        ;; append it after the current list
        (setq result (append result (list (match-string 1))))
        ;; go back to the beginning of the second part
        (goto-char (match-beginning 2)))
      result)))

例子:

(my-split "apple orange 'tasty things' 'my lunch' zucchini 'my dinner'")
  ==> ("apple" "orange" "'tasty things'" "'my lunch'" "zucchini" "'my dinner'")
于 2012-10-11T08:28:59.507 回答
3

你可能想看看split-string-and-unquote

于 2012-10-11T13:37:38.403 回答
0

如果你经常操作字符串,你应该s.el通过包管理器安装库,它在一致的 API 下引入了大量的字符串实用程序函数。对于此任务,您需要 function s-match,其可选的第三个参数接受起始位置。然后,您需要一个正确的正则表达式,尝试:

(concat "\\b[a-z]+\\b" "\\|" "'[a-z ]+'")

\|表示匹配构成单词的字母序列(\b表示单词边界),或匹配引号内的字母序列和空格。然后使用循环

;; let s = given string, r = regex
(loop for start = 0 then (+ start (length match))
      for match = (car (s-match r s start))
      while match 
      collect match)

出于教育目的,我还使用递归函数实现了相同的功能:

;; labels is Common Lisp's local function definition macro
(labels
    ((i
      (start result)
      ;; s-match searches from start
      (let ((match (car (s-match r s start))))
        (if match
            ;; recursive call
            (i (+ start (length match))
               (cons match result))
          ;; push/nreverse idiom
          (nreverse result)))))
  ;; recursive helper function
  (i 0 '()))

由于 Emacs 缺乏尾调用优化,在一个大列表上执行它会导致堆栈溢出。因此你可以用do宏重写它:

(do* ((start 0)
      (match (car (s-match r s start)) (car (s-match r s start)))
      (result '()))
    ((not match) (reverse result))
  (push match result)
  (incf start (length match)))
于 2014-03-18T08:10:42.807 回答