我有兩個XML文件:A.xml和B.xml。
每個XML包含類似事件的數量:如何在大型XML文件中查找事件對象
<Event>
<EventData Name="Time">09/10/2017 12:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
的值的例子。 。
我要搜索如果在A.XML已在B.XML相同"WorkstationName
「和"UserName"
值的事件
例如,這是個XML:
A.XML
<?xml version="1.0" encoding="UTF-8"?>
<Events>
<Event>
<EventData Name="Time">09/10/2017 12:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
</Events>
B.XML
<?xml version="1.0" encoding="UTF-8"?>
<Events>
<Event>
<EventData Name="Time">09/10/2017 14:54:16</EventData>
<EventData Name="WorkstationName">USER1-PC</EventData>
<EventData Name="UserName">user1</EventData>
</Event>
<Event>
<EventData Name="Time">09/10/2017 13:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
</Events>
預期結果:
<Event>
<EventData Name="Time">09/10/2017 13:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
我寫的確實是代碼:
算法:
$fileA = "C:\tmp\A.xml"
$fileB = "C:\tmp\B.xml"
$a = New-Object Xml.XmlDocument
$a.Load($fileA)
$b = New-Object Xml.XmlDocument
$b.Load($fileB)
$pc = ($event.EventData | Where-Object {$_.Name -eq "WorkstationName"})."#text"
$username = ($event.EventData | Where-Object {$_.Name -eq "UserName"})."#text"
$result = $b.Events.Event | Where-Object {
(($_.EventData | where-object {$_.Name -eq "WorkstationName"})."#text" -eq $pc) -and
(($_.EventData | where-object {$_.Name -eq "UserName"})."#text" -eq $username)
}
$result.EventData
問題是,當我與大B.XML文件工作(〜25萬線)。
我寫了一個代碼,將創建兩個XML實例(小A.XML文件和大B.XML文件):
function createXMLFiles($numberOfLinesToCreateInB){
$legitXmlPrefix = @(0xef, 0xbb, 0xbf, 0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3d, 0x22, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x22, 0x3f, 0x3e, 0x0d, 0x0a, 0x3c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x0d, 0x0a)
$XMLEnd = @(0x0d, 0x0a, 0x3c, 0x2f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3e)
$enc = [system.Text.Encoding]::UTF8
$aXML = @"
<Event>
<EventData Name="Time">09/10/2017 12:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
"@
$data1 = $enc.GetBytes($aXML)
$newXmlFile = "c:\tmp\A.xml"
$newArr = $legitXmlPrefix + $data1 + $XMLEnd
[io.file]::WriteAllBytes($newXmlFile, $newArr)
$bXML = @"
<Event>
<EventData Name="Time">09/10/2017 14:54:16</EventData>
<EventData Name="WorkstationName">USER1-PC</EventData>
<EventData Name="UserName">user1</EventData>
</Event>
<Event>
<EventData Name="Time">09/10/2017 13:54:16</EventData>
<EventData Name="WorkstationName">USER2-PC</EventData>
<EventData Name="UserName">user2</EventData>
</Event>
"@
$newXmlFile = "c:\tmp\B.xml"
$data1 = $enc.GetBytes($bXML)
$newArr = $legitXmlPrefix
$additionals = @"
<Event>
<EventData Name="Time">09/10/2017 14:54:16</EventData>
<EventData Name="WorkstationName">USER1-PC</EventData>
<EventData Name="UserName">user1</EventData>
</Event> `n
"@
$data2 = $enc.GetBytes($additionals)
if($numberOfLinesToCreateInB -gt 0){
$data2 = $data2 * $numberOfLinesToCreateInB
$newArr += $data2
}
$newArr += $data1
$newArr += $XMLEnd
[io.file]::WriteAllBytes($newXmlFile, $newArr)
}
createXMLFiles 50000
如果你運行我寫,你會看到,它需要很長時間才能算法在B.XML中從A.XML中查找事件。
這是因爲A.XML中的事件是B.XML中的最後一個事件,所以只有當它遇到B.XML中的最後一個節點時它纔會完成。
有沒有讓它更有效率的選擇?
我雖然也許使用多線程劃分的部分:一個線程將搜索事件0..1000之間,第二將搜索1001..2000等
但也許你有更好的解決方案。
參考文獻:
How can i use XmlReader in PowerShell to stream big/huge XML files?
How to create a new System.Xml.Linq.XElement with PowerShell
EDIT(FAST):
我試着用XPATH。它越過所有的事件時仍能正常工作緩慢:
Select-Xml -Path $fileB -XPath "/Events/Event" | Where-Object {
(($events[0].Node.EventData | where-object {$_.Name -eq "WorkstationName"})."#text" -eq $pc) -and
(($events[0].Node.EventData | where-object {$_.Name -eq "UserName"})."#text" -eq $username)
}
繼@Tomalak的建議,我刪除了幾乎所有的Where-Object
管道,事情開始以更快的速度運行。
Measure-Command {
Select-Xml -Path $fileB -XPath "/Events/Event" | Where-Object {
($_.Node.EventData[1]."#text" -eq $pc) -and
($_.Node.EventData[2]."#text" -eq $username)
}
}
Days : 0
Hours : 0
Minutes : 0
Seconds : 6
Milliseconds : 253
Ticks : 62535333
TotalDays : 7.23788576388889E-05
TotalHours : 0.00173709258333333
TotalMinutes : 0.104225555
TotalSeconds : 6.2535333
TotalMilliseconds : 6253.5333
Measure-Command {
$result = $b.Events.Event | Where-Object {
($_.EventData[1]."#text" -eq $pc) -and
($_.EventData[2]."#text" -eq $username)
}
}
Days : 0
Hours : 0
Minutes : 0
Seconds : 17
Milliseconds : 700
Ticks : 177006124
TotalDays : 0.000204868199074074
TotalHours : 0.00491683677777778
TotalMinutes : 0.295010206666667
TotalSeconds : 17.7006124
TotalMilliseconds : 17700.6124
它的工作速度更快,並且還使用-XPath
更快(6秒)比普通的傳遞方式(17秒)。
測試是在50,000個事件中,250,000個XML行。
EDIT(非常快):
的結局是這樣的:
Measure-Command {
Select-Xml -Path $fileB -XPath "/Events/Event[EventData[@Name = 'WorkstationName'] = '$pc' and EventData[@Name = 'UserName'] = '$username']"
}
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 609
Ticks : 6099484
TotalDays : 7.05958796296296E-06
TotalHours : 0.000169430111111111
TotalMinutes : 0.0101658066666667
TotalSeconds : 0.6099484
TotalMilliseconds : 609.9484
,這使得它非常快(小於1秒!)。
首先,測量XML文件加載需要多長時間。只需調用'$ b.Load($ fileB)'。如果可以接受的話,切換到使用XPath查找您的節點,而不是Powershell循環,看看是否改善了事情。如果即使加載時間本身是不可接受的,切換到[XMLReader](https://msdn.microsoft.com/en-us/library/system.xml.xmlreader(v = vs.110).aspx)來處理文件,就像你已經建議的那樣([C#examples](https://stackoverflow.com/q/2441673/18771),[Powershell example](https://stackoverflow.com/q/26820590/18771))。 – Tomalak
@Tomalak,用Load()加載大文件很快(1秒,甚至更少)。 – E235
@Tomalak我嘗試使用XPath,它在傳遞所有事件時仍然工作緩慢:'Select-Xml -Path $ fileB -XPath「/ Events/Event」| Where-Object {({$ events [0] .Node.EventData | where-object {$ _。Name -eq「WorkstationName」})。「#text」-eq $ pc)和 (($ events [0] .Node.EventData | where-object {$ _。Name -eq「UserName」})。「#text」-eq $ username)' – E235