ruby - Nokogiri 无法使用 UTF-16 声明输出 XML(理解和解决)

标签 ruby xml character-encoding nokogiri libxml2

总结

尝试读取和序列化具有 UTF-16 编码和声明的 XML 文档会导致 Nokogiri 在某个点后产生垃圾。

  1. 这是一个错误,还是对此有合理的解释?
  2. 避免它的最佳方法是什么?

环境

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>

最佳答案

而不是序列化 xml 然后调用 encode在字符串上,您可以指定要在 options to to_xml 中使用的编码;而不是

xml1 = doc1.to_xml.encode('utf-8')

使用:

xml1 = doc1.to_xml(:encoding => 'utf-8')

这似乎解决了问题。


至于怎么回事,我只能提供一些观察。

首先,to_xml产生的字符串的编码不指定编码是UTF-16 ,这在 Ruby 中是一种“虚拟编码”(不管是什么意思):

xml1 = doc1.to_xml
p xml1.encoding
#=> #<Encoding:UTF-16 (dummy)>

关于 dummy encodings 的文档是这样说的:

A dummy encoding is an encoding for which character handling is not properly implemented. It is used for stateful encodings.

我注意到的另一件事是输出的 munged 部分中的值实际上对应于应该出现的代码点。

Hello there.\uFFFE\u3C00\u0000\u2F00\u0000\u4A00\u0000\u6900...

3C< , 2F/ , 4AJ , 69i等等,生产(如果你忽略零和额外的 BOM)

Hello there.</Ji...

如果您在编码为 UTF-8 之前写出 Nokogiri 生成的 XML)并用一个十六进制编辑器指向它,开头看起来像这样:

0000000 ff fe 3c 00 3f 00 78 00 6d 00 6c 00 20 00 76 00

FF FE开头,即小端 BOM。

在 munging 开始时,它看起来像这样:

0001f20 3c 00 4a 00 69 00 6d 00 3e 00 4f 00 68 00 21 00
0001f30 20 00 48 00 65 00 6c 00 6c 00 6f 00 20 00 74 00
0001f40 68 00 65 00 72 00 65 00 2e 00 fe ff 00 3c 00 00
0001f50 00 2f 00 00 00 4a 00 00 00 69 00 00 00 6d 00 00
0001f60 00 3e 00 00 00 0a 00 00 00 3c 00 00 00 2f 00 00

fe ff是 munged 输出开始的地方(在中间一行)。 fe ff也是 big 字节序 BOM,其他字符似乎是 BE(您可以看到零列如何在 fe ff 前后不对齐。有额外的零对字符之间的字节。

关于ruby - Nokogiri 无法使用 UTF-16 声明输出 XML(理解和解决),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12162548/

相关文章:

xml - android包含标签 - 无效的布局引用

javascript - 如何在 JavaScript 文件中正确编写 servlet url

character-encoding - 什么是最有效的二进制到文本编码?

c++ - 为什么此文件中的字符在C++(VS2017)中读为负字符代码?

c++ - 如何在 MSVC 中的原始字符串文字中使用 Unicode 符号/表情符号

ruby - 如何合并两个数组,其中数组中的值在 1 中?

ruby - 处理在 Ruby 线程中引发的异常

java - Android - 如何选择没有本地化的 strings.xml

ruby - 太阳黑子/Solr : indexing a time field results in NumberFormatException

ruby - Ruby block 是如何实现的(在编译器中)