2016-03-02 65 views
2

我有兩個大型(> 100MB,每個數百萬行)XML文件,其結構如下。Powershell - 比較兩個大型XML文件的部分

<?xml version='1.0' encoding='UTF-8'?> 
<index> 
    <doc id='0'> 
     <field name='PART' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val>12345-678</val> 
     </field> 
     <field name='DESCRIPTION' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val>Part XYX123 Description</val> 
     </field> 
     <field name='QTY' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val>18</val> 
     </field> 
     <field name='VENDOR' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val>ACME</val> 
     </field> 
     <field name='MFG' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val></val> 
     </field> 
    </doc> 
    <doc id='1'> 
     <field name='PART' norm='124' flags='Idfp--S--Ni08--------'> 
      <val>ABCD-1234</val> 
     </field> 
     <field name='DESCRIPTION' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val>PART ABCD Description</val> 
     </field> 
     <field name='QTY' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val>4</val> 
     </field> 
     <field name='VENDOR' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val></val> 
     </field> 
     <field name='MFG' norm='-1' flags='Idfp--S--Ni08--------'> 
      <val></val> 
     </field> 
    </doc> 
</index> 

我需要找到一個項目,而不是另一個,反之亦然。最初,我只想比較屬性PART的值,但還想比較其他值(描述等)。

我想,以確定是否有什麼在xmlfile1:

index/doc/field name=part/val - 12345-678 

也xmlfile2。如果不是,請將其寫入文本/ csv文件。

我試過使用Compare-Object & Get-Content,但我遇到的其中一個問題是每個XML文件中的其他屬性。這兩個XML文件可能有

index/doc/field name=part/val - 12345-678 

,但不同的是,xmlfile1可能有不同的值常態&標誌比xmlfile2屬性。這使得使用Compare-Object & Get-Content標記一切。

使用Powershell,您將如何進行比較忽略「噪音」屬性,但只匹配PART屬性的<value>

EDIT

爲了澄清 - 第二XML文件將是幾乎相同所示。然而 - 什麼可能是不同的是<doc id='0'>在任何XML中,<field name='PART'將是相同的,但其他屬性norm='-1'flags='Idfp--S--Ni08--------'>可能會在每個不同。我想查找PART屬性,忽略field中的其餘屬性,並確定<val>中的內容是否存在於第二個XML文件中。

+0

你有沒有xmlfile2的樣本?你如何知道要比較哪些元素?既然你說PART-MAY可能是一樣的,那就沒用了。什麼是靜態的?文檔ID的靜態? –

+0

@ FrodeF.-爲了清晰起見,我添加了一些更多信息。屬性'name'將是靜態的,但其他('norm','flags')可能具有不同的值。在屬性'name = PART'中,我想確定''中的內容是否在XML文件2中。忽略該行中其餘的屬性。此外,'

就像我說的,有可能是一個更有效答案,但這應該是有效的。

+0

我需要更有效的答案。對於2個大的XML文件,在我可以加載第一個XML文件之前,'Get-Content'會給出一個'Out of Memory'錯誤。適用於小文件。 –

0

我會使用與@TheMadTechnician的答案相同類型的解決方案,但它確實需要一點內存(至少當你有大文件時)。但是有一些方法可以優化它。你說它在內存不足Get-ContentGet-Content使用每行一個字符串創建一個對象數組。既然我們要將它轉換爲xml文檔,我們可以將該文件讀爲一個簡單的字符串,這會爲我們節省大量內存。

如果仍有問題,您可能需要在具有更多資源的計算機上運行此腳本。當我們可以將整個文件保存到內存中時,XML解析更容易。

$xml = [xml]([System.IO.File]::ReadAllText("c:\path\to\file1.xml")) 
$File1Objs = $xml.index.doc | ForEach-Object { 
    $Obj = New-Object psobject -Property @{"ID" = $_.id} 
    $_.field | ForEach-Object { Add-Member -InputObject $Obj -MemberType NoteProperty -Name $_.Name -Value $_.val } 
    $Obj 
} 
#Throw out the garbage 
$xml = $null 
[gc]::Collect() 

$xml = [xml]([System.IO.File]::ReadAllText("c:\path\to\file2.xml")) 
$File2Objs = $xml.index.doc | ForEach-Object { 
    $Obj = New-Object psobject -Property @{"ID" = $_.id} 
    $_.field | ForEach-Object { Add-Member -InputObject $Obj -MemberType NoteProperty -Name $_.Name -Value $_.val } 
    $Obj 
} 

#Throw out the garbage 
$xml = $null 
[gc]::Collect() 

#One compare to save resources. Compare PART and Description-property (to show off multiple-property-comparison) 
$comparison = Compare-Object $File1Objs $File2Objs -Property Part, Description -PassThru 

$comparison | Where-Object { $_.SideIndicator -eq '<=' } | Select-Object -Property * -Exclude SideIndicator | Export-CSV -Path "c:\path\to\File1Only.txt" -NoTypeInformation 
$comparison | Where-Object { $_.SideIndicator -eq '=>' } | Select-Object -Property * -Exclude SideIndicator | Export-CSV -Path "c:\path\to\File2Only.txt" -NoTypeInformation 

你也可以用你來自哪裏,文件1存儲值的基於哈希表的解決方案和價值,當你閱讀文件2與比較。例如:

#Read as single string to save memory 
$text = [System.IO.File]::ReadAllText("C:\users\frode\Test.txt") 

#Hashtable to store PART-value from file1 
$PART = @{} 
#Regex to extract PART-value 
[regex]::Matches($text,"(?s)doc id='(?<ID>.*?)'>.*?'PART' norm.*?val>(?<PART>.*?)<\/val>") | 
ForEach-Object { 
    #Store PART-value in hashtable with doc-id as key 
    $PART.Add($_.Groups["ID"].Value,$_.Groups["PART"].Value) 
} 

$text = [System.IO.File]::ReadAllText("C:\users\frode\Test2.txt") 
[regex]::Matches($text,"(?s)doc id='(?<ID>.*?)'>.*?'PART' norm.*?val>(?<PART>.*?)<\/val>") | 
ForEach-Object { 
    #Check if docid was in file1 
    if($PART.ContainsKey($_.Groups["ID"].Value)) { 
     #If in file1, check if value is different 
     if($PART[$_.Groups["ID"].Value] -ne $_.Groups["PART"].Value) { 
      "MISMATCH in DocID '$($_.Groups["ID"].Value)' - File1 PART: '$($PART[$_.Groups["ID"].Value])' - File2 PART: '$($_.Groups["PART"].Value)'" 
     } 
    } 
} 

輸出:

MISMATCH in DocID '0' - File1 PART: '12345-678' - File2 PART: '12345-6789' 
MISMATCH in DocID '1' - File1 PART: 'ABCD-1234' - File2 PART: 'ABCD-1235' 

這僅僅是一個使用正則表達式驗證的概念。使用像這樣的文本解析解決方案(使用前面的哈希表來存儲值),您可以使用StreamReader一次讀取一行,以最大限度地減少內存使用量。