在 Powershell 中从非常大的 XML 文件中删除节点

Posted

技术标签:

【中文标题】在 Powershell 中从非常大的 XML 文件中删除节点【英文标题】:Deleting nodes from VERY LARGE XML file in Powershell 【发布时间】: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 它完美地工作!!! 【参考方案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();
 


        
    


【讨论】:

以上是关于在 Powershell 中从非常大的 XML 文件中删除节点的主要内容,如果未能解决你的问题,请参考以下文章

在 java 中解析非常大的 XML 文档(以及更多)

读取大文件的最佳方式(例如非常大的文本文档)

在 Bamboo 中从 powershell 脚本创建元数据

在 Eclipse 中从 XML 生成 Java 代码

如何在 C# 中解析非常大的 XML 文件? [复制]

在 php 中解析非常大的 XML 文件