Мне нужно просмотреть файл csv, содержащий до 58 тыс. строк. Каждая строка имеет 60 значений, разделенных точкой с запятой. Для каждой строки мне нужно создать один XML-файл. Мой сценарий делает то, что я ожидал, но на создание файлов размером 39 КБ уходит 20 часов, и он работает очень медленно, поскольку потребляет всю системную память. Всего у вас есть 10 файлов csv, содержащих от 30 до 90 тысяч строк. Для каждой из этих строк мне нужно создать один XML-файл.
Как я могу избежать этого потребления памяти, есть ли лучший способ сделать это?
Я читаю данные csv с помощью Import-csv, зацикливаясь на каждом идентификаторе в файле. Это мой код:
функция createxmlfromcsvcharge{
$csv = Import-csv $conf_YearFiles$conf_File -Delimiter ";"
foreach($ChargingID in $CSV){
$ID = $ChargingID.ChargingID
#file name
$FileName = "MIS_Record_2013_"+$ID+".xml"
# Creating a simple XML document
$xmlDocument = New-Object System.Xml.XmlDocument
$dec = ($xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", $null))
$root = $xmlDocument.DocumentElement
$xmlDocument.InsertBefore($dec,$root)
# Adding a root element
$rootElement = $xmlDocument.CreateElement("ChargingRecord")
$xmlDocument.AppendChild($rootElement)
# create <ChargingID> container node
$xmluser = $xmlDocument.CreateElement('Charge')
$rootElement.AppendChild($xmluser)
# add each property from csv entry to <ChargingID>
Foreach ($property in $csv[0].psobject.Properties.name) {
# create a property node
$xmlproperty = $xmlDocument.CreateElement($property)
$text = $xmlDocument.CreateTextNode($ChargingID.$property)
$xmlproperty.AppendChild($text)
# add to current <ChargingID> node
$xmluser.AppendChild($xmlproperty)
}
$xmlDocument.Save('m:\temp\2013\'+$FileName)
}
}
Код генерирует именно то, что я ожидаю.
Любая помощь или подсказки действительно ценятся. Спасибо.
Привет, Матиас! Большое спасибо за подсказку, все работает отлично! !
Рад слышать! Я добавил правильный ответ ниже :)





Это требует много времени и памяти, поскольку управляет полным весом файла и относительного объекта.
Простое подключение Import-Csv к Foreach-Object должно улучшить ситуацию:
$DestinationDirectory = 'm:\temp\2013\'
Import-Csv $conf_YearFiles$conf_File -Delimiter ';' |
ForEach-Object {
$ID = $PSItem.ChargingID
#file name
$FileName = Join-Path -Path $DestinationDirectory -ChildPath "MIS_Record_2013_${ID}.xml"
# Creating a simple XML document
$xmlDocument = New-Object System.Xml.XmlDocument
$dec = ($xmlDocument.CreateXmlDeclaration('1.0', 'UTF-8', $null))
$root = $xmlDocument.DocumentElement
$xmlDocument.InsertBefore($dec, $root)
# Adding a root element
$rootElement = $xmlDocument.CreateElement('ChargingRecord')
$xmlDocument.AppendChild($rootElement)
# create <ChargingID> container node
$xmluser = $xmlDocument.CreateElement('Charge')
$rootElement.AppendChild($xmluser)
# add each property from csv entry to <ChargingID>
Foreach ($property in $PSItem.psobject.Properties.name) {
# create a property node
$xmlproperty = $xmlDocument.CreateElement($property)
$text = $xmlDocument.CreateTextNode($PSItem.$property)
$xmlproperty.AppendChild($text)
# add to current <ChargingID> node
$xmluser.AppendChild($xmlproperty)
}
$xmlDocument.Save( $FileName)
}
Как намекают в комментариях, нехватка памяти вызвана поведением XmlDocument API!
Когда вы вызываете InsertNode(...) или AppendChild(...), возвращается вставленный/добавленный экземпляр узла.
Если вы не отбросите это возвращаемое значение активно, процессор среды выполнения PowerShell соберет его как выходные данные. Поскольку экземпляры узла ссылаются на документ-владелец, сборщик мусора не может очистить документы, созданные вами во время предыдущих итераций цикла, до тех пор, пока весь метод не вернется и вызывающая сторона не отбросит ссылки.
Решение состоит в том, чтобы отбросить возвращаемые значения на месте вызова — я предпочитаю приводить выражение вызова метода к [void], но вы можете передать его по каналу Out-Null или назначить $null для того же эффекта:
[void]$xmlDocument.InsertBefore($dec, $root)
# or
$null = $xmlDocument.InsertBefore($dec, $root)
# or
$xmlDocument.InsertBefore($dec, $root) |Out-Null
Повторите эти действия для любых вызовов AppendChild() (и любого другого вызова метода, который возвращает ссылку на узел), и сборщик мусора сможет свободно выполнять свою работу :)
Измените
$xmlDocument.InsertBefore(...)на[void]$xmlDocument.InsertBefore(...), затем сделайте то же самое со всеми вызовамиAppendChild(...)- в противном случае эти методы выведут ссылку на вставленный/добавленный узел, заставляя PowerShell собирать ее как выходные данные, что, в свою очередь, предотвращает сбор мусора экземпляров XML-документа. из предыдущих итераций