【问题标题】:A fast way to compare two XML files and create another one with differences一种比较两个 XML 文件并创建另一个具有差异的文件的快速方法
【发布时间】: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

标签: c# xml linq


【解决方案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 条记录/秒

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-10
    • 2011-05-31
    • 2012-10-28
    • 1970-01-01
    • 2018-01-09
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    相关资源
    最近更新 更多