6

概要

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 ファイルがあり、エンコードが UTF-16 であることを示す PI XML 宣言が先頭に含まれています。要約すると、次のようになります。

<?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 テキストからいくつかの単語を変更せずに追加および削除できますが、特定のポイントより下のテキストを削除すると、出力が突然修正されます。)

ただし、ノコギリ文書は壊れていません。独立して<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>
4

1 に答える 1

3

xmlをシリアル化してから文字列を呼び出すのではなく、;のオプションencodeで使用するエンコーディングを指定できます。それ以外の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)>

ドキュメントはダミーエンコーディングについてこれを言っています:

ダミーエンコーディングは、文字処理が適切に実装されていないエンコーディングです。ステートフルエンコーディングに使用されます。

私が気付いたもう1つのことは、出力の変更された部分の値が、実際に表示されるはずのコードポイントに対応していることです。

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

3Cis <2Fis /4Ais J69is ietc、生成(ゼロと余分なBOMを無視した場合)

Hello there.</Ji...

UTF-8にエンコードする前にNokogiriによって生成されたXMLを書き出し、16進エディターをポイントすると、開始は次のようになります。

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

それはFF FE、つまり、リトルエンディアンのBOMで始まります。

マンギングが始まる時点では、次のようになります。

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変更された出力が開始する場所です(中央の行)。fe ffビッグfe ffエンディアンのBOMでもあり、他の文字はBEのようです(ただし、文字の間にゼロバイトの余分なペアがあります。ゼロの列が前後に並んでいないことがわかります。

于 2012-09-05T18:19:39.547 に答える