2009-08-19 24 views
4

大量のデータを含む1つのオブジェクト(入れ子にされたコレクションを含む)からXMLファイルを生成したい。 XMLには制限があり、を超えることはできません.50MBを超えています。C#のファイルサイズの制限または制限

これを行うには良い方法はありますか?

更新:速度が

+0

残りの部分はどうしますか?あなたの出力ファイルはどのように見えますか? –

+0

50MBファイルに分割するまでに何か問題がありますか? –

答えて

1

あなたは、文字列のようなXMLファイルを足す代わりに.NETでXMLのサポートを使用して検討している主なものは、ファイルごとに50メガバイトに分割され、重要ではありません。

私は、ツールがそれを消費する唯一の方法であったため、10GBのデータをXMLに書き込んでいました。

私はこのような問題を抱えていましたが、私のXMLはとてもシンプルでした。私はTextWriterを使用し、ループをネストしてXMLを書きました。

魅力的なことに加えて、XMLオブジェクトよりもはるかに高速でした。

+0

すべてがxmlオブジェクトより高速です;) – NotMe

2

XmlWriterまたはXDocumentと大きなxmlファイルを問題なく書き込むことができます。

ここでは例を示します。この例では、63MBのxmlファイルを5秒未満で生成します。この例では、クラスXmlWriterを使用します。

using (XmlWriter writer = XmlWriter.Create("YourFilePath")) 
{ 
    writer.WriteStartDocument(); 

    writer.WriteStartElement("Root"); 

    for (int i = 0; i < 1000000; i++) //Write one million nodes. 
    { 
     writer.WriteStartElement("Root"); 
     writer.WriteAttributeString("value", "Value #" + i.ToString()); 
     writer.WriteString("Inner Text #" + i.ToString()); 
     writer.WriteEndElement(); 
    } 
    writer.WriteEndElement(); 

    writer.WriteEndDocument(); 
} 
+0

私はこのメソッドを使用してうまく動作する複数のギガバイトのxmlファイルを書き込んだり読んだりしています。余分なクレジットのために、ファイルを圧縮するためにGzipStreamを通してそれを接続することができます... –

2

私の仕事で同様の要件があります。私の最善の努力(直感的で、簡単な実装、比較的パフォーマンスの高い)は次のとおりです。基本的にはXmlWriterと書いて、基本ストリームを監視しています。私のファイルサイズの制限を超えると、私は現在のXMLフラグメントを完成させ、ファイルを保存し、ストリームを閉じます。

次に、2回目のパスで、完全なDOMをメモリにロードし、ノードを繰り返し削除し、ドキュメントを許容サイズになるまで保存します。例えば

// arbitrary limit of 10MB 
long FileSizeLimit = 10*1024*1024; 

// open file stream to monitor file size 
using (FileStream file = new FileStream("some.data.xml", FileMode.Create)) 
using (XmlWriter writer = XmlWriter.Create(file)) 
{ 
    writer.WriteStartElement("root"); 

    // while not greater than FileSizeLimit 
    for (; file.Length < FileSizeLimit;) 
    { 
     // write contents 
     writer.WriteElementString(
      "data", 
      string.Format("{0}/{0}/{0}/{0}/{0}", Guid.NewGuid())); 
    } 

    // complete fragment; this is the trickiest part, 
    // since a complex document may have an arbitrarily 
    // long tail, and cannot be known during file size 
    // sampling above 
    writer.WriteEndElement(); 
    writer.Flush(); 
} 

// iteratively reduce document size 
// NOTE: XDocument will load full DOM into memory 
XDocument document = XDocument.Load("some.data.xml"); 
XElement root = document.Element("root"); 
for (; new FileInfo("some.data.xml").Length > FileSizeLimit;) 
{ 
    root.LastNode.Remove(); 
    document.Save("some.data.xml"); 
} 

これを改善する方法があります。メモリが制約条件である場合の1つの可能性は、最初のパスで実際に書き込まれたノードの数を取ってから、1つ少ない要素でファイルを書き換え、完全なドキュメントが望ましいサイズになるまで続行することです。

この最後の推奨事項は、特に既に別のファイルへの書き込みを再開するために書き込まれた要素を追跡する必要がある場合は、移動するルートかもしれません。

希望すると便利です。


EDIT

直感的で、実現しやすい、私は上記の最適化を検討する価値はそれを感じましたが。これは私が得たものです。

祖先ノードを書き込むのに役立つ拡張メソッド(すなわち、コンテナノード、およびマークアップの他のすべての種類)、

// performs a shallow copy of a given node. courtesy of Mark Fussell 
// http://blogs.msdn.com/b/mfussell/archive/2005/02/12/371546.aspx 
public static void WriteShallowNode(this XmlWriter writer, XmlReader reader) 
{ 

    switch (reader.NodeType) 
    { 
     case XmlNodeType.Element: 
      writer.WriteStartElement(
       reader.Prefix, 
       reader.LocalName, 
       reader.NamespaceURI); 
      writer.WriteAttributes(reader, true); 
      if (reader.IsEmptyElement) 
      { 
       writer.WriteEndElement(); 
      } 
      break; 
     case XmlNodeType.Text: writer.WriteString(reader.Value); break; 
     case XmlNodeType.Whitespace: 
     case XmlNodeType.SignificantWhitespace: 
      writer.WriteWhitespace(reader.Value); 
      break; 
     case XmlNodeType.CDATA: writer.WriteCData(reader.Value); break; 
     case XmlNodeType.EntityReference: 
      writer.WriteEntityRef(reader.Name); 
      break; 
     case XmlNodeType.XmlDeclaration: 
     case XmlNodeType.ProcessingInstruction: 
      writer.WriteProcessingInstruction(reader.Name, reader.Value); 
      break; 
     case XmlNodeType.DocumentType: 
      writer.WriteDocType(
       reader.Name, 
       reader.GetAttribute("PUBLIC"), 
       reader.GetAttribute("SYSTEM"), 
       reader.Value); 
      break; 
     case XmlNodeType.Comment: writer.WriteComment(reader.Value); break; 
     case XmlNodeType.EndElement: writer.WriteFullEndElement(); break; 
    } 
} 

とのいずれかを拡張するため、トリミング(ない拡張メソッドを実行する方法パラメータの型は少し曖昧です)。

// trims xml file to specified file size. does so by 
// counting number of "victim candidates" and then iteratively 
// trimming these candidates one at a time until resultant 
// file size is just less than desired limit. does not 
// consider nested victim candidates. 
public static void TrimXmlFile(string filename, long size, string trimNodeName) 
{ 
    long fileSize = new FileInfo(filename).Length; 
    long workNodeCount = 0; 

    // count number of victim elements in xml 
    if (fileSize > size) 
    { 
     XmlReader countReader = XmlReader.Create(filename); 
     for (; countReader.Read();) 
     { 
      if (countReader.NodeType == XmlNodeType.Element && 
       countReader.Name == trimNodeName) 
      { 
       workNodeCount++; 
       countReader.Skip(); 
      } 
     } 
     countReader.Close(); 
    } 

    // if greater than desired file size, and there is at least 
    // one victim candidate 
    string workFilename = filename+".work"; 
    for (; 
     fileSize > size && workNodeCount > 0; 
     fileSize = new FileInfo(filename).Length) 
    { 
     workNodeCount--; 
     using (FileStream readFile = new FileStream(filename, FileMode.Open)) 
     using (FileStream writeFile = new FileStream(
      workFilename, 
      FileMode.Create)) 
     { 
      XmlReader reader = XmlReader.Create(readFile); 
      XmlWriter writer = XmlWriter.Create(writeFile); 

      long j = 0; 
      bool hasAlreadyRead = false; 
      for (; (hasAlreadyRead) || reader.Read();) 
      { 

       // if node is a victim node 
       if (reader.NodeType == XmlNodeType.Element && 
        reader.Name == trimNodeName) 
       { 
        // if we have not surpassed this iteration's 
        // allowance, preserve node 
        if (j < workNodeCount) 
        { 
         writer.WriteNode(reader, true); 
        } 
        j++; 

        // if we have exceeded this iteration's 
        // allowance, trim node (and whitespace) 
        if (j >= workNodeCount) 
        { 
         reader.ReadToNextSibling(trimNodeName); 
        } 
        hasAlreadyRead = true; 
       } 
       else 
       { 
        // some other xml content we should preserve 
        writer.WriteShallowNode(reader); 
        hasAlreadyRead = false; 
       } 
      } 
      writer.Flush(); 
     } 
     File.Copy(workFilename, filename, true); 
    } 
    File.Delete(workFilename); 
} 

あなたのXMLが空白の書式設定、最後に残っている被害者のノード間の空白が含まれており、閉じたコンテナ要素タグが失われた場合。これはskip節を変更することで緩和することができます(j++文のスキップをスキップして移動する)が、追加の空白で終了します。上記のソリューションは、ソースファイルの最小限のファイルサイズの複製を生成します。