2009-08-19 8 views
4

많은 양의 데이터가 포함 된 하나의 개체 (중첩 컬렉션 포함)에서 XML 파일을 생성하려고합니다. 그러나 XML에는 제한이있어서 이 50MB을 초과 할 수 없습니다.파일 크기 제한 또는 C#의 제한

이렇게하는 좋은 방법이 있습니까?

업데이트 : 속도는

+0

나머지는 무엇을 할 것입니까? 출력 파일은 어떻게 생겼을까요? –

+0

50MB 파일로 분할하는 것과 관련하여 문제가있는 것은 무엇입니까? –

답변

1

당신이 문자열과 같은 XML 파일을 writting 대신 .NET의 XML 지원을 사용하여 고려 가지고 중요한 것은 각 파일에 대해 50메가바이트으로 분할, 중요하지 않습니다.

저는 도구에서 XML 데이터를 소비 할 수있는 유일한 방법이기 때문에 ~ 10GB의 데이터를 XML에 기록했습니다.

이런 문제가 있었지만 XML이 너무 단순해서 방금 TextWriter를 사용하고 XML을 작성하는 루프를 중첩했습니다.

매력을 발휘하여 XML 객체보다 훨씬 빠릅니다.

+0

모든 것이 xml 개체보다 빠릅니다.) – NotMe

2

XmlWriter 또는 XDocument으로 큰 xml 파일을 작성할 수 있습니다.

여기 예제. 이 예제는 5 초 이내에 63MB xml 파일을 생성합니다. 이 예제에서는 클래스 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

내 작업에서 비슷한 요구 사항을 따르십시오. 내 최선의 노력 (직관적 인, 구현의 용이성, 상대적으로 performant)은 다음과 같습니다. 기본적으로 XmlWriter으로 작성하여 기본 스트림을 모니터링합니다. 내 파일 크기 제한을 초과하면 현재 XML 조각을 완료하고 파일을 저장하고 스트림을 닫습니다.

두 번째 단계에서 전체 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"); 
} 

이를 개선 할 수있는 방법이 있습니다; 하나의 가능성은 메모리가 제약 조건이라면 첫 번째 패스에서 실제로 쓰여진 노드의 수를 가져와 하나의 요소가 적은 파일을 다시 쓰고 전체 문서가 원하는 크기가 될 때까지 계속 반복 비트를 다시 쓰는 것입니다.

이 마지막 권장 사항은 특히 다른 파일에 쓰기를 재개하기 위해 작성된 요소를 추적해야하는 경우에 사용할 수있는 경로 일 수 있습니다.

희망이 도움이됩니다.


편집

직관적이며 쉽게 구현, 내가 위에서 언급 한 최적화를 조사하고 가치를 생각하지만. 이것이 내가 가진 것입니다.

쓰기 조상 노드 (즉, 컨테이너 노드 및 마크 업의 모든 다른 종류의) 도움이 연장 방법,

// 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++ 문을 건너 뛰고) 공백을 추가로 줄일 수 있습니다. 위에 제시된 솔루션은 소스 파일의 최소 파일 크기 복제본을 생성합니다.