一种比较两个 XML 文件并创建另一个具有差异的文件的快速方法

Posted

技术标签:

【中文标题】一种比较两个 XML 文件并创建另一个具有差异的文件的快速方法【英文标题】:A fast way to compare two XML files and create another one with differences 【发布时间】:2021-08-04 12:18:40 【问题描述】:

我的朋友只想将产品差异上传到他的网上商店。所以我的想法是比较 XML 文件并仅提取更改。因此我创建了这个:

部分 XML 文件(请注意,此 XML 有更多元素,但我已将它们排除在外):

<?xml version="1.0" encoding="UTF-8"?>
<artikli>
    <artikal>
        <id>1039282</id>
        <sifra>42640</sifra>
        <naziv><![CDATA[Bluetooth zvucnik za tablet IYIGLE X7 crni]]></naziv>
    </artikal>
    <artikal>
        <id>1048331</id>
        <sifra>48888</sifra>
        <naziv><![CDATA[Bluetooth zvucnik REMAX RB-M15 crni]]></naziv>
    </artikal>
</artikli>

C# 脚本

    static IEnumerable<XElement> StreamRootChildDoc(string uri)
    
      using (XmlReader reader = XmlReader.Create(uri))
      
        reader.MoveToContent();

        while (!reader.EOF)
        
          if (reader.NodeType == XmlNodeType.Element && reader.Name == "artikal")
          
            XElement el = XElement.ReadFrom(reader) as XElement;
            if (el != null)
              yield return el;
          
          else
          
            reader.Read();
          
        
      
    

    void ProcessFiles()
    

      try
      

        IEnumerable<XElement> posle = from el in StreamRootChildDoc(@"lisic2.xml")
                                      select el;

        IEnumerable<XElement> pre = from el in StreamRootChildDoc(@"lisic1.xml")
                                    select el;

        XmlDocument doc = new XmlDocument();

        //(1) the xml declaration is recommended, but not mandatory
        XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
        XmlElement root = doc.DocumentElement;
        doc.InsertBefore(xmlDeclaration, root);

        //(2) string.Empty makes cleaner code
        XmlElement element1 = doc.CreateElement(string.Empty, "artikli", string.Empty);
        doc.AppendChild(element1);

        int count_files = 0;

        foreach (XElement node_posle in posle)
        
          count_files++;

          var node_pre = pre.First(child => child.Element("id").Value == node_posle.Element("id").Value);
          if (node_pre != null)
          
            string pre_Value = node_pre.Value.Replace("\t", ""); ;
            string posle_Value = node_posle.Value.Replace("\t", ""); ;
            if (pre_Value != posle_Value)
            
              var reader = node_posle.CreateReader();
              reader.MoveToContent();

              XmlElement element2 = doc.CreateElement(string.Empty, "artikal", reader.ReadInnerXml());
              element1.AppendChild(element2);
            
          
        
        doc.Save("document.xml");
      
      finally
      

      
    

这可行,但在 10000 条通过记录之后,速度为每秒 18 条记录,在 14000 - 12 条记录/秒之后。有没有其他方法可以加快速度?

更新

现在,我将尝试更快地移动到已检查 XML 的相应 ID。

【问题讨论】:

"可以改进吗?" -- 你打开一个分析器,看看为什么它很慢。我们无法为您猜测 我建议使用像 BenchMarkDotNet 这样的分析器来分析你的代码。使用分析中的信息来确定最大改进的领域。但是,根据个人经验,通过查看您的代码,对性能的追求将以可读性为代价,因为您的许多 LINQ 操作都需要扩展。 为了像这样在原地分析预先存在的代码,我会选择内置的 VS 分析器(在 CPU 模式下)或 PerfMon。 BenchmarkDotNet 不会告诉您为什么您的代码很慢,但可以很好地比较两个备选方案 为什么不直接使用超越比较? XmlReader 又旧又慢。您可以在一个指令中对 xml linq 执行相同操作:XDocument = doc = XDocument.Load(uri); List artikals = doc.Descendants("artikal").ToList(); XmlReader 不是“又老又慢”——它是一个低级的流式 XML 解析器,而像 XDocument 这样的高级框架就是 built on top of! 【参考方案1】:

一种方法是使用 XmlDocument,因为 XML 很小(22000 个产品),所以可以使用它。

   void ProcessXMLDocument()
    
      SetControlEnabled(btStart, false);
      Stopwatch sw = new Stopwatch();
      sw.Start();
      try
      
        XmlDocument sada = new XmlDocument();
        sada.Load(tbPathSada.Text);

        XmlDocument pre = new XmlDocument();
        pre.Load(tbPathOdPre.Text);

        XmlDocument doc = new XmlDocument();

        //(1) the xml declaration is recommended, but not mandatory
        XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
        XmlElement root = doc.DocumentElement;
        doc.InsertBefore(xmlDeclaration, root);

        //(2) string.Empty makes cleaner code
        XmlElement element1 = doc.CreateElement(string.Empty, "artikli", string.Empty);
        doc.AppendChild(element1);

        root = sada.DocumentElement;
        XmlNodeList nodes = root.SelectNodes("artikal"); 
        int count_files = 0;
        foreach (XmlNode nodeSada in nodes)
        
          count_files++;
          try
          
            SetControlText(lbBlokova, count_files.ToString());
            TimeSpan elapsed = sw.Elapsed;
            var files_per_sec = Math.Floor((double)count_files / (double)elapsed.TotalSeconds);
            SetControlText(lbPerSecond, files_per_sec.ToString());
            SetControlText(lbTime, elapsed.ToString(@"hh\:mm\:ss"));
          
          catch (Exception ex2)
          

          

          var idSada = nodeSada.SelectSingleNode("id").InnerText.Trim();
          var nodePre = pre.DocumentElement.SelectSingleNode("artikal[id='" + idSada + "']");
          if (nodePre != null)
          
            string pre_Value = nodePre.InnerXml.Replace("\t", ""); ;
            string posle_Value = nodeSada.InnerXml.Replace("\t", ""); ;
            if (pre_Value != posle_Value)
            
              XmlNode importNode = doc.ImportNode(nodeSada, true);
              element1.AppendChild(importNode);
            
          
          else
          
            XmlNode importNode = doc.ImportNode(nodeSada, true);
            element1.AppendChild(importNode);
          
        
        doc.Save("razlika.xml");
      
      finally
      
        sw.Stop();
        SetControlEnabled(btStart, true);
      
    

通过这种方式,我设法提高了@10000 条记录 => 140 条记录/秒和@14000 => 104 条记录/秒

【讨论】:

以上是关于一种比较两个 XML 文件并创建另一个具有差异的文件的快速方法的主要内容,如果未能解决你的问题,请参考以下文章

创建具有特定 mime 类型的文件

按列和值比较两个 csv 文件并显示不同值的行号 [关闭]

比较 2 个表中的值并生成具有差异的新表

C#比较存储在XML文件中的两个日期时间

比较两个集合的 mongo 差异

比较两个文本并找出差异