【问题标题】:Deleting nodes from VERY LARGE XML file in Powershell在 Powershell 中从非常大的 XML 文件中删除节点
【发布时间】:2020-11-05 04:52:15
【问题描述】:

我目前正在阅读一个巨大的 (3GB) XML 文件。这个 XML 文件由记录组成,我希望根据属性值删除一些(大约 5% 的记录),然后将剩余的 95% 写入新文件。

我当前的代码:

$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$xml = [xml]''
$xml.Load("C:\Users\Jack\Documents\development\record removal\records.xml")

$nodes = $xml.SelectNodes("//record[@category='APPLE'] | //record[@category='BANANA'] | 
//record[@category='ORANGE']")

foreach ($node in $nodes){
    $node.ParentNode.RemoveChild($node)
}

$xml.save("C:\Users\Jack\Documents\development\record removal\records-NEW.xml")
$StopWatch.Stop()
$StopWatch.Elapsed.TotalSeconds

完成任务所花费的时间太多,我需要它更有效率。当我一次只处理一个类别时,速度快了很多,我是否遗漏了一些明显的东西?

我应该使用 XMLReader 之类的其他东西吗?

XML 示例:

<?xml version="1.0" encoding="UTF-8"?>
<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<record category="APPLE" sub-category="FRUIT">
</record>
<record category="BANANA" sub-category="FRUIT">
</record>
<record category="ORANGE" sub-category="FRUIT">
</record>
<record category="KIWI" sub-category="FRUIT">
</record>
<record category="GRAPE" sub-category="FRUIT">
</record>
</records>

更新的解决方案

使用 jdweng 的代码,我已将其导入到我的 powershell 代码中。完整代码如下:

$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

$id = get-random

$Assem = (
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.XML.dll",
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Xml.Linq.dll"
)

$Source = @”
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace Jack.Tools 
{
    public class Class$id
    {
        const string INPUT_FILENAME = @"C:\temp\old.xml";
        const string OUTPUT_FILENAME = @"C:\temp\new.xml";
        
        public static void method()
        {
            XmlReader reader = XmlReader.Create(INPUT_FILENAME);
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME, settings);
            writer.WriteStartDocument();
            writer.WriteStartElement("records");
            writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");

            while (!reader.EOF)
            {
                if(reader.Name != "record")
                {
                    reader.ReadToFollowing("record");
                }
                if (!reader.EOF)
                {
                    XElement record = (XElement)XElement.ReadFrom(reader);
                    if ((string)record.Attribute("category") != "APPLE")
                    {
                        record.WriteTo(writer);
                    }
                }
            }
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Flush();
            writer.Close();
        }
    }
}
“@

Add-Type -AssemblyName Microsoft.CSharp
Add-Type -AssemblyName System
Add-Type -AssemblyName System.Core
Add-Type -AssemblyName System.Data
Add-Type -AssemblyName System.Data.DataSetExtensions
Add-Type -AssemblyName System.Xml
Add-Type -AssemblyName System.Linq
Add-Type -ReferencedAssemblies $Assem -TypeDefinition $Source -Language CSharp 

iex "[Jack.Tools.Class$id]::method()"

$StopWatch.Stop()
$StopWatch.Elapsed.TotalSeconds

【问题讨论】:

  • 在这种情况下,是的。 XmlReader 应该一次读取一条记录并将它们写入 XmlWriter,或者如果它们符合条件则丢弃它们。现在,您正在内存中加载 3GB 的文本,将其解析为超过 3GB 的对象,扫描这 3GB 的记录,最后将剩余的 3GB 的节点写入磁盘。使用 XmlReader/XmlWriter 组合,您一次只能在内存中保留一条记录,以及用于读取器、写入器流的文件缓冲区
  • @PanagiotisKanavos 这可以在 Powershell 中完成吗?我还在学习 Powershell,还不知道如何用 C# 编写代码。
  • 您已经在编写 C# - 您编写的代码直接创建和使用 .NET 类。您将失去任何 Intellisense 和编译优势,这意味着编写此代码相当棘手
  • @PanagiotisKanavos 感谢队友,我设法使用 Add-Type 导入了 c# 方法。我会用我的代码在这里发布答案,感谢你和 jdweng 它完美地工作!!!

标签: c# windows powershell .net-core


【解决方案1】:

尝试以下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication11
{
    class Program
    {
        const string INPUT_FILENAME = @"c:\temp\test.xml";
        const string OUTPUT_FILENAME = @"c:\temp\test1.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(INPUT_FILENAME);
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME, settings);
            writer.WriteStartDocument();
            writer.WriteStartElement("records");
            writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");

            while (!reader.EOF)
            {
                if(reader.Name != "record")
                {
                    reader.ReadToFollowing("record");
                }
                if (!reader.EOF)
                {
                    XElement record = (XElement)XElement.ReadFrom(reader);
                    if ((string)record.Attribute("category") != "BANANA")
                    {
                        record.WriteTo(writer);
                    }
                }
            }
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Flush();
            writer.Close();
 


        }
    }

}

【讨论】:

    猜你喜欢
    • 2020-08-05
    • 2018-06-14
    • 1970-01-01
    • 2013-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2018-03-17
    相关资源
    最近更新 更多