如何在minidom中禁用'输出转义
我正在尝试使用 xml.dom.minidom
从头开始构建一个 XML 文档。一切进展顺利,直到我想在文本节点中加入一个 ®(注册商标)符号。我的目标是,当我最终执行 print mydoc.toxml()
时,这个特定的节点能够真正包含一个 ® 符号。
我首先尝试了:
import xml.dom.minidom as mdom
data = '®'
结果出现了一个很明显的错误:
File "C:\src\python\HTMLGen\test2.py", line 3
SyntaxError: Non-ASCII character '\xae' in file C:\src\python\HTMLGen\test2.py on line 3, but no encoding declared; see http://www.python.or
g/peps/pep-0263.html for details
当然,我也尝试过通过在脚本开头加上编码声明,把 Python 脚本的编码改成 'utf-8',但这并没有解决问题。
所以我想到了:
import xml.dom.minidom as mdom
data = '®' #Both accepted xml encodings for registered trademark
data = '®'
text = mdom.Text()
text.data = data
print data
print text.toxml()
但是因为当我打印 text.toxml()
时,& 符号被转义了,所以我得到了这样的输出:
®
®
我的问题是,有没有办法强制输出中的 & 符号不被转义,这样我就可以让我的特殊字符引用在 XML 文档中保留下来?
基本上,对于这个节点,我希望 print text.toxml()
能够愉快地输出 ®
或 ®
!
编辑 1:
顺便说一下,如果 minidom 确实没有这个功能,我很乐意使用你推荐的其他模块。
编辑 2:
正如 Hugh 建议的那样,我尝试使用 data = u'®'
(同时在数据中使用 # -*- coding: utf-8 -*-
的 Python 源代码标签)。这在某种程度上有帮助,因为它确实使 ® 符号被输出到我的 XML 中。但这并不是我想要的结果。正如你们可能已经猜到的(也许我应该早些说明),这个 XML 文档实际上是一个 HTML 页面,需要在浏览器中正常工作。所以在文档中有 ® 符号会导致浏览器出现乱码(具体来说是 ®
!)。
我还尝试了:
data = unichr(174)
text.data = data.encode('ascii','xmlcharrefreplace')
print text.toxml()
但当然,这又导致了最初的问题,所有的结果都是 & 符号被 .toxml()
转义了。
我理想的情况是有某种方法可以转义 & 符号,这样 XML 打印功能就不会为我“转义”它(换句话说,实现我最初的目标,让 ®
或 ®
出现在文档中)。
看起来我很快就得求助于正则表达式了!
编辑 2a:
或者也许不是。似乎正确设置我的 HTML 元信息 <META http-equiv="Content-Type" Content="text/html; charset=UTF-8">
可能会有所帮助,但我还不确定这如何与 XML 结构相结合……
3 个回答
默认的反转义:
from xml.sax.saxutils import unescape
unescape("< & >")
结果是,
'< & >'
还有,更多的反转义:
unescape("' "", {"'": "'", """: '"'})
详细信息请查看这里, https://wiki.python.org/moin/EscapingXml
如果我理解得没错,你真正想要的是能够从一个 unicode
对象(比如 u'®'
或 u'\u00ae'
)创建一个文本节点,然后让 toxml()
输出的unicode字符以实体的形式编码(比如 ®
)。不过,从 minidom.py 的源代码来看,minidom 似乎不支持除了 &
、"
、<
和 >
这几个特殊情况之外的实体编码。
你还问到了其他可能有帮助的模块。其实有几个可选的模块,但 ElementTree(xml.etree)似乎可以进行合适的编码。例如,如果你从 Doug Hellmann 的这篇博客 中拿到第一个例子,然后把:
child_with_tail.text = 'This child has regular text.'
... 替换为:
child_with_tail.text = u'This child has regular text \u00ae.'
... 然后运行这个脚本,你应该能看到输出中包含:
This child has regular text®.
你也可以在这个例子中使用 lxml 的 ElementTree 实现,只需把导入语句替换为:
from lxml.etree import Element, SubElement, Comment, tostring
更新:John Machin 提供的另一个 答案 采用了一个不错的方法,就是对 minidom 的 toxml()
输出运行 .encode('ascii', 'xmlcharrefreplace')
,这样可以把任何非 ASCII 字符转换成它们对应的 XML 数字字符引用。
这里有两种可行的选择,一种是用转义字符 ®
,另一种是不使用转义字符。其实不太明显为什么需要用转义字符……因为用转义字符的方式占用6个字节,而不使用转义字符的方式只占用2或3个字节,对于非中文、日文、韩文的字符来说。
import xml.dom.minidom as mdom
text = mdom.Text()
# Start with unicode
text.data = u'\xae'
f = open('reg1.html', 'w')
f.write("header saying the file is ascii")
uxml = text.toxml()
bxml = uxml.encode('ascii', 'xmlcharrefreplace')
f.write(bxml)
f.close()
f = open('reg2.html', 'w')
f.write("header saying the file is UTF-8")
xml = text.toxml(encoding='UTF-8')
f.write(xml)
f.close()