外部DTD内のエンティティをUTF-8にISO-8859-1からの大規模なXMLを変換

einsweniger:

私が持っています:

  • ISO-8859-1で圧縮されていないXMLの2.2GiB、始まります

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE dblp SYSTEM "dblp-2017-08-29.dtd">

  • 次のようにエンティティを定義し、対応するDTD

<!ENTITY Aacute "&#193;" ><!-- capital A, acute accent -->

  • RAMに解析されたXMLに適合しないことができるコンピュータ

が欲しいです

既にセットアップされており、作業のApache Solrの、にXMLをインポートするには。Solrの/ Javaは(当然)私は設定することで上げることができますについてあまりにも多くの拡大実体、文句を言うでしょう-DentityExpansionLimit=2000000JVMのために、私は編集すると上限を上げるために輸入を持っていると思いますSystem::setProperty

私は試した

xmllint

xmllint --stream --loaddtd --encode utf8 --output dblp.utf8.xml dblp-2018-07-01.xml

なし--streamそれはメモリに構造を解析しようとして、プロセスカーネルによって殺されます。

--stream、それは出力ファイルを書き込みません、私はそれが唯一のDTDに対してXMLを検証しています疑い。

編集XML、パイソン

私は、PythonでDTDをインポートし、私はにエンティティを入れているので、パーサでそれを使用する方法を見つけ出すことができませんでした<!DOCTYPE dblp [ … ]>し、その後、

import xml.etree.ElementTree
f = open('dblp-2018-07-01.xml')
out = open('dblp.utf8.xml','wb')
xml.etree.ElementTree.parse(f).write(out, encoding='UTF-8')

これは、メモリの約11ジブを消費します私の作品が、:

細部

私はので、ここで私が持っているしたいものだ、私がやっているものを再現するために他人をしたいです:

  • 無エンティティを挿入するために、手動でXMLを編集
  • エンコーディングを変換することができますスクリプトまたはコンパイルされたプログラム
  • 可能な限り少ないメモリとして使用し、6ジブの下にそれを維持しよう
  • 追加ボーナスは、読み取りおよび節約スペースにgzip圧縮されたファイルへの書き込みが、それは必要はありませんされます。

(私はSolrのにインポート処理を組み込むことができるように)私はプログラムで解決のためのJavaを好むだろうが、私は喜んで(私はJavaScriptを避けたいのですが)、他のソリューションを取りますよ。

あなたはXMLを自分で周りのネジにしたい場合は、ファイルはここにあります:

gzipで圧縮されたファイルのサイズは430MiB程度であり、XMLの2.2GiBに展開されます。

ありがとうございました!

einsweniger:

私はそれが遅い少しだ、ソリューションを自分自身を発見した(〜11-12分)が、私はそれで大丈夫ですよ。

import javax.xml.stream.*;
import java.io.*;
import java.util.zip.*;

public class ConvertToUtf8 {

  public static void main(String[] args) {
    System.setProperty("entityExpansionLimit", "10000000");
    XMLInputFactory inputFactory = XMLInputFactory.newFactory();
    XMLOutputFactory outputFactory = XMLOutputFactory.newFactory();

    try (
        FileInputStream ifs = new FileInputStream("dblp-2018-08-01.xml.gz"); 
        GZIPInputStream gzIn = new GZIPInputStream(ifs);
        FileOutputStream ofs = new FileOutputStream("dblp_utf8.xml.gz");
        GZIPOutputStream gzOut = new GZIPOutputStream(ofs, true);
        ) 
    {
      XMLEventReader inEvt = inputFactory.createXMLEventReader(gzIn);
      XMLEventWriter outEvt = outputFactory.createXMLEventWriter(gzOut, "UTF-8");
      outEvt.add(inEvent);
    } catch (IOException | XMLStreamException e) {
      e.printStackTrace();
    }
  }
}

でGZIPを使用して/アウト(6倍高速私のマシン上の)システムの残りの部分をボトルネックになり、ディスクからの読み取りなどのプロセスを大幅にスピードアップします。あなたが複製する場合は、必ずDTDは、そうでないエンティティは置き換えられません、作業ディレクトリにあることを確認してください。Javaは、それはそれ以外の場合はDTDを見つけることができないことを示す、XMLにコメントを挿入します。

@janbrohlの答えにより構築:

#! python3
import re
import gzip
from lxml import etree

# read the DTD with the lxml parser
dtd = etree.DTD('dblp-2017-08-29.dtd')
# build a dict with it for lookup
replacements = {x.name: x.content for x in dtd.entities()}

entity_re=re.compile('&(\w+);')

def resolve_entity(m):
    """
    This will replace the defined entities with their expansions from the DTD:
    '&Ouml;' will be replaced with '&#214;'.
    The entities that are already escaped with '&#[0-9]+;' should not be expanded,
    Ex: if some of the escapes produced the character '<', the XML would no longer be well formed.

    If the matched entity is not in the replacements, use the match as default
    """
    return replacements.get(m.group(1),f'&{m.group(1)};')

def expand_line(line):
    return entity_re.sub(resolve_entity,line)

def recode_file(src,dst):
    with gzip.open(src,mode='rt', encoding='ISO-8859-1', newline='\n') as src_file:
        # discard first line with wrong encoding
        print('discard: ' + src_file.readline())  
        with gzip.open(dst, mode='wt', encoding='UTF-8', newline='\n') as dst_file:
            # replace with correct encoding statement
            dst_file.write('<?xml version="1.0"?>\n')  
            for line in src_file:
                dst_file.write(expand_line(line))

recode_file('dblp-2018-08-01.xml.gz','dblp_recode.xml.gz')

私は、正規表現置き換えによって生成された出力をインポート動作しているようだしました:Dは確かに、それはより速くJavaバージョンよりもですが、結果のXMLは、実際のパーサを通過したバージョンと同じである場合、私はまだ不確実です。私は少し実験ます。

編集:いくつかの実験の後、私はデータを変更するかもしれないいくつかのエッジケースを発見しました。それは速いですので、私は、ここではPythonスクリプトを残しておきます。しかし、私は実際にパーサを使用しているバージョンを使用して好む:それは、従うことは簡単だだけで、標準ライブラリの使用と保守が容易です。エッジケースでは、C ++のマップで行いたいように私はPythonの辞書を使用し、私のせいだった:アクセスはどこreplacements['val']C ++でのエントリを作成し、replacements.at('val')スローされます。:Pythonで、それは他の方法でラウンドですreplacements['val']、スローされますreplacements.get('val')しませんし、何のデフォルトが提供されなかった場合は、空の文字列を返します。

私は、誰かが速く解決策を見つけることができる場合には、少し長くするために、このオープンを残しておきます。編集:D:誰かがXMLパーサを使用するより早く解決策を見つけることができる場合

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=221299&siteId=1