【发布时间】:2012-08-28 15:25:09
【问题描述】:
总结
尝试读取和序列化具有 UTF-16 编码和声明的 XML 文档会导致 Nokogiri 在某个时间点之后产生垃圾。
- 这是一个错误,还是对此有合理的解释?
- 避免它的最佳方法是什么?
环境
C:\>nokogiri -v
# Nokogiri (1.5.5)
---
warnings: []
nokogiri: 1.5.5
ruby:
version: 1.9.3
platform: i386-mingw32
description: ruby 1.9.3p194 (2012-04-20) [i386-mingw32]
engine: ruby
libxml:
binding: extension
compiled: 2.7.7
loaded: 2.7.7
详情
我有一个用 UTF-16(LE) 编码的 XML 文件,它还在顶部包含一个 PI XML 声明,表明编码是 UTF-16。总结起来是这样的:
<?xml version="1.0" encoding="UTF-16" ?>
<Foo>
<Bar><![CDATA[
Lorem ipsum dolor ...about 3900 more bytes of content here...
]]></Bar>
<Jim>Oh! Hello there.</Jim>
</Foo>
当我使用 Nokogiri 阅读此文档时,一切似乎都很好:
xml = File.open('Simplified.xml','rb:utf-16le',&:read)
p xml.encoding # #<Encoding:UTF-16LE>
p xml.valid_encoding? # true
doc1 = Nokogiri.XML(xml,&:noblanks)
xml1 = doc1.to_xml.encode('utf-8')
p xml1.encoding # #<Encoding:UTF-8>
p xml1.valid_encoding? # true
但是,序列化文档的输出在某个时间点之后会变得模糊:
p xml1 # Correct contents of CDATA removed from the following output
#=> "<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n<Foo>\n <Bar><![CDATA[\n...\n\t]]></Bar>\n <Jim>Oh! Hello there.\uFFFE\u3C00\u0000\u2F00\u0000\u4A00\u0000\u6900\u0000\u6D00\u0000\u3E00\u0000\u0A00\u0000\u3C00\u0000\u2F00\u0000\u4600\u0000\u6F00\u0000\u6F00\u0000\u3E00\u0000\u0A00\u0000"
(限制似乎与字符数有关。我可以从 Lorem ipsum 文本中添加和删除几个单词而无需更改,但是删除某个点以下的文本会突然修复输出。)
然而,Nokogiri 文件并未损坏。我可以独立序列化<Jim>成功:
puts doc1.at('Jim').to_xml.encode('utf-8')
#=> <Jim>Oh! Hello there.</Jim>
我发现的唯一解决方法是在解析文档之前删除文档顶部的 XML 声明。有了这个,一切都按预期工作:
decl = '<?xml version="1.0" encoding="UTF-16" ?>'.encode('UTF-16LE')
doc2 = Nokogiri.XML(xml.sub(decl,''),&:noblanks)
puts doc2.to_xml.encode('utf-8')
#=> <?xml version="1.0"?>
#=> <Foo>
#=> <Bar><![CDATA[
#=> Lorem ipsum dolor...and more...
#=> ]]></Bar>
#=> <Jim>Oh! Hello there.</Jim>
#=> </Foo>
完整的 XML
这是您自己测试的完整文件:
<?xml version="1.0" encoding="UTF-16" ?>
<Foo>
<Bar><![CDATA[
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ac augue arcu, eget laoreet lorem. Quisque ac augue velit. Integer consectetur suscipit vehicula. Etiam et convallis enim. Etiam varius massa sit amet lacus rhoncus varius in non ante. Sed dictum, metus eu bibendum ornare, ligula dui commodo urna, ut dignissim felis dolor eget nisl. Proin sit amet nisi nunc. Vestibulum a urna sed dui dignissim blandit nec vel enim. Vivamus tincidunt nulla id dui hendrerit hendrerit.
Aliquam neque orci, luctus sit amet fringilla eu, varius vitae diam. Suspendisse varius rutrum lorem eget malesuada. Sed dapibus dapibus nisl, in cursus ante lacinia non. Aenean id sagittis ipsum. Suspendisse elit nunc, porta sit amet blandit ut, laoreet sed est. Nunc eget sem vitae nisl elementum ullamcorper ut sit amet urna. Sed ligula quam, fringilla in facilisis tincidunt, vehicula in nisi. Maecenas a augue in augue semper scelerisque sit amet ut arcu.
Praesent hendrerit, enim in elementum ornare, lorem nisi euismod dolor, sit amet ornare mi sem sodales lacus. Fusce et tempor mauris. In non quam nisl, non consequat diam. Duis sit amet massa ultrices massa cursus iaculis. Nunc ullamcorper malesuada sem dignissim semper. Fusce aliquet lacus quis nisi tincidunt sodales. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque posuere commodo aliquet. Aliquam blandit vestibulum facilisis. Sed pellentesque viverra dignissim. Etiam est lacus, mollis eu pretium vitae, lacinia eleifend augue. Mauris vitae quam nisl. In venenatis nunc ac eros elementum cursus.
Sed a metus sit amet nunc euismod condimentum id non orci. Curabitur velit turpis, lacinia non eleifend sed, rhoncus id est. Fusce ut massa dolor, ut sodales odio. Donec aliquam convallis tellus, eu pharetra tortor iaculis non. Integer imperdiet feugiat ipsum a gravida. Mauris sapien ipsum, ultricies ac placerat ut, imperdiet eu justo. Quisque quis consectetur velit. Etiam facilisis sapien nec enim tincidunt pulvinar. Duis fermentum faucibus felis, sed consequat libero pretium at. Phasellus nibh purus, suscipit in vestibulum vel, blandit at leo. Suspendisse placerat elit sed enim bibendum vel hendrerit mauris pretium. Maecenas ut lacus eu nisi euismod pretium.
Aliquam feugiat felis id massa aliquam pharetra sed non eros. Morbi interdum molestie iaculis. Curabitur varius ante ac dui dapibus non laoreet risus blandit. Nunc sit amet magna lacus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus egestas nunc sed turpis imperdiet a rhoncus massa aliquam. Nulla facilisi. Phasellus sit amet neque felis, nec vestibulum massa. Donec luctus fringilla dolor et gravida. Phasellus euismod lectus eget elit hendrerit non vehicula tellus venenatis. Phasellus sit amet ligula et purus dignissim feugiat at vitae libero. Proin ut tortor eros, quis laoreet lectus. Quisque nec urna mattis ante gravida fermentum eu at nibh. Phasellus sapien elit, tincidunt quis laoreet id, lobortis sed magna. Aliquam pulvinar erat eu sapien pretium bibendum. Maecenas eleifend, leo quis sodales tincidunt, leo felis tristique dolor, vitae ultrices neque felis ut metus.
Etiam dignissim egestas ipsum, eget tempor ipsum rutrum eu. Donec vehicula eleifend ullamcorper. Mauris justo nulla, varius a mattis a, cursus sit amet risus. Phasellus rutrum interdum blandit. Donec ut justo eros, ut auctor dolor. Suspendisse potenti. Cras ultricies, dui eget mattis bibendum, leo dui luctus purus, sit amet rhoncus libero metus eget purus. Pellentesque scelerisque ornare sapien faucibus tempor.
Suspendisse potenti. Proin fermentum bibendum dapibus. Pellentesque facilisis aliquam. Nam egestas tellus non mauris scelerisque feugiat pellentesque lacus dignissim. Quisque id nulla felis. Mauris justo mauris, posuere sed facilisis in, venenatis nec risus. Mauris eu dui sed tellus laoreet tempor a in turpis volutpat.
]]></Bar>
<Jim>Oh! Hello there.</Jim>
</Foo>
【问题讨论】:
-
假设这是一个错误,我已经提交了a bug report。
-
"它还在顶部包含一个 PI,指示编码是 UTF-16。"我猜你的意思是它是一个BOM,对吧?
\uFFFE是一个 BOM,但没有指定字符的长度。查看检查中的以下字节,让我觉得您正在处理 UTF-32LE,而不是 16,因为我们不能完全信任encoding=语句,但是,这可能是 libxml 或 Nokogiri 代码的结果,而不是文件本身。尝试od查看文件中的字符和字节,特别是问题所在的区域。那个位置的文件有什么奇怪的地方吗? -
您可以使用stackoverflow.com/questions/12085250/… 对问题进行三角测量。听起来可能是一个类似的问题。
-
@theTinMan 该文件在磁盘上编码为带有 BOM 的 UTF-16LE,并且(如上所示)文件的第一行具有
<?xml version="1.0" encoding="UTF-16" ?>。正是这条第一线处理指令的存在导致了 Nokogiri 的序列化问题。 -
十六进制转储文件的第一个字节是:
FF FE 3C 00 3F 00 78 00 6D 00 6C 00 20 00 76 00;我们看到the two-byte BOM 后跟一系列双字节字符,其中这些 ASCII 字符的第二个字节为空。这有助于确认 UTF-16LE 编码是否有效。
标签: ruby xml character-encoding nokogiri libxml2