我使用以下源文件打印我的测试XML文件,但它不能正确处理非ASCII字符:
XML test.朋友:
import xml.sax.xmlreader
import xml.sax.saxutils
def testJunk(file, e2content):
attr0 = xml.sax.xmlreader.AttributesImpl({})
x = xml.sax.saxutils.XMLGenerator(file)
x.startDocument()
x.startElement("document", attr0)
x.startElement("element1", attr0)
x.characters("bingo")
x.endElement("element1")
x.startElement("element2", attr0)
x.characters(e2content)
x.endElement("element2")
x.endElement("document")
x.endDocument()
如果我做
>>> import xmltest
>>> xmltest.testJunk(open("test.xml","w"), "ascii 001: \001")
然后我得到一个字符代码为001的xml文件。我无法弄清楚如何逃避这个角色。 Firefox告诉我它不是格式良好的XML并抱怨该角色。我怎样才能解决这个问题?
澄清:我正在尝试记录一个我无法控制的函数的输出,它输出非ASCII字符。
更新:好的,所以现在我知道characters outside one of the accepted ranges不能以
的形式编码。 (或者更确切地说,它们可以被编码,但这并不能帮助任何w / r / t XML格式不正确。)但是如果我定义了这样做的方法,它们就可以被转义。
(供将来参考:W3C在XML标准之外有a useful page,它说“控制代码应该用适当的标记替换”,但并没有真正建议这样做的例子。)
如果我想以下列方式转义可接受范围之外的字符:
在转义之前:(
代表一个字符,而不是字面上的8个字符的字符串)
abcdefghijkl
逃跑后:
abcd<u>0001</u>efgh<u>0002</u>ijkl
我怎么能在python中这样做?
def escapeXML(src)
dest = ??????
return dest
"\001"
又名“\ x01”是ASCII控制代码。但它不是允许的XML字符之一。唯一的ASCII control codes which qualify是“\ t”,“\ n”和“\ r”。
例子:
>>> import xml.etree.cElementTree as ET
# Raw newline works
>>> t = ET.fromstring("<e>\n</e>")
>>> t.text
'\n'
# Hex escaping of a newline works
>>> t = ET.fromstring("<e>
</e>")
>>> t.text
'\n'
# Hex escaping of "\x01" doesn't work; it's not a valid XML character
>>> t = ET.fromstring("<e></e>")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 106, in XML
cElementTree.ParseError: reference to invalid character number: line 1, column 3
如果要在XML文档中以某种方式包含无效的XML字符,则必须通过额外的转义级别从XML解析器中隐藏它们。该机制需要由您的文档的读者记录,发布和理解。
例如,在Microsoft Excel 2007+ XLSX文件中,非有效XML字符的Unicode代码点通过将它们表示为_xhhhh_
而被遍历解析器,其中hhhh
是代码点的十六进制表示形式。在你的例子中,这将是7个字节的_x0001_
。请注意,有必要转义文本中的任何_
字符,否则将被错误地解释为引入_xhhhh_
序列。
这是丑陋,痛苦,低效等等。您可能希望考虑其他方法。是否需要使用XML? CSV文件(震惊,恐怖!)会在您的应用程序中做得更好吗?
编辑有关OP编码建议的一些注意事项:
答:虽然\r
是一个有效的XML 1.0输入字符,但它受mandatory immediate transmogrification的限制,因此您也应该将其转义。
B.该方案假设/希望<u>hhhh</u>
不会与任何其他标记混淆。
C.我收回了我上面所说的有关Microsoft转义方案的内容。它相对美丽,无痛,高效。要为温和的读者完成您的方案图片,您应该显示需要的代码来解决讨厌的碎片并将碎片粘合在一起。请记住,MS方案需要有人编写一个转义函数和一个非转义函数,而您的方案需要对每个工具(SAX,DOM,ElementTree)进行不同的处理。
D.在详细级别,代码有点漂亮:
if (len(g1) > 0):
应该是if g1:
if (not foo == None):
与普遍接受的成语有三个偏差记录:(1)括号(2)not x == y
而不是x != y
(3)!= None
而不是is not None
不要将list
(以及其他内置对象的名称)用作您自己的变量的名称。
编辑2您想使用正则表达式拆分字符串。为什么不使用re.split?
splitInvalidXML2 = re.compile(
ur'([^\x09\x0A\x0D\x20-\x7E\x85\xA0-\xFF\u0100-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD])'
).split
def submitCharacters2(x, string):
badchar = True
for fragment in splitInvalidXML2(string):
badchar = not badchar
if badchar:
x.startElement("u", attr0)
x.characters('%04X' % ord(fragment))
x.endElement("u")
elif fragment:
x.characters(fragment)
这似乎对我有用。
r = re.compile(ur'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\xFF' \
+ ur'\u0100-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]')
def escapeInvalidXML(string):
def replacer(m):
return "<u>"+('%04X' % ord(m.group(0)))+"</u>"
return re.sub(r,replacer,string)
例:
>>> s='this is a \x01 test \x0B of something'
>>> escapeInvalidXML(s)
'this is a <u>0001</u> test <u>000B</u> of something'
>>> s2 = u'this is a \x01 test \x0B of \uFDD0'
>>> escapeInvalidXML(s2)
u'this is a <u>0001</u> test <u>000B</u> of <u>FDD0</u>'
字符范围从http://www.w3.org/TR/2006/REC-xml-20060816/#charsets,我没有逃过一切,只有下面的\ uFFFF。
更新:糟糕,忘了适应SAX的startElement / characters方法,并正确处理多行:
import re
import xml.sax.xmlreader
import xml.sax.saxutils
r = re.compile(ur'(.*?)(?:([^\x09\x0A\x0D\x20-\x7E\x85\xA0-\xFF' \
+ ur'\u0100-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD])|([\n])|$)')
attr0 = xml.sax.xmlreader.AttributesImpl({})
def splitInvalidXML(string):
list = []
def replacer(m):
g1 = m.group(1)
if (len(g1) > 0):
list.append(g1)
g2 = m.group(2)
if (not g2 == None):
list.append(ord(g2))
g3 = m.group(3)
if (not g3 == None):
list.append(g3)
return ""
re.sub(r,replacer,string)
return list
def submitCharacters(x, string):
for fragment in splitInvalidXML(string):
if (isinstance(fragment,int)):
x.startElement("u", attr0)
x.characters('%04X' % fragment)
x.endElement("u")
else:
x.characters(fragment)
def test1(fname):
with open(fname,'w') as f:
x = xml.sax.saxutils.XMLGenerator(f)
x.startDocument()
x.startElement('document',attr0)
submitCharacters(x, 'this is a \x01 test\nof the \x02\x0b xml system.')
x.endElement('document')
x.endDocument()
test1('test.xml')
这会产生:
<?xml version="1.0" encoding="iso-8859-1"?>
<document>this is a <u>0001</u> test
of the <u>0002</u><u>000B</u> xml system.</document>
这个https://bugs.python.org/issue5166有一个开放的python错误 - 还不确定解决方案是什么/它是否会被修复,因为它已经开放了一段时间,但值得定期检查,以防我们得到一个处理无效XML字符的正确解决方案内置于python本身。