在 Emacs 上漂亮地打印 XML 文件

Posted

技术标签:

【中文标题】在 Emacs 上漂亮地打印 XML 文件【英文标题】:Pretty printing XML files on Emacs 【发布时间】:2010-09-05 23:08:37 【问题描述】:

我使用 emacs 编辑我的 xml 文件(nxml 模式),并且这些文件是由机器生成的,没有任何漂亮的标签格式。

我已经搜索了带有缩进的漂亮打印整个文件并保存它,但找不到自动方式。

有办法吗?或者至少有一些 Linux 上的编辑器可以做到这一点。

【问题讨论】:

【参考方案1】:

您甚至不需要编写自己的函数 - sgml-mode(一个 gnu emacs 核心模块)有一个名为 (sgml-pretty-print ...) 的内置漂亮打印函数,它需要区域开始和结束论据。

如果您正在剪切和粘贴 xml,并且发现您的终端在任意位置截断线条,您可以使用此 pretty printer,它首先修复断线。

【讨论】:

(sgml-pretty-print (region-beginning) (region-end)) 我不确定sgml-mode 随着时间的推移会发生怎样的变化。今天,我调用了C-x C-f foo.xmlM-x sgml-mode,然后是M-x sgml-pretty-print,我的 xml 文件打印得很漂亮。 (好吧,emacs 在完成之前挂了 20 秒或更长时间。在漂亮的打印之前是一个单行文件,之后是 720 行。) 其实我还得做C-x g来选择整个缓冲区作为一个区域。 我什至不必切换到 sgml 模式。这是 nXML 模式下的 M-x 命令! 使用 Emacs 26.2,我可以停留在 nXML 模式,选择整个缓冲区C-x h 然后M-x sgml-pretty-print。 xml 现在将被很好地格式化【参考方案2】:

如果您只需要漂亮的缩进而不引入任何新的换行符,您可以通过这些击键将indent-region 命令应用于整个缓冲区:

C-x h
C-M-\

如果您还需要引入换行符,以便开始和结束标签在不同的行上,您可以使用以下非常好的 elisp 函数,由 Benjamin Ferrari 编写。我在他的博客上找到了它,希望我可以在这里复制它:

(defun bf-pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    (goto-char begin)
    (while (search-forward-regexp "\>[ \\t]*\<" nil t) 
      (backward-char) (insert "\n") (setq end (1+ end)))
    (indent-region begin end))
  (message "Ah, much better!"))

这不依赖于像 Tidy 这样的外部工具。

【讨论】:

很好,谢谢。从上面的漂亮打印 d​​efun 中删除 (nxml-mode) 允许它在 emacs 22.2.1 内置的 sgml-mode 中工作。但我修改它以将整个缓冲区 (point-min) 改为 (point-max),因为这是我的主要工作。此外,还有一个错误:对于您插入的每个换行符,您都需要增加 end。 如何在 Emacs 中使用这个功能?我已经在 scratch 缓冲区中复制并粘贴了函数代码并对其进行了评估。现在,我该如何调用这个函数? 在评估了 defun 之后,您可以像调用任何其他函数一样调用它:M-x bf-pretty-print-xml-region。 (你不必全部输入,当然,使用制表符补全: Mx bf 应该就足够了。)你可能不想每次要使用它时都定义函数,所以把它放在某个地方它在开始时加载的位置,例如在 ~/.emacs.d/init.el 打破长属性列表怎么样? 这太棒了,因为 tidy 抱怨无效的字符编码并希望我在 重新格式化文件之前清理它们!有时重点是查看损坏的 xml 文件的结构,tidy 会拒绝提供帮助。【参考方案3】:

Emacs 可以使用 M-| 运行任意命令。如果您安装了 xmllint:

"M-| xmllint --format -" 将格式化所选区域

"C-u M-| xmllint --format -" 会做同样的事情,用输出替换区域

【讨论】:

在前面使用 M-x mark-whole-buffer 将整个缓冲区内容标记为要处理的区域。【参考方案4】:

当我想格式化和缩进 XML 或 html 时,我使用 nXML mode 进行编辑和 Tidy。还有an Emacs interface to Tidy.

【讨论】:

到 2013 年底 tidy.el 版本:20111222.1756 无法在 Emacs 24 上运行 wrong type argument: stringp, nil @keiw 这可能是因为您在没有文件名的缓冲区中执行此操作。遇到了同样的错误,至少在我这边是这样。【参考方案5】:

感谢上面的 Tim Helmstedt 我做了这样的 st:

(defun nxml-pretty-format ()
    (interactive)
    (save-excursion
        (shell-command-on-region (point-min) (point-max) "xmllint --format -" (buffer-name) t)
        (nxml-mode)
        (indent-region begin end)))

快速简单。非常感谢。

【讨论】:

这在 GNU Emacs 24 上给了我一个错误,所以我将最后一行更改为:(indent-region 0 (count-lines (point-min) (point-max)))【参考方案6】:

用于引入换行符,然后进行漂亮的打印

M-x sgml-mode
M-x sgml-pretty-print

【讨论】:

【参考方案7】:

以下是我对 Benjamin Ferrari 的版本进行的一些调整:

search-forward-regexp 没有指定结束,因此它将对从区域开始到缓冲区结束(而不是区域结束)的内容进行操作 正如 Cheeso 所指出的,现在可以正确递增 end。 它将在&lt;tag&gt;&lt;/tag&gt; 之间插入一个中断,从而修改其值。是的,从技术上讲,我们正在修改这里所有内容的值,但是空的开始/结束更可能是重要的。现在使用两个单独的、稍微更严格的搜索来避免这种情况。

仍然有“不依赖外部整洁”等。但是,incf 宏确实需要cl

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pretty print xml region
(defun pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    (goto-char begin)
    ;; split <foo><foo> or </foo><foo>, but not <foo></foo>
    (while (search-forward-regexp ">[ \t]*<[^/]" end t)
      (backward-char 2) (insert "\n") (incf end))
    ;; split <foo/></foo> and </foo></foo>
    (goto-char begin)
    (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
      (backward-char) (insert "\n") (incf end))
    (indent-region begin end nil)
    (normal-mode))
  (message "All indented!"))

【讨论】:

【参考方案8】:

一种方法是 如果你有以下格式的东西

<abc>     <abc><abc>   <abc></abc> </abc></abc>       </abc>

在 Emacs 中,尝试

M-x nxml-mode
M-x replace-regexp RET  > *< RET >C-q C-j< RET 
C-M-\ to indent

这会将上面的 xml 示例缩进到下面

<abc>
  <abc>
    <abc>
      <abc>
      </abc>
    </abc>
  </abc>
</abc>

在 VIM 中你可以这样做

:set ft=xml
:%s/>\s*</>\r</g
ggVG=

希望这会有所帮助。

【讨论】:

【参考方案9】:
    Emacs nxml-mode 可以处理呈现的格式,但您必须拆分行。 对于根本不值得的更长文件。运行这个样式表(最好使用 Saxon 恕我直言,哪个行缩进大约正确)针对更长的文件 得到一个漂亮漂亮的印刷品。对于您想要保留空白的任何元素 将他们的名字添加到“programlisting”旁边,就像“programlisting yourElementName”一样

HTH

【讨论】:

【参考方案10】:

我采用了Jason Viers' version 并添加了将 xmlns 声明放在自己的行中的逻辑。这假设您有 xmlns= 和 xmlns: 中间没有空格。

(defun cheeso-pretty-print-xml-region (begin end)
  "Pretty format XML markup in region. You need to have nxml-mode
http://www.emacswiki.org/cgi-bin/wiki/NxmlMode installed to do
this.  The function inserts linebreaks to separate tags that have
nothing but whitespace between them.  It then indents the markup
by using nxml's indentation rules."
  (interactive "r")
  (save-excursion
    (nxml-mode)
    ;; split <foo><bar> or </foo><bar>, but not <foo></foo>
    (goto-char begin)
    (while (search-forward-regexp ">[ \t]*<[^/]" end t)
      (backward-char 2) (insert "\n") (incf end))
    ;; split <foo/></foo> and </foo></foo>
    (goto-char begin)
    (while (search-forward-regexp "<.*?/.*?>[ \t]*<" end t)
      (backward-char) (insert "\n") (incf end))
    ;; put xml namespace decls on newline
    (goto-char begin)
    (while (search-forward-regexp "\\(<\\([a-zA-Z][-:A-Za-z0-9]*\\)\\|['\"]\\) \\(xmlns[=:]\\)" end t)
      (goto-char (match-end 0))
      (backward-char 6) (insert "\n") (incf end))
    (indent-region begin end nil)
    (normal-mode))
  (message "All indented!"))

【讨论】:

【参考方案11】:

Tidy 看起来是个不错的模式。必须看看它。如果我真的需要它提供的所有功能,我会使用它。

无论如何,这个问题困扰了我大约一个星期,我没有正确搜索。发布后,我开始搜索并找到一个带有elisp function 的网站,它做得很好。作者还建议使用 Tidy。

感谢 Marcel 的回答(太糟糕了,我没有足够的积分来升级你)

很快就会在我的博客上发布。这是post about it(带有 Marcel 网站的链接)。

【讨论】:

【参考方案12】:

我使用 xml-parse.elxml-reformat-tags。通常在运行这个命令时你会希望在文件的开头有这个点。

有趣的是,该文件被合并到Emacspeak 中。当我每天使用 Emacspeak 时,我认为 xml-reformat-tags 是 Emacs 内置的。有一天我把它弄丢了,不得不在网上搜索它,然后就进入了上面提到的wiki页面。

我还附上了我的代码以启动 xml-parse。不确定这是否是最好的 Emacs 代码,但似乎对我有用。

(if (file-exists-p "~/.emacs.d/packages/xml-parse.el")
  (let ((load-path load-path))
    (add-to-list 'load-path "~/.emacs.d/packages")
    (require 'xml-parse))
)

【讨论】:

【参考方案13】:

如果您使用spacemacs,只需使用命令“spacemacs/indent-region-or-buffer”。

M-x spacemacs/indent-region-or-buffer

【讨论】:

【参考方案14】:

自 2017 年起,emacs 已默认提供此功能,但您必须将这个小功能写入您的 ~/.emacs.d/init.el

(require 'sgml-mode)

(defun reformat-xml ()
  (interactive)
  (save-excursion
    (sgml-pretty-print (point-min) (point-max))
    (indent-region (point-min) (point-max))))

然后只需拨打M-x reformat-xml

来源:https://davidcapello.com/blog/emacs/reformat-xml-on-emacs/

【讨论】:

【参考方案15】:

恐怕我更喜欢本杰明法拉利版本。内部漂亮的打印总是将结束标记放在值之后的新行中,在标记值中插入不需要的 CR。

【讨论】:

以上是关于在 Emacs 上漂亮地打印 XML 文件的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Python 的 ElementTree 漂亮地打印到 XML 文件?

java 8中漂亮的打印XML

php CLI上简单漂亮的打印覆盖检查器。从终端中phpunit的coverage-clover xml文件打印指标。可用于整合机智

在 Python 中漂亮地打印 XML

XML日志文件打印到漂亮[重复]

如何从命令行漂亮地打印 XML?