命名字符串替换?


13

我经常必须对同一字符串进行多次替换:

(format "%s %s %s" "a" "a" "a") ;; gives: "a a a"

(这只是一个虚拟的示例,在这种情况下,最好将“ a”与空格粘合在一起,但是通常我会处理更复杂的情况)

有没有办法进行命名替换?例如在python中,将这样写:

"{0} {0} {0}".format("a") # or:
"{name} {name} {name}".format(name="a")


@Malabarba:我在这里发布了该线程的一些修改后的答案作为答案
Adobe

Answers:


16

重写这个答案给出了另一个解决方案:

(format-spec "%a %a %a %b %b %b" (format-spec-make ?a "a" ?b "b"))

编辑:另一个format-spec解决方案

正如Malabarba在评论中提供了另一种解决方案:

(format-spec "%a %a %a %b %b %b" '((?a . "a") (?b . "b")))

编辑2:替代前的评估:

以下是替换前评估的示例:

(let ( (a 1)
       (b 2) )
  (message (format-spec "a = %a; b = %b" (format-spec-make ?a a ?b b))) )
;; ⇒ a = 1; b = 1

(let ( (a 1)
       (b 2) )
  (message (format-spec "a = %a; b = %b" `((?a . ,a) (?b . ,b)))) )
;; ⇒ a = 1; b = 2

3
还要注意,这format-spec-make只是一个名单:'((?a . "a") (?b . "b"))
马拉巴巴

1
“似乎没有为数字工作”-请参阅emacs.stackexchange.com/questions/7481/…–
npostavs

@npostavs:很高兴知道!我编辑了答案。
Adobe

14

Magnar Sveen的字符串处理库s.el提供了多种方法来执行此操作。例如:

(require 's)
(s-format "${name} ${name} ${name}" 'aget '(("name" . "test")))
;; ==> "test test test"

请注意,s-format可以采取任何替代品的功能,但提供了特殊的处理ageteltgethash。因此,您可以使用令牌列表并按索引引用它们,如下所示:

(s-format "$0 $0 $0 $1 $1 $1" 'elt '("a" "b"))
;; ==> "a a a b b b"

您还可以使用范围内的变量进行替换,如下所示:

(let ((name "test"))
  (s-lex-format "${name} ${name} ${name}"))
;; ==> "test test test"

1
太好了,我对此功能一无所知!我大部分时间都使用s.el来窥视如何在Emacs中执行常见的字符串操作任务,但这确实不只是现有函数的单行包装。
wasamasa 2015年

3

s.el的s-lex格式确实是您想要的,但是如果您希望真正能够将代码放入替换块中,而不仅仅是变量名,那么我将其写为概念证明。

(defmacro fmt (str)
  "Elisp string interpolation for any expression."
  (let ((exprs nil))
    (with-temp-buffer
      (insert str)
      (goto-char 1)
      (while (re-search-forward "#{" nil t 1)
        (let ((here (point))
              (emptyp (eql (char-after) ?})))
          (unless  emptyp (push (read (buffer-substring (point) (progn (forward-sexp 1) (point)))) exprs))
          (delete-region (- here 2) (progn (search-forward "}") (point)))
          (unless emptyp (insert "%s"))
          (ignore-errors (forward-char 1))))
      (append (list 'format (buffer-string)) (reverse exprs)))))

;; demo with variable and code substitution 
(fmt "My name is #{user-full-name}, I am running Emacs #{(if (display-graphic-p) \"with a GUI\" \"in a terminal\")}.")
;; results in
"My name is Jordon Biondo, I am running Emacs with a GUI."

如果您疯了,甚至可以将一个fmt电话嵌入另一个电话fmt

(fmt "#{(fmt\"#{(fmt\\\"#{user-full-name}\\\")}\")}")
;; =>
"Jordon Biondo"

该代码只是扩展为一个format调用,因此所有替换均按顺序完成并在运行时进行评估。

(cl-prettyexpand '(fmt "Hello, I'm running Emacs #{emacs-version} on a #{system-type} machine with #{(length (window-list))} open windows."))

;; expands to

(format "Hello, I'm running Emacs %s on a %s machine with %s open windows."
        emacs-version
        system-type
        (length (window-list)))

可以使用哪种格式类型而不是始终使用%s进行改进,但这必须在运行时完成,并且会增加开销,但是可以通过将所有格式args包围在一个函数调用中来完成,该函数调用可以很好地格式化对象在类型上,但实际上,您唯一想得到的可能就是浮点数,甚至可以替换(格式为“%f”浮点数),这是您的绝望。

如果我做得更多,我更有可能更新此要点而不是此答案。https://gist.github.com/jordonbiondo/c4e22b4289be130bc59b


3

不是通用的,但是可以解决您的情况:

(apply 'format "%s %s %s" (make-list 3 'a))

使用提供的示例:

(apply 'format (concat " * - :raw-html:`<img width=\"100%%\" "
                       "src=\"http://xxx.xxx/images/languages/"
                       "staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:")
       (make-list 3 'some-image))

给出:

" * - :raw-html:`<img width=\"100%\" src=\"http://xxx.xxx/images/languages/staff/some-image.jpg\" alt=\"some-image.jpg\"/>` - .. _some-image:"

这是我要处理的示例字符串:" * - :raw-html:`<img width=\"100%%\" src=\"http://xxx.xxx/images/languages/staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:"-都%s一样。
Adobe

@Adobe我已经用您的示例更新了答案。
wvxvw
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.