【问题标题】:Processing text with elisp用 elisp 处理文本
【发布时间】:2010-01-03 05:25:18
【问题描述】:

自从我皈依了 Emacs 教会以来,我一直在尝试从里面做所有事情,我想知道如何用它快速有效地进行一些文本处理。

以我几分钟前在 org-mode 上编辑的这个列表为例。

** 迭戈:b QI ** 布鲁诺吉尔:b QI ** 科马:乔 ** 嗯:rsrs pr0n ** 费利佩奥古斯托:特别是 ** GustavoPupo:pinto tr 等 ** GP:点燃 gtk ** 艾伦:jo mil pc ** 约斯特:b hq jo 1997 ** 赫伯特:b rsrs pr0n ** 安德烈:maia mil 伪 ** 罗德里戈:c ** 原因:b rsrs 7arte 伪 **肯尼:cri gif ** 丹尼尔:gtk mu pr0n rsrs b ** 托尼:1997 年的 esp ** 维托:b jo mimimi ** 拉斐尔:b rpg 7arte ** Luca:b lit gnu pc prog mmu 7arte 1997 ** LZZ:一个qt ** 威廉:b an jo pc 1997 ** 史诗:gtk ** 阿尔多:b 伪 pol mil 毛皮 ** GustavoKyon:一个gtk ** CarlosIsaksen : an hq jo 7arte gtk 1997 ** 彼得:pseudo pol mil est 1997 gtk lit lang ** 莱德罗:b jo cb ** 弗雷德里科:7arte lit gtk ** 角色:b 一个伪 mimimi 7arte ** 马蒂亚斯:jo lit ** 恩里克:1997 h gtk qt ** eumané:一个 qt ** 海象:cri de ** FilipePinheiro:点燃伪 ** 伊戈尔:伪 b ** 埃里克:b jo rpg q 1997 gtk ** 加布里埃尔:pr0n rsrs qt **乔治:克洛咪咪 ** anão: hq jo 1997 rsrs clô b ** 杰夫:7arte gtk ** davidatenas:7arte 1997 esp qt ** 哈哈哈:b ** 爱德华多:b

这是一个与标签关联的名称列表,我想获取与名称关联的标签列表。

在 bash 中,我会首先用单引号回显整个粘贴的内容,然后通过管道将其传送到 awk,循环遍历每一行并将其每个部分添加到正确的临时变量中,然后对其进行处理,直到它像我想要的那样。

echo '** Diego: b QI ** 布鲁诺吉尔:b QI ** 科马:乔 ** 嗯:rsrs pr0n ** 费利佩奥古斯托:特别是 ** GustavoPupo:pinto、tr 等 ** GP:点燃 gtk ** 艾伦:jo mil pc ** 约斯特:b hq jo 1997 ** 赫伯特:b rsrs pr0n ** 安德烈:maia mil 伪 ** 罗德里戈:c ** 原因:b rsrs 7arte 伪 **肯尼:cri gif ** 丹尼尔:gtk mu pr0n rsrs b ** 托尼:1997 年的 esp ** 维托:b jo mimimi ** 拉斐尔:b rpg 7arte ** Luca:b lit gnu pc prog mmu 7arte 1997 ** LZZ:一个qt ** 威廉:b an jo pc 1997 ** 史诗:gtk ** 阿尔多:b 伪 pol mil 毛皮 ** GustavoKyon:一个gtk ** CarlosIsaksen : an hq jo 7arte gtk 1997 ** 彼得:pseudo pol mil est 1997 gtk lit lang ** 莱德罗:b jo cb ** 弗雷德里科:7arte lit gtk ** 角色:b 一个伪 mimimi 7arte ** 马蒂亚斯:jo lit ** 恩里克:1997 h gtk qt ** eumané:一个 qt ** 海象:cri de ** FilipePinheiro:点燃伪 ** 伊戈尔:伪 b ** 埃里克:b jo rpg q 1997 gtk ** 加布里埃尔:pr0n rsrs qt **乔治:克洛咪咪 ** anão: hq jo 1997 rsrs clô b ** 杰夫:7arte gtk ** davidatenas:7arte 1997 esp qt ** 哈哈哈:b ** 爱德华多:b ' | awk '{sub(":","");for (i=3;i

...还有TA-DA!不到 2 分钟的预期输出,以直观和增量的方式完成。你能告诉我如何在 elisp 中做这样的事情,最好是在 emacs 缓冲区中,优雅而简单吗?

谢谢!

【问题讨论】:

    标签: emacs lisp awk elisp


    【解决方案1】:

    我要做的第一件事是利用org-mode 的标签支持。而不是

    ** Diego: b QI
    

    你会的

    ** Diego                          :b:QI:
    

    org-mode 识别为标签“b”和“QI”。

    要将您当前的格式转换为标准的org-mode 格式,您可以使用 以下(假设您的源缓冲区称为“asdf”)

    (with-current-buffer "asdf"
      (beginning-of-buffer)
      (replace-string " " ":")
      (beginning-of-buffer)
      (replace-string "**:" "** ")
      (beginning-of-buffer)
      (replace-string "::" " :")
      (beginning-of-buffer)
      (replace-string "\n" ":\n")
      (org-set-tags-command t t))
    

    它既不美观也不高效,但它完成了工作。

    之后,您可以使用以下内容生成具有以下格式的缓冲区 你想要的 shell 脚本:

    (let ((results (get-buffer-create "results"))
          tags)
      (with-current-buffer "asdf"
        (beginning-of-buffer)
        (while (org-on-heading-p)
          (mapc '(lambda (item) (when item (add-to-list 'tags item))) (org-get-local-tags))
          (outline-next-visible-heading 1)))
      (setq tags (sort tags 'string<))
      (with-current-buffer results
        (erase-buffer)
        (mapc '(lambda (item)
                 (insert (format "%s: %s\n"
                                 item
                                 (with-current-buffer "asdf"
                                   (org-map-entries '(substring-no-properties (org-get-heading t)) item)))))
              tags)
        (beginning-of-buffer)
        (replace-regexp "[()]" "")))
    

    这会将结果放入一个名为“results”的缓冲区中,如果没有则创建它 已经存在。基本上,它正在收集缓冲区“asdf”中的所有标签, 对它们进行排序,然后遍历每个标签并搜索每个标题 “asdf”中的那个标签并将其插入到“结果”中。

    稍微清理一下,这可以变成一个函数;基本上只是 用参数替换“asdf”和“results”。如果你需要做,我可以做 那个。

    【讨论】:

      【解决方案2】:

      有一个函数 shell-command-on-region 几乎可以做到它所说的。您可以突出显示一个区域,执行 M-|,键入 shell 命令的名称,然后将数据通过管道传送到该命令。给它一个参数,该区域将替换为命令的结果。

      举个简单的例子,突出一个区域,输入'C-u 0 M-| wc' (control-u, zero, meta-pipe and then 'wc') 并且该区域将被替换为该区域的字符数、单词数和行数。

      您可以做的另一件事是弄清楚如何操作一行,使其成为宏,然后重复运行宏。例如,'C-x ( C-s foo C-g bar C-x )' 将搜索单词“foo”,然后输入单词“bar”,将其更改为“foobar”。然后,您可以执行一次“C-u C-x e”,这将继续运行宏,直到它找不到更多的“foo”。

      【讨论】:

      • 此外,现代 Emacsen 为键盘宏提供了方便的绑定。 绑定到start-macro-of-insert-counter', &lt;f4&gt; is bound to kmacro-end-or-call-macro'——这样可以节省输入。忽略计数器功能(一如既往,“C-h k RET”以获得完整文档),这可以让您点击“ C-s foo C-g bar ...”——第一个 结束宏定义,第二个执行它。
      【解决方案3】:

      好的,这是我在 elisp 中的第一次尝试:

      1. 我启动了一个启用 elisp 和 paredit 模式的缓冲区,打开双引号并粘贴文本
      2. 我使用let将它绑定到一个符号
      (let ((foobar "** Diego: b QI ** 布鲁诺吉尔:b QI ** 科马:乔 ** 嗯:rsrs pr0n ** 费利佩奥古斯托:特别是 ** GustavoPupo:pinto、tr 等 ** GP:点燃 gtk ** 艾伦:jo mil pc ** 约斯特:b hq jo 1997 ** 赫伯特:b rsrs pr0n ** 安德烈:maia mil 伪 ** 罗德里戈:c ** 原因:b rsrs 7arte 伪 **肯尼:cri gif ** 丹尼尔:gtk mu pr0n rsrs b ** 托尼:1997 年的 esp ** 维托:b jo mimimi ** 拉斐尔:b rpg 7arte ** Luca:b lit gnu pc prog mmu 7arte 1997 ** LZZ:一个qt ** 威廉:b an jo pc 1997 ** 史诗:gtk ** 阿尔多:b 伪 pol mil 毛皮 ** GustavoKyon:一个gtk ** CarlosIsaksen : an hq jo 7arte gtk 1997 ** 彼得:pseudo pol mil est 1997 gtk lit lang ** 莱德罗:b jo cb ** 弗雷德里科:7arte lit gtk ** 角色:b 一个伪 mimimi 7arte ** 马蒂亚斯:jo lit ** 恩里克:1997 h gtk qt ** eumané:一个 qt ** 海象:cri de ** FilipePinheiro:点燃伪 ** 伊戈尔:伪 b ** 埃里克:b jo rpg q 1997 gtk ** 加布里埃尔:pr0n rsrs qt **乔治:克洛咪咪 ** anão: hq jo 1997 rsrs clô b ** 杰夫:7arte gtk ** davidatenas:7arte 1997 esp qt ** 哈哈哈:b ** 爱德华多:b ")) 富吧)

      现在我将 foobar 更改为花哨的东西。

      1. 首先,我使用正则表达式删除符号并使用(split-string) 将文本拆分为字符串
      2. 然后我做一个地图车把每一行变成一个单词列表
      (mapcar #'(lambda (y) (split-string y " " t)) (split-string (replace-regexp-in-string "[:\*]" "" foobar) "\n" t))
      1. 然后我创建一个 hashmap 并将其绑定到 temphash ((temphash (make-hash-table :test 'equal)))
      2. 然后我循环进入嵌套列表以将元素添加到哈希表中。我认为我不应该使用 mapcar 进行非函数式编程,但没有人在看 ;)
      (mapcar #'(λ (l) (mapcar #'(lambda (m) (puthash m (format "%s %s" (car l) (let ((tempel (gethash m temphash)))) (if tempel tempel ""))) temphash)) (rest l))) (mapcar #'(lambda (y) (split-string y " " t)) (split-string (replace-regexp-in-string "[:\*]" "" foobar) "\n" t)))
      1. 最后,我将哈希表中的元素提取到另一组嵌套列表中,并使用从 Xah Lee 网页中窃取的便捷功能,
      2. 最后我用 M-x pp-eval-last-sexp 将它打印到另一个缓冲区

      这有点令人费解,特别是双地图车,但它有点工作。这是完整的“代码”:

      ;;从 Xah Lee 的页面被盗 (defun hash-to-list (hashtable) “返回代表哈希表的列表。” (让(我的名单) (maphash (lambda (kk vv) (setq mylist (cons (list kk vv) mylist))) 哈希表) 我的列表 ) ) ;;代码 (let ((foobar "** Diego: b QI ** 布鲁诺吉尔:b QI ** 科马:乔 ** 嗯:rsrs pr0n ** 费利佩奥古斯托:特别是 ** GustavoPupo:pinto、tr 等 ** GP:点燃 gtk ** 艾伦:jo mil pc ** 约斯特:b hq jo 1997 ** 赫伯特:b rsrs pr0n ** 安德烈:maia mil 伪 ** 罗德里戈:c ** 原因:b rsrs 7arte 伪 **肯尼:cri gif ** 丹尼尔:gtk mu pr0n rsrs b ** 托尼:1997 年的 esp ** 维托:b jo mimimi ** 拉斐尔:b rpg 7arte ** Luca:b lit gnu pc prog mmu 7arte 1997 ** LZZ:一个qt ** 威廉:b an jo pc 1997 ** 史诗:gtk ** 阿尔多:b 伪 pol mil 毛皮 ** GustavoKyon:一个gtk ** CarlosIsaksen : an hq jo 7arte gtk 1997 ** 彼得:pseudo pol mil est 1997 gtk lit lang ** 莱德罗:b jo cb ** 弗雷德里科:7arte lit gtk ** 角色:b 一个伪 mimimi 7arte ** 马蒂亚斯:jo lit ** 恩里克:1997 h gtk qt ** eumané:一个 qt ** 海象:cri de ** FilipePinheiro:点燃伪 ** 伊戈尔:伪 b ** 埃里克:b jo rpg q 1997 gtk ** 加布里埃尔:pr0n rsrs qt **乔治:克洛咪咪 ** anão: hq jo 1997 rsrs clô b ** 杰夫:7arte gtk ** davidatenas:7arte 1997 esp qt ** 哈哈哈:b ** 爱德华多:b ") (temphash (make-hash-table :test 'equal))) (mapcar #'(λ (l) (mapcar #'(lambda (m) (puthash m (format "%s %s" (car l) (let ((tempel (gethash m temphash)))) (if tempel tempel ""))) temphash)) (rest l))) (mapcar #'(lambda (y) (split-string y " " t)) (split-string (replace-regexp-in-string "[:\*]" "" foobar) "\n" t))) (哈希列表 temphash))

      这是输出:

      ((“clô”“anão”) (“克洛”“乔治”) (“q”“埃里克”) (“德”“海象”) (“h”“恩里克”) ("cb" "莱安德罗") (“朗”“彼得”) ("est" "彼得") (“毛皮”“阿尔多”) (“波尔”“彼得奥尔多”) (“qt”“davidatenas Gabriel eumané henrique LZZ”) (“mmu”“卢卡”) (“前卫”“卢卡”) (“gnu”“卢卡”) ("rpg" "埃里克·拉斐尔") (“mimimi”“乔治·罗尔·维托”) (“一个”“davidatenas eumané rol CarlosIsaksen GustavoKyon William LZZ tony”) (“亩”“丹尼尔”) (“gif”“肯尼”) (“cri”“海象肯尼”) (“7arte”“davidatenas jeff rol frederico CarlosIsaksen Luca raphael caue”) (“c”“罗德里戈”) (“伪”“Igor FilipePinheiro rol Peter Aldo caue Andre”) (“玛雅”“安德烈”) (“1997”“davidatenas anão Erick henrique Peter CarlosIsaksen William Luca tony Jost”) (“hq”“anão CarlosIsaksen Jost”) (“pc”“威廉·卢卡·艾伦”) (“米尔”“彼得·奥尔多·安德烈·艾伦”) (“gtk”“杰夫·埃里克·恩里克·弗雷德里科·彼得·卡洛斯·伊萨克森·古斯塔沃肯史诗丹尼尔·GP”) (“点燃”“FilipePinheiro mathias frederico Peter Luca GP”) (“等”“古斯塔沃普波”) ("tr" "古斯塔沃普波") (“平托”,“古斯塔沃普波”) (“esp”“davidatenas tony FelipeAugusto”) (“pr0n”“加布里埃尔丹尼尔赫伯特嗯”) (“rsrs”“anão Gabriel daniel caue Herbert um”) (“jo”“anão Erick mathias leandro CarlosIsaksen William Vitor Jost Alan Koma”) (“齐”“布鲁诺吉尔迭戈”) (“b”“Eduardo HHahaah anão Erick Igor rol leandro Aldo William Luca raphael Vitor daniel cue Herbert Jost bruno-gil Diego”))

      【讨论】:

        【解决方案4】:

        如果你知道*nix pipes,那么你就更熟悉functional programming,因为函数式编程将程序视为使用函数的应用对数据的连续转换。还记得学校数学中的函数组合吗?基本上,g ∘ f表示你先应用f,然后立即应用g(g ∘ f)(x) = g(f(x))。函数式程序是一个巨大的函数组合。还有一个 pipe is just a function composition,只是方向相反:数学中的 (g ∘ f)(x) 与命令行中的 x | f | g 相同。

        有一个第三方库dash.el 提供了用于列表和树转换的大量函数以及简化函数方法的函数和宏。其中之一是线程宏-&gt;&gt;,它模仿命令行管道:

        (->> '(1 2 3) (-map '1+) (-reduce '+)) ; returns 9
        ;; equivalent to (-reduce '+ (-map '1+ '(1 2 3)))
        

        因此,如果我们想通过串行应用操作来操作文本数据,我们的函数可能如下所示:

        (defun key-value-swap (s)
          (->> s
               nil ; Split into lines
               nil ; Remove stars from each line
               nil ; Split each line
               nil ; Add 1st element as a value to each element starting from
                   ; 2nd as keys
               nil ; Return a hash-table
               ))
        

        完全符合您要求的函数将如下所示:

        (defun key-value-swap (s)
          (let ((h (make-hash-table :test 'equal)))
            (->> s
                 s-lines ; split into lines
                 (--map (s-split "\\(\\s-\\|:\\)" ; split each line
                                 (s-chop-prefix "** " it) ; throw away stars
                                 t))
                 (--map (-each (cdr it) ; for every field in the line, except 1st
                          (lambda (k) ; append 1st line to value under key
                            (puthash k (cons (car it) (gethash k h)) h)))))
            h)) ; return hash-table
        

        (puthash k (cons (car it) (gethash k h)) h) 看起来很神秘,但它只是意味着在哈希表中的每个键下都有一个列表,每次找到新值时都会附加到该列表中。所以如果b下有(Diego),我们发现bruno-gil也应该在b下,b下的值变成(bruno-gil Diego)

        【讨论】:

          【解决方案5】:

          之前的替代方案很有趣,但我不相信能抓住问题的“我将如何在 Emacs 中做到这一点作为最近的转换”方面。我怀疑学习 Emacs 并着眼于使用 Emacs Lisp 完成整个工作的人可能会从以下内容开始:

          (defun create-tags-to-name (buffer-name)
            "Create a buffer filled with lines containg `** TAG:
          LIST-OF-NAMES' by transposing lines in the region matching the
          format `** NAME: LIST-OF-TAGS' where the list items are white
          space separated."
            (interactive)
            (let ((buf (get-buffer-create buffer-name))
              (tag-to-name-list (list))
              name tags element)
              ;; Clear the destination buffer
              (with-current-buffer buf
                (erase-buffer))
              ;; Build the list of tag to name associations.
              (while (re-search-forward "^** \\([-a-zA-Z0-9 ]+\\):\\(.+\\)$" (point-max) t)
                (setq name (buffer-substring (match-beginning 1) (match-end 1))
                  tags (split-string (buffer-substring (match-beginning 2) (match-end 2))))
                ;; For each tag add the name to the tag's name list
                (while tags
              (let ((tag (car tags)))
                (setq element (assoc tag tag-to-name-list)
                  tags (cdr tags))
                (if element
                    (setcdr element (append (list name) (cdr element)))
                  (setq tag-to-name-list (append (list (cons tag (list name))) tag-to-name-list))))))
              ;; Dump the associations to the target buffer
              (with-current-buffer buf
                (while tag-to-name-list
              (setq element (car tag-to-name-list)
                    tag-to-name-list (cdr tag-to-name-list))
              (insert (concat "** " (car element) ":"))
              (let ((tag-list (cdr element)))
                (while tag-list
                  (insert " " (car tag-list))
                  (setq tag-list (cdr tag-list))))
              (insert "\n")))))
          

          【讨论】:

            【解决方案6】:

            这是我的第二次尝试。我写了一个小宏和一些函数来处理这些数据。

            (defun better-numberp (s) (字符串匹配 "^ *[0-9.,]* *$" s)) (defmacro 类似 awk (&rest args) (让((arg(汽车(最后一个args))) (调用 (mapcar #'(lambda (l) (条件 ((numberp (first l)) (cons `(lambda (f) (equal %r ,(first l))) (rest l))) ((stringp (first l)) (cons `(lambda (f) (string-match ,(first l) %)) (rest l))) (t l))) (但最后一个参数)))) `(mapcar #'(lambda (%%) (让 ((%r 0)) (地图车 #'(λ (l) (setq %r (1+ %r)) (让 ((% l)) (dolist (tipo ',calls) (预测 (setq % (条件 ((funcall (first tipo) %) (eval (cadr tipo))) (t %))) (set (intern (format "%%%d" %r)) %))) %)) %%))) (mapcar #'(lambda (y) (split-string y " " t)) (拆分字符串,arg "\n" t))))) (defun hash-to-list (hashtable) “返回代表哈希表的列表。” (让(我的名单) (maphash (lambda (kk vv) (setq mylist (cons (list kk vv) mylist))) 哈希表) 我的列表 ) ) (defun append-hash (键值哈希表) (let ((current (getash key hashtable)))) (推送键 (条件 ((空当前)(列表值)) ((listp current) (cons value current)) (t 电流)) 哈希表))) (let ((foohash (make-hash-table :test 'equal))) (类似awk (2 (replace-regexp-in-string ":" "" %)) ((lambda (f) (> %r 2)) (追加哈希 % %2 foohash)) "** 迭戈:b QI ** 布鲁诺吉尔:b QI ** 科马:乔 ** 嗯:rsrs pr0n ** 费利佩奥古斯托:特别是 ** GustavoPupo:pinto tr 等 ** GP:点燃 gtk ** 艾伦:jo mil pc ** 约斯特:b hq jo 1997 ** 赫伯特:b rsrs pr0n ** 安德烈:maia mil 伪 ** 罗德里戈:c ** 原因:b rsrs 7arte 伪 **肯尼:cri gif ** 丹尼尔:gtk mu pr0n rsrs b ** 托尼:1997 年的 esp ** 维托:b jo mimimi ** 拉斐尔:b rpg 7arte ** Luca:b lit gnu pc prog mmu 7arte 1997 ** LZZ:一个qt ** 威廉:b an jo pc 1997 ** 史诗:gtk ** 阿尔多:b 伪 pol mil 毛皮 ** GustavoKyon:一个gtk ** CarlosIsaksen:1997 年 7arte gtk 总部 ** 彼得:pseudo pol mil est 1997 gtk lit lang ** 莱德罗:b jo cb ** 弗雷德里科:7arte lit gtk ** 角色:b 一个伪 mimimi 7arte ** 马蒂亚斯:jo lit ** 恩里克:1997 h gtk qt ** eumané:一个 qt ** 海象:cri de ** FilipePinheiro:点燃伪 ** 伊戈尔:伪 b ** 埃里克:b jo rpg q 1997 gtk ** 加布里埃尔:pr0n rsrs qt **乔治:克洛咪咪 ** anão: hq jo 1997 rsrs clô b ** 杰夫:7arte gtk ** davidatenas:7arte 1997 esp qt ** 哈哈哈:b ** 爱德华多:b ") (hash-to-list foohash))

            【讨论】:

            • 这可能只是一个吹毛求疵,但我发现在学习一门新语言时学习该语言惯用的那种代码缩进是有帮助的——该语言所期望的那种缩进和括号帮助我保持这种语言的流畅。而且,作为一个经常使用 lisp 的人,看到它以这种方式被夸大是不和谐的,就像看到 BASIC 或 FORTRAN 以外的语言中的所有大写代码一样。
            • 你是指宏还是哈希列表函数?如果是宏,你能告诉我如何正确缩进吗?该函数只是从 Xah Lee 的页面复制而来
            猜你喜欢
            • 2011-11-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-10-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多