【问题标题】:Substring from very large text file using Powershell使用 Powershell 来自非常大的文本文件的子字符串
【发布时间】:2020-11-18 17:01:42
【问题描述】:

我有一个非常大的文本文件 (209 MB),其中包含我试图隔离的一大块 XML。文本文件是两个不同的 XML 段,我想要第二段。使用 PowerShell,我正在尝试使用 match 从我想要的开头隔离到结尾,寻找一个始终位于我想要的段的开头和结尾的特定字符串,它不起作用:

 $Hello = Get-Content "E:\sandbox\test.txt" 
 $Hello -match "</ns1:GetAllCompliancesResponse>(?<content>.*)</soapenv:Body>"
 $Hello = $Matches['content'] 
 $Hello | Out-File -FilePath "E:\sandbox\testoutput.txt"

当我在 PS 中运行它时出现空数组错误。我不太确定问题出在哪里。

在我得到很大帮助后添加更多内容:

我的数据源是一个 200mb 的文件。由于它的大小,测试解析是一个艰巨的过程。如果我的一般结构是:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <ns1:GetAllCompliancesResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://webservices.web.arber.arb.ca.gov">
     <GetAllCompliancesReturn soapenc:arrayType="ns2:ComplianceSummary[263026]" xsi:type="soapenc:Array" xmlns:ns2="urn:DrayageTruckStatusService" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
        <GetAllCompliancesReturn href="#id0"/>
        <GetAllCompliancesReturn href="#id1"/>
        <GetAllCompliancesReturn href="#id2"/>
        <GetAllCompliancesReturn href="#id3"/>
        <GetAllCompliancesReturn href="#id4"/>
        <GetAllCompliancesReturn href="#id263024"/>
        <GetAllCompliancesReturn href="#id263025"/>
     </GetAllCompliancesReturn>
  </ns1:GetAllCompliancesResponse>
     <multiRef id="id83299" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns3:ComplianceSummary" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns3="urn:TruckStatusService">
     <dtrNumber xsi:type="xsd:string">*********</dtrNumber>
     <licensePlateNumber xsi:type="xsd:string">*******</licensePlateNumber>
     <licensePlateState xsi:type="xsd:string">**</licensePlateState>
     <status xsi:type="xsd:string">************</status>
     <traceNumber xsi:type="xsd:int" xsi:nil="true"/>
     <untilDate xsi:type="xsd:date">***********</untilDate>
     <vin xsi:type="xsd:string">**********************</vin>
  </multiRef>
  <multiRef id="id132635" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns4:ComplianceSummary" xmlns:ns4="urn:TruckStatusService" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
     <dtrNumber xsi:type="xsd:string">*********</dtrNumber>
     <licensePlateNumber xsi:type="xsd:string">*******</licensePlateNumber>
     <licensePlateState xsi:type="xsd:string">**</licensePlateState>
     <status xsi:type="xsd:string">***********</status>
     <traceNumber xsi:type="xsd:int" xsi:nil="true"/>
     <untilDate xsi:type="xsd:date">***********</untilDate>
     <vin xsi:type="xsd:string">**********************</vin>
   </multiRef>
   </soapenv:Body>
</soapenv:Envelope>

如何形成 XPath 以获取 multiRef 节点中的项目?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    成功的-match 仅在标量模式 中使用时填充$Matches(即当左侧操作数为单个对象时),当您使用@987654323 时它不起作用@ 过滤集合。

    $Hello = Get-Content "E:\sandbox\test.txt" |ForEach-Object {
      if($_ -match "</ns1:GetAllCompliancesResponse>(?<content>.*)</soapenv:Body>"){
        $Matches['content']
      }
    }
    
    $Hello | Out-File -FilePath "E:\sandbox\testoutput.txt"
    

    这种方法只有在整个字符串都在一行时才有效。


    鉴于内容是 XML,我宁愿建议使用 PowerShell 中提供的一些出色的 XML 解析工具,而不是正则表达式。

    假设您希望所有兄弟节点都遵循&lt;/ns1:GetAllCompliancesResponse&gt;,您可能会执行以下操作:

    $Content = Get-Content .\big.xml |Select-Xml -XPath '//*[local-name() = "GetAllCompliancesResponse"]' |ForEach-Object {
      $node = $_.Node
      while($node = $node.NextSibling){
        $node.OuterXml # output current node markup as text
      }
    }
    
    $Content |Out-File .\output.txt
    

    【讨论】:

    • 感谢您的回答。我正在使用 Select-Xml -XPath //multiRef 并且出现错误:“'soapenv' 是一个未声明的前缀。我的文件来自 WSDL 端点。我需要在顶部放置一个 XML 声明吗?
    • 要么添加声明命名空间前缀的声明,要么将哈希表传递给Select-Xml -Namespace @{ soapenv = "schema URI goes here" }
    • 我将更新我的帖子以显示我的数据结构。您能否协助 Xpath/命名空间?你真的在教我我需要的工具。谢谢。
    • @alcor8 不太确定为什么会收到您提到的错误 - 您添加到帖子中的示例文档具有正确的 ns 声明,我可以用 Select-Xml 解析它就好了:-/
    • @alcor8:我建议恢复问题编辑,接受基于问题最初提出的问题的答案(Mathias 的答案似乎合适),然后询问 new ,解决您的命名空间/XPath 问题的重点问题。
    【解决方案2】:

    补充Mathias R. Jessen's helpful answer

    假设基于正则表达式的逐行纯文本处理是可能的(根据您以后的更新,它不可能;另外,如在 Mathias 的回答中说,通常最好使用 XML 解析):

    逐行处理文件的有效方法是switch statement,其-Regex 选项也填充automatic $Matches variable

    Set-Content E:\sandbox\testoutput.txt -Encoding utf8 -Value $(
      switch -Regex -File E:\sandbox\test.txt {
        '</ns1:GetAllCompliancesResponse>(?<content>.*)</soapenv:Body>' {
          $Matches.content
        }
      }
    )
    

    注意使用-Value 将文件内容传递给Set-Content,这比通过管道提供内容要快得多;虽然使用-Value 需要先收集内存中的所有内容,但在您的情况下这应该不是问题。

    【讨论】:

      猜你喜欢
      • 2013-11-15
      • 2013-01-01
      • 1970-01-01
      • 2015-03-23
      • 2011-03-31
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多