首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >XMLWriter &递归

XMLWriter &递归
EN

Stack Overflow用户
提问于 2020-05-21 14:57:01
回答 1查看 90关注 0票数 0

我已经成功地将目录结构实现为XML脚本,如下所示。

代码语言:javascript
复制
function GenerateLibraryMap ($path) {
    function ProcessChildNode { 
        param ( 
            $parentNode, 
            $childPath
        ) 
        $dirInfo = [System.IO.DirectoryInfo]::New($childPath)
        foreach ($directory in $dirInfo.GetDirectories()) {
            $childNode = $xmlDoc.CreateElement('folder')
            $childNode.SetAttribute('name', $directory.Name) > $null
            $parentNode.AppendChild($childNode) > $null
            ProcessChildNode -parentNode:$childNode -childPath:"$childPath\$($directory.Name)"
        }
        foreach ($file in $dirInfo.GetFiles()) {
            $childNode = $xmlDoc.CreateElement('file')
            $childNode.SetAttribute('name', $file.Name) > $null
            $childNode.SetAttribute('size', $file.Length) > $null
            $childNode.SetAttribute('hash', (Get-FileHash -Path:$file.FullName -Algorithm:MD5).Hash) > $null
            $parentNode.AppendChild($childNode) > $null
        }
    }

    $xmlDoc = [XML]::New()

    $xmlDoc.AppendChild($xmlDoc.CreateProcessingInstruction('xml', 'version="1.0"')) > $null
    $rootNode = $xmlDoc.CreateElement('rootDirectory')
    $rootNode.SetAttribute('path', $path) > $null
    $xmlDoc.AppendChild($rootNode) > $null
    ProcessChildNode -parentNode:$rootNode -childPath:$path

    $xmlDoc.Save("$path\Tree.xml") > $null
    Write-Host "$path\Tree.xml"
}
Measure-Command {
    GenerateLibraryMap 'C:\assets\Revit\2020'
}

这很好用,但在我测试的文件结构上需要2分钟以上,这很可能只是我实际数据的20%。因此,我正在考虑使用XML进行重构,因为我知道这可能要快得多。我找到了这个参考文献来启动它,但是它只包含根节点,没有提到生成层次结构是如何工作的。看起来,您几乎需要跟踪层次结构,以便能够在每个节点上适当地进行.WriteEndElement。但这让我的简单递归崩溃了。我想我只需要简单地在第12行的.WriteEndElement之后使用ProcessChildNode,但我不确定。

我想我有两个问题..。

他说:在我进入这个兔子洞之前,这会导致明显更快的代码吗?尤其是在处理成千上万个子文件夹中的文件时?还有..。

他说:有人能告诉我如何处理递归问题的资源,或者提供一个例子吗?我有一种感觉,如果不这样的话,我的头会撞到墙上。

好吧,真的有三个问题。

他说:一旦我有了这个工作,我计划重构到一个班级,这既是为了提高性能,也是作为一个练习,因为我正在学习OOP。当我从下一个兔子洞开始的时候,我有什么需要注意的问题吗?

编辑:有了Mathias的回应,再加上一些挖掘,我得出了这个结论。

代码语言:javascript
复制
function GenerateLibraryMap {
    param ( 
        [String]$path
    )
    function ProcessChildNode { 
        param ( 
            [String]$childPath
        ) 
        $dirInfo = [System.IO.DirectoryInfo]::New($childPath)

        foreach ($directory in $dirInfo.GetDirectories()) {
            $xmlDoc.WriteStartElement('folder')
            $xmlDoc.WriteAttributeString('name', $directory.Name)
            ProcessChildNode -childPath:"$childPath\$($directory.Name)"
        }
        foreach ($file in $dirInfo.GetFiles()) {
            $xmlDoc.WriteStartElement('file')
            $xmlDoc.WriteAttributeString('name', $file.Name)
            $xmlDoc.WriteAttributeString('size', $file.Length)
            $xmlDoc.WriteAttributeString('hash', (Get-FileHash -Path:$file.FullName -Algorithm:MD5).Hash)

            $xmlDoc.WriteEndElement()
        }

        $xmlDoc.WriteEndElement()
    }

    $mapFilePath = "$(Split-Path $path -parent)\Tree_Stream.xml"
    $xmlSettings = [System.XMl.XmlWriterSettings]::New()
    $fileStream = [System.IO.FileStream]::New($mapFilePath, [System.IO.FileMode]::Append, [System.IO.FileAccess]::Write, [System.IO.FileShare]::Read)
    $streamWriter = [System.IO.StreamWriter]::New($fileStream)
    $xmlSettings.Indent = $true
    $xmlSettings.IndentChars = '  '
    $xmlSettings.ConformanceLevel = 'Auto'
    $xmlDoc = [System.XMl.XmlTextWriter]::Create($fileStream, $xmlSettings)

    $xmlDoc.WriteStartDocument()
    $xmlDoc.WriteStartElement('rootDirectory')

    $xmlDoc.WriteAttributeString('path', $path)

    ProcessChildNode -childPath:$path

    $xmlDoc.WriteEndElement
    $xmlDoc.WriteEndDocument
    $xmlDoc.Finalize
    $xmlDoc.Flush
    $xmlDoc.Close()
    Write-Host $mapFilePath
}
CLS
Measure-Command {
    GenerateLibraryMap 'C:\assets\Revit\2020'
}

我尝试使用[System.IO.FileStream]和直接用文件路径实例化$xmlDoc (仍然不能100%确定我理解其中的区别)。在任何情况下,这三种方法都是在几秒钟之内,就在2分钟左右。因此,在这种情况下,似乎没有什么有意义的区别。如果有人看到了提高成绩的机会,我会全神贯注地听,但现在我将把重构推进到课堂上。

编辑2:嗯,我实现了基于类的方法,像这样.

代码语言:javascript
复制
class GenerateLibraryMap {
    # Properties
    [XML.XMLDocument]$XML = [XML]::New()
    [String]$MapFilePath

    # Constructor
    GenerateLibraryMap ([String]$path) {
        $this.MapFilePath = "$(Split-Path $path -parent)\Tree_Class.xml"

        $this.XML.AppendChild($this.XML.CreateProcessingInstruction('xml', 'version="1.0"')) > $null
        $rootNode = $this.XML.CreateElement('rootDirectory')
        $rootNode.SetAttribute('path', $path) > $null
        $this.XML.AppendChild($rootNode) > $null

        $this.ProcessChildNode($rootNode, $path)

        $this.XML.Save($this.MapFilePath)
    }

    # Method
    [Void] ProcessChildNode([XML.XMLElement]$parentNode, [String]$childPath) {
        $dirInfo = [System.IO.DirectoryInfo]::New($childPath)
        foreach ($directory in $dirInfo.GetDirectories()) {
            $childNode = $this.XML.CreateElement('folder')
            $childNode.SetAttribute('name', $directory.Name)
            $parentNode.AppendChild($childNode)
            $this.ProcessChildNode($childNode, "$childPath\$($directory.Name)")
        }
        foreach ($file in $dirInfo.GetFiles()) {
            $childNode = $this.XML.CreateElement('file')
            $childNode.SetAttribute('name', $file.Name)
            $childNode.SetAttribute('size', $file.Length)
            $childNode.SetAttribute('hash', (Get-FileHash -Path:$file.FullName -Algorithm:MD5).Hash)
            $parentNode.AppendChild($childNode)
        }
    }

}


Measure-Command {
    $xml = [GenerateLibraryMap]::New('C:\assets\Revit\2020')
}

Write-Host "$($xml.MapFilePath)"

花同样的时间。但是无论如何都是教育性的。看起来,基于流的版本的内存效率略高一些。希望有人能找到有用的结果。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-21 15:53:45

这会导致明显更快的代码吗?

也许吧。找出答案的最简单方法(不实际分析当前的方法)就是继续做下去,然后比较结果:)

如何处理递归问题?

简单!

遵循以下规则:

  • 打开节点的递归函数调用也应该负责关闭节点-换句话说,递归函数的结构应该类似于(伪代码):
代码语言:javascript
复制
function Recurse
{
    WriteStartElement
    if($shouldRecurse){
      Recurse
    }
    WriteEndElement
}

只要你坚持这份表格,你就会没事的。

由于我正在学习OOP,我计划将其重构到一个类中,这既是为了提高性能,也是作为一个练习。当我从下一个兔子洞开始的时候,我有什么需要注意的问题吗?

可能是吧?

同样,最简单的方法就是继续做下去--如果你撞到墙的时候,StackOverflow仍然会在这里:)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61937366

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档