我想从一个对象(包含嵌套集合)生成大量数据的XML文件。 但是XML有一个限制,它不能超过50MB。C#中的文件大小限制或限制
有没有什么好的方法可以做到这一点?
更新:速度并不重要,主要的是分裂成50MB为每个文件
我想从一个对象(包含嵌套集合)生成大量数据的XML文件。 但是XML有一个限制,它不能超过50MB。C#中的文件大小限制或限制
有没有什么好的方法可以做到这一点?
更新:速度并不重要,主要的是分裂成50MB为每个文件
你有没有考虑书面方式就像一个字符串的XML文件,而不是使用.NET中的XML支持。
我正在向XML写入大约10GB的数据,因为这是工具可以使用它的唯一方式。
我有这样一个问题,但我的XML是如此简单,我只是用一个TextWriter和嵌套的for循环编写XML。
工作了一个魅力,加上比XML对象快得多。
一切都比xml对象更快;) – NotMe 2009-08-19 02:45:55
你可以写很大的XML文件的XmlWriter或的XDocument没有任何问题。
这里是一个示例。本示例在不到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();
}
我已经使用这种方法写入/读取了多个千兆字节的xml文件,它工作正常。对于额外的功劳,您可以通过GzipStream将其连接起来以压缩文件... – 2009-08-19 03:19:09
在我的工作中遇到类似的要求。我的最大努力(直观,易于实施,相对高性能)如下。我基本上写了一个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++
语句后跳过)来缓解,但最终会产生额外的空白。上面介绍的解决方案生成了源文件的最小文件大小副本。
你打算怎么处理剩下的事情?你的输出文件会是什么样子? – 2012-09-19 23:58:45
将它分成50MB文件有什么问题? – 2012-09-20 17:44:02