将新节点添加到xml结尾的最快方法?

时间:2009-05-11 17:01:40

标签: c# .net xml

我有一个大的xml文件(大约10 MB),其结构如下:

<Errors>
   <Error>.......</Error>
   <Error>.......</Error>
   <Error>.......</Error>
   <Error>.......</Error>
   <Error>.......</Error>
</Errors>

我需要写一个新节点&lt; Error&gt;在&lt; / Errors&gt;之前的末尾标签。什么是在.net中实现这一目标的最快方式?

9 个答案:

答案 0 :(得分:10)

您需要使用XML包含技术。

您的error.xml(不会更改,只是一个存根。由XML解析器用于读取):

<?xml version="1.0"?>
<!DOCTYPE logfile [
<!ENTITY logrows    
 SYSTEM "errorrows.txt">
]>
<Errors>
&logrows;
</Errors>

您的errorsrows.txt文件(更改,xml解析器无法理解):

<Error>....</Error>
<Error>....</Error>
<Error>....</Error>

然后,在errorsrows.txt中添加一个条目:

using (StreamWriter sw = File.AppendText("logerrors.txt"))
{
    XmlTextWriter xtw = new XmlTextWriter(sw);

    xtw.WriteStartElement("Error");
    // ... write error messge here
    xtw.Close();
}

或者您甚至可以使用.NET 3.5 XElement,并将文本附加到StreamWriter

using (StreamWriter sw = File.AppendText("logerrors.txt"))
{
    XElement element = new XElement("Error");
    // ... write error messge here
    sw.WriteLine(element.ToString());
}

另见Microsoft's article Efficient Techniques for Modifying Large XML Files

答案 1 :(得分:7)

首先,我会取消System.Xml.XmlDocument的资格,因为it is a DOM需要解析并在内存中构建整个树才能追加到它。这意味着您的10 MB文本内存将超过10 MB。这意味着它“内存密集”,“耗时”。

其次,我会取消System.Xml.XmlReader的资格,因为它requires parsing the entire file之前,你可以到达可以追加它的时间点。您必须将XmlReader复制到XmlWriter中,因为您无法修改它。这需要先将内存中的XML复制,然后才能附加到它。

XmlDocument和XmlReader的更快解决方案是字符串操作(它有自己的内存问题):

string xml = @"<Errors><error />...<error /></Errors>";
int idx = xml.LastIndexOf("</Errors>");

xml = xml.Substring(0, idx) + "<error>new error</error></Errors>";

删除结束标记,添加新错误,然后添加结束标记。

我想你可能会对此感到茫然,并将你的文件截断9个字符并附加到它上面。不必读入文件并让操作系统优化页面加载(只需加载最后一个块或其他东西)。

System.IO.FileStream fs = System.IO.File.Open("log.xml", System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
fs.Seek(-("</Errors>".Length), System.IO.SeekOrigin.End);
fs.Write("<error>new error</error></Errors>");
fs.Close();

如果您的文件为空或仅包含“&lt;错误&gt;&lt; /错误&gt;”,则会遇到问题,通过检查长度可以轻松处理这两个问题。

答案 2 :(得分:3)

最快的方式可能是直接文件访问。

using (StreamWriter file = File.AppendText("my.log"))
{
    file.BaseStream.Seek(-"</Errors>".Length, SeekOrigin.End);
    file.Write("   <Error>New error message.</Error></Errors>");
}

但是你丢失了所有漂亮的XML功能,可能很容易破坏文件。

答案 3 :(得分:1)

我会使用XmlDocument或XDocument来加载你的文件,然后相应地操作它。

然后我会考虑在内存中缓存此XmlDocument的可能性,以便您可以快速访问该文件。

你需要什么速度?您是否已经存在性能瓶颈,或者您是否期待性能瓶颈?

答案 4 :(得分:1)

试试这个:

        var doc = new XmlDocument();
        doc.LoadXml("<Errors><error>This is my first error</error></Errors>");

        XmlNode root = doc.DocumentElement;

        //Create a new node.
        XmlElement elem = doc.CreateElement("error");
        elem.InnerText = "This is my error";

        //Add the node to the document.
        if (root != null) root.AppendChild(elem);

        doc.Save(Console.Out);
        Console.ReadLine();

答案 5 :(得分:0)

最快的方法可能是使用XmlReader在文件中读取,并使用XmlWriter简单地将每个读取节点复制到新流中当您到达遇到关闭的点时</Errors>标记,然后您只需输出额外的<Error>元素,然后再继续“读取和复制”循环。这种方式不可避免地要比将整个文档读入DOM(XmlDocument类)更难,但对于大型XML文件,更多更快。不可否认,使用StreamReader / StreamWriter会更快一些,但在代码中使用会非常糟糕。

答案 6 :(得分:0)

您的XML文件如何在代码中表示?你使用System.XML类吗?在这种情况下,您可以使用XMLDocument.AppendChild。

答案 7 :(得分:0)

以下是如何在C中实现它,.NET应该是类似的。

游戏是简单地跳转到文件的末尾,跳过标签,附加新的错误行,然后写一个新的标签。

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(int argc, char** argv) {
        FILE *f;

        // Open the file
        f = fopen("log.xml", "r+");

        // Small buffer to determine length of \n (1 on Unix, 2 on PC)
        // You could always simply hard code this if you don't plan on 
        // porting to Unix.
        char nlbuf[10];
        sprintf(nlbuf, "\n");

        // How long is our end tag?
        long offset = strlen("</Errors>");

        // Add in an \n char.
        offset += strlen(nlbuf);

        // Seek to the END OF FILE, and then GO BACK the end tag and newline
        // so we use a NEGATIVE offset.
        fseek(f, offset * -1, SEEK_END);

        // Print out your new error line
        fprintf(f, "<Error>New error line</Error>\n");

        // Print out new ending tag.
        fprintf(f, "</Errors>\n");

        // Close and you're done
        fclose(f);
}

答案 8 :(得分:0)

使用基于字符串的技术(比如寻找文件的末尾然后向后移动结束标记的长度)很容易受到文档结构中意外但完全合法的变化的影响。

文档可以以任何数量的空格结束,以选择您将遇到的最可能的问题。它也可以以任意数量的注释或处理指令结束。如果顶级元素未命名为Error会发生什么?

这是一种使用字符串操作完全无法检测到的情况:

<Error xmlns="not_your_namespace">
   ...
</Error>

如果您使用XmlReader来处理XML,虽然它可能没有寻求EOF那么快,但它也可以让您处理所有这些可能的异常情况。