在 C# 中遍历 XML 文件的最快方法是什么?
What is the fastest way to go through a XML file in C#?
我有一个程序可以检查数千个文件,并且必须检查它们是否具有正确的 xml 格式。
问题是它需要很长时间才能完成,我认为这是因为我使用的 xml reader 类型。
在下面的方法中有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,很少超过这个大小。文件位于本地服务器上。我在一台有 ssd 的笔记本电脑上。
以下是一些测试结果:
我还应该补充一点,文件之前已经过过滤,所以只有 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();
}
这两种方法在整个程序中只调用一次(如果 allFiles
或 compatibleFiles
列表为空)。
更新 3
看来 WriteAllFilesInList
方法才是真正的问题所在,如下所示:
最终更新
看来,我的方法不需要任何改进,因为瓶颈是别的。
我会使用 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;
}
这是示例,它读取示例 XML 并显示 Linq/XMlReader
和 XmlDocument
之间的比较
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 reader 在速度方面曾经看起来更有前途。
我有一个程序可以检查数千个文件,并且必须检查它们是否具有正确的 xml 格式。 问题是它需要很长时间才能完成,我认为这是因为我使用的 xml reader 类型。
在下面的方法中有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,很少超过这个大小。文件位于本地服务器上。我在一台有 ssd 的笔记本电脑上。
以下是一些测试结果:
我还应该补充一点,文件之前已经过过滤,所以只有 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();
}
这两种方法在整个程序中只调用一次(如果 allFiles
或 compatibleFiles
列表为空)。
更新 3
看来 WriteAllFilesInList
方法才是真正的问题所在,如下所示:
最终更新
看来,我的方法不需要任何改进,因为瓶颈是别的。
我会使用 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;
}
这是示例,它读取示例 XML 并显示 Linq/XMlReader
和 XmlDocument
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 reader 在速度方面曾经看起来更有前途。