在 C# 中浏览 XML 文件的最快方法是啥?

Posted

技术标签:

【中文标题】在 C# 中浏览 XML 文件的最快方法是啥?【英文标题】:What is the fastest way to go through a XML file in C#?在 C# 中浏览 XML 文件的最快方法是什么? 【发布时间】:2021-02-17 02:43:37 【问题描述】:

我有一个程序会检查数千个文件,并且必须检查它们是否具有正确的 xml 格式。 问题是它需要很长时间才能完成,我认为这是因为我使用的 xml 阅读器的类型。

在下面的方法中,我尝试了 3 个不同的版本,第一个是最快的,但只有 5%。 (该方法不需要检查文件是否为xml)

private bool HasCorrectXmlFormat(string filePath)

    try
    
        //-Version 1----------------------------------------------------------------------------------------
        XmlReader reader = XmlReader.Create(filePath, new XmlReaderSettings()  IgnoreComments = true, IgnoreWhitespace = true );

        string[] elementNames = new string[]  "DocumentElement", "Protocol", "DateTime", "Item", "Value" ;

        int i = 0;

        while (reader.Read())
        
            if (reader.NodeType == XmlNodeType.Element)
            
                if (reader.Name != elementNames.ElementAt(i))
                
                    return false;
                

                if (i >= 4)
                
                    return true;
                

                i++;
            

        

        return false;
        //--------------------------------------------------------------------------------------------------


        //-  Version 2  ------------------------------------------------------------------------------------
        IEnumerable<XElement> xmlElements = XDocument.Load(filePath).Descendants();

        string[] elementNames = new string[]  "DocumentElement", "Protocol", "DateTime", "Item", "Value" ;

        for (int i = 0; i < 5; i++)
        
            if (xmlElements.ElementAt(i).Name != elementNames.ElementAt(i))
            
                return false;
            
        

        return true;
        //--------------------------------------------------------------------------------------------------


        //-  Version 3  ------------------------------------------------------------------------------------
        XDocument doc = XDocument.Load(filePath);

        if (doc.Root.Name != "DocumentElement")
        
            return false;
        

        if (doc.Root.Elements().First().Name != "Protocol")
        
            return false;
        

        if (doc.Root.Elements().First().Elements().ElementAt(0).Name != "DateTime")
        
            return false;
        

        if (doc.Root.Elements().First().Elements().ElementAt(1).Name != "Item")
        
            return false;
        

        if (doc.Root.Elements().First().Elements().ElementAt(2).Name != "Value")
        
            return false;
        

        return true;
        //--------------------------------------------------------------------------------------------------
    
    catch (Exception)
    
        return false;
    

我需要的是一种更快的方法来做到这一点。有没有更快的方法来浏览 xml 文件?我只需要检查前 5 个元素的名称是否正确。

更新

Xml 文件的大小只有 2-5 KB,很少超过这个大小。文件位于本地服务器上。我在一台有固态硬盘的笔记本电脑上。

以下是一些测试结果:

我还应该补充一点,文件是之前过滤的,所以只有 xml 文件被赋予该方法。我使用以下方法获取文件:

public List<FileInfo> GetCompatibleFiles()
    
        return new DirectoryInfo(folderPath)
                    .EnumerateFiles("*", searchOption)
                    .AsParallel()
                    .Where(file => file.Extension == ".xml" ? HasCorrectXmlFormat(file.FullName) : false)
                    .ToList();
    

我的代码中没有这个方法(它把两个方法放在一起),这只是为了说明如何调用 HasCorrectXmlFormat 方法。这个方法不用改,我知道是可以改进的。

UDPATE 2

这里是更新 1 末尾提到的两个完整方法:

private void WriteAllFilesInList()
    
        allFiles = new DirectoryInfo(folderPath)
                    .EnumerateFiles("*", searchOption)
                    .AsParallel()
                    .ToList();
    

private void WriteCompatibleFilesInList()
    
        compatibleFiles = allFiles
                            .Where(file => file.Extension == ".xml" ? HasCorrectXmlFormat(file.FullName) : false)
                            .ToList();
    

这两个方法在整个程序中只调用一次(如果allFilescompatibleFiles 列表为空)。

更新 3

WriteAllFilesInList 方法似乎是这里的真正问题,如下所示:

最终更新

看起来,我的方法不需要任何改进,因为瓶颈是别的东西。

【问题讨论】:

编写一个模式文件来进行您需要的验证并使用预制的模式验证器? 你不会轻易得到比 XmlReader 更快的东西。您是否检查过处理时间是由 I/O 还是由 CPU 决定的?在后一种情况下,您可能会尝试并行检查多个文件。 @jdweng OP 在哪里使用 XML 序列化? 你确认外循环是正确的吗?也许你做的工作比你想象的要多?例如。多次读取同一个文件 您能分享一下版本 1、版本 2 和版本 3 的性能测量结果吗?此外,您正在验证的 XML 文件的平均大小是多少,您使用的硬件存储类型是什么(硬盘驱动器?SSD?),它位于哪里(本地机器、本地网络等)? 【参考方案1】:

这里是示例,它读取示例 XML 并显示 Linq/XMlReaderXmlDocument 之间的比较

Linq 最快。

示例代码

using System;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace ReadXMLInCsharp

  class Program
  
    static void Main(string[] args)
    
     
        //returns url of main directory which contains "/bin/Debug"
        var url=System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
        
        //correction in path to point it in Root directory
        var mainpath = url.Replace("\\bin\\Debug", "") + "\\books.xml";

        var stopwatch = new Stopwatch();
        stopwatch.Start();

        //create XMLDocument object
        XmlDocument xmlDoc = new XmlDocument();
        //load xml file
        xmlDoc.Load(mainpath);
        //save all nodes in XMLnodelist
        XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes("/catalog/book");

        //loop through each node and save it value in NodeStr
        var NodeStr = "";

        foreach (XmlNode node in nodeList)
        
            NodeStr = NodeStr + "\nAuthor " + node.SelectSingleNode("author").InnerText;
            NodeStr = NodeStr + "\nTitle " + node.SelectSingleNode("title").InnerText;
            NodeStr = NodeStr + "\nGenre " + node.SelectSingleNode("genre").InnerText;
            NodeStr = NodeStr + "\nPrice " + node.SelectSingleNode("price").InnerText;
            NodeStr = NodeStr + "\nDescription -" + node.SelectSingleNode("description").InnerText;


        
        //print all Authors details
        Console.WriteLine(NodeStr);
        stopwatch.Stop();
        Console.WriteLine();
        Console.WriteLine("Time elapsed using XmlDocument (ms)= " + stopwatch.ElapsedMilliseconds);
        Console.WriteLine();

        stopwatch.Reset();

        stopwatch.Start();
        NodeStr = "";
        //linq method
        //get all elements inside book
        foreach (XElement level1Element in XElement.Load(mainpath).Elements("book"))
        
            //print each element value
            //you can also print XML attribute value, instead of .Element use .Attribute
            NodeStr = NodeStr + "\nAuthor " + level1Element.Element("author").Value;
            NodeStr = NodeStr + "\nTitle " + level1Element.Element("title").Value;
            NodeStr = NodeStr + "\nGenre " + level1Element.Element("genre").Value;
            NodeStr = NodeStr + "\nPrice " + level1Element.Element("price").Value;
            NodeStr = NodeStr + "\nDescription -" + level1Element.Element("description").Value;
        

        //print all Authors details
        Console.WriteLine(NodeStr);
        stopwatch.Stop();
        Console.WriteLine();
        Console.WriteLine("Time elapsed using linq(ms)= " + stopwatch.ElapsedMilliseconds);
        Console.WriteLine();

        stopwatch.Reset();
        stopwatch.Start();
        //method 3
        //XMLReader
        XmlReader xReader = XmlReader.Create(mainpath);

        xReader.ReadToFollowing("book");
        NodeStr = "";
        while (xReader.Read())
        
            switch (xReader.NodeType)
            
                case XmlNodeType.Element:
                    NodeStr = NodeStr + "\nElement name:" + xReader.Name;
                    break;
                case XmlNodeType.Text:
                    NodeStr = NodeStr + "\nElement value:" + xReader.Value;
                    break;
                case XmlNodeType.None:
                    //do nothing
                    break;

            
        

        //print all Authors details
        Console.WriteLine(NodeStr);
        stopwatch.Stop();
        Console.WriteLine();
        Console.WriteLine("Time elapsed using XMLReader (ms)= " + stopwatch.ElapsedMilliseconds);
        Console.WriteLine();
        stopwatch.Reset();


        Console.ReadKey();
    
  

输出:

-- First Run
Time elapsed using XmlDocument (ms)= 15

Time elapsed using linq(ms)= 7

Time elapsed using XMLReader (ms)= 12

-- Second Run
Time elapsed using XmlDocument (ms)= 18

Time elapsed using linq(ms)= 3

Time elapsed using XMLReader (ms)= 15

我删除了一些输出以仅显示比较数据。

来源:Open and Read XML in C# (Examples using Linq, XMLReader, XMLDocument)

编辑:如果我从所有方法中评论“Console.WriteLine(NodeStr)”并仅打印时间比较。 这就是我得到的

Time elapsed using XmlDocument (ms)= 11


Time elapsed using linq(ms)= 0


Time elapsed using XMLReader (ms)= 0

基本上,这取决于您处理数据的方式以及读取 XML 的方式。 Linq/XML 阅读器曾经在速度方面看起来更有前途。

【讨论】:

测试数据小得离谱。并且没有 Console.WriteLine 的结果表明测量方法不合适。 @KlausGütter 是的,测试数据很小,但这仅用于示例目的,据我所知,我们可以采用大 XML 并做同样的事情来检查输出。关于没有 Console.WriteLine,这只是示例测试,我在这里执行并提到了所有内容。【参考方案2】:

我会使用比您的代码快一点的 Xml Linq 编写这样的代码。你的代码在 xml 文件中循环了多次,而我的代码只在文件中循环了一次。

    try
    

        XDocument doc = XDocument.Load(filePath);
        XElement root = doc.Root;
        if (doc.Root.Name != "DocumentElement")
        
            return false;
        
        else
        
            XElement protocol = root.Elements().First();
            if (protocol.Name != "Protocol")
            
                return false;
            
            else
            
                XElement dateTime = protocol.Elements().First();
                if (dateTime.Name != "DateTime")
                
                    return false;
                
                XElement item = protocol.Elements().Skip(1).First();
                if (item.Name != "Item")
                
                    return false;
                
                XElement value = protocol.Elements().Skip(2).First();
                if (doc.Root.Elements().First().Elements().ElementAt(2).Name != "Value")
                
                    return false;
                
 
            

        
    
    catch (Exception)
    
        return false;
    
    return true;

【讨论】:

这可能会比版本 3 快一点,但无法击败 XmlReader;另见***.com/questions/2735434/… 您的 linq 正在浏览文件 4 次。我的只有一次。因此,您使用我的代码的 15 毫秒将与 XmlReader 大致相同。 @jdweng 这几乎和我的 V2 一样快(这是我的三个版本中最慢的)。感谢您的努力,谢谢。 Xml 文件有多大? XmlReader 在读取整个文件时动态读取 Xml Linq。 @jdweng 只有几个 KB(最大 5KB)。但我发现瓶颈实际上是获取文件的函数。

以上是关于在 C# 中浏览 XML 文件的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中填充组合框的最快方法是啥? [关闭]

在 C# 中解析 XML 文件的最快方法?

在 C# 中计算数组频率分布的最快方法是啥?

在 SQL Server(C# 客户端)中批量插入大量数据的最快方法是啥

从 C# 中的字符串中删除换行符的最快方法是啥?

在 C# 中过滤 XmlDocument xml 的最快方法