Экспорт Hashtable в CSV - PullRequest
       8

Экспорт Hashtable в CSV

0 голосов
/ 30 августа 2018

Я пытаюсь написать скрипт Powershell, который будет принимать несколько очень длинных файлов, разделенных пробелами, и экспортировать некоторые столбцы в файлы CSV с одинаковыми именами.

У меня есть успешная версия:

Foreach ($file in $files) {
    $WriteString=""
    $outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"  

    Get-Content -Path $path"\"$file | Select-Object -Skip $lines | ForEach-Object{
        $ValueArray = ($_ -split "\s+")
        $WriteString += $ValueArray[1] + "," + $ValueArray[2] + "," + $ValueArray[3] + "`n"
    } 

    Add-Content -Path $outfile -Value $Writestring
 }

Это работает, но очень медленно - для полного запуска скрипта требуется более 16 часов. Основной причиной (я думаю) является добавление к строке. Я попытался улучшить это с помощью хеш-таблицы:

Foreach ($file in $files) {
    $outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"

    $ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines 

    $OutputData = ForEach ($Line in $ParseLines) {
        $ValueArray = ($Line -split "\s+")
        $Line | Select-Object $ValueArray[1], $ValueArray[2], $ValueArray[3]
    } 

   $OutputData | Export-CSV -Path $outfile #-NoTypeInformation
 }    

Однако это экспорт только одной строки хеш-таблицы:

#TYPE Selected.System.String
"636050.000","7429825.000","77.438"
,,
,,
,,
,,
,,
,,

Если я изменю последнюю строку на:

Set-Content -Path $outfile -Value $OutputData

тогда вывод будет:

@{636050.000=; 7429825.000=; 77.438=}
@{636075.000=; 7429825.000=; 75.476=}
@{636100.000=; 7429825.000=; 74.374=}
@{636125.000=; 7429825.000=; 73.087=}
@{636150.000=; 7429825.000=; 71.783=}
@{636175.000=; 7429825.000=; 70.472=}

Я явно делаю что-то не так с хэш-таблицей или Export-CSV, но не могу понять. Любая помощь будет принята с благодарностью.

Как указано ниже, здесь часть одного исходного файла. Я вырезал все строки, не относящиеся к данным, и не включал заголовки в свой выходной CSV, так как входная программа (в которую входят файлы CSV) не требует их, а выходные данные очевидны (не так много шансов на неверные значения X, Y и Z, просто взглянув на данные)

*
* DEFINITION
*   HEADER_VARIABLES 3
*     QUALITIES        C  16   0 key
*     DATE             C  12   0
*     TIME             C  12   0
*   VARIABLES 4
*     X                F  12   3
*     Y                F  12   3
*     Z                F  12   3
*     gcmaq0.drg       F  12   3
*
*        1         2         3         4
*23456789012345678901234567890123456789012345678
*         X|          Y|          Z| gcmaq0.drg|
*
* HEADER:QUALITIES       29Aug2018   13:53:16    
  636575.000 7429800.000      75.551      75.551
  636600.000 7429800.000      77.358      77.358
  636625.000 7429800.000      78.823      78.823
  636650.000 7429800.000      80.333      80.333
  636675.000 7429800.000      82.264      82.264
  636700.000 7429800.000      84.573      84.573
  636725.000 7429800.000      87.447      87.447

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Избегайте медленных операций, таких как добавление к строкам (или массивам) в цикле. Изменить это:

Get-Content -Path $path"\"$file |
    Select-Object -Skip $lines |
    ForEach-Object {
        $ValueArray = ($_ -split "\s+")
        $WriteString += $ValueArray[1] + "," + $ValueArray[2] + "," + $ValueArray[3] + "`n"
    }

Add-Content -Path $outfile -Value $Writestring

в это:

Get-Content -Path "${path}\${file}" |
    Select-Object -Skip $lines |
    ForEach-Object {
        ($_ -split "\s+")[1..3] -join ','
    } |
    Set-Content -Path $outfile

Замените Set-Content на Add-Content, если вы действительно хотите добавить к существующему файлу.

0 голосов
/ 31 августа 2018

Приведенное выше решение Ансгара Вихера сработало лучше, но я также нашел второй способ сделать это на этом вопросе. Он использует ArrayList для хранения хеш-таблицы, затем пишет ArrayList. Этот метод почти, но не так быстр, как решение Ансгара. (Примерно в 10 раз быстрее, чем строковый метод, против 12x для метода регулярных выражений)

Foreach ($file in $files) {
    [System.Collections.ArrayList]$collection = New-Object System.Collections.ArrayList($null)
    $outfile = $path + "\" + ($file -replace ".{4}$") + ".csv" 

    $ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines 

    $OutputData =@{}
    ForEach ($Line in $ParseLines) {
        $ValueArray = ($Line -split "\s+")
        $OutputData.Easting = $ValueArray[1]
        $OutputData.Northing = $ValueArray[2]
        $OutputData.ZValue = $ValueArray[3]

        $collection.Add((New-Object PSObject -Property $OutputData)) | Out-Null
    } 

    $collection | Export-CSV -Path $outfile -NoTypeInformation
 }
0 голосов
/ 30 августа 2018

Export-Csv работает с объектами. Он ожидает свойства и значения - то, что вы предоставляете (судя по результатам Set-Content), может быть хеш-таблицей только с ключами.

Одним из способов решения этой проблемы является создание объекта и увеличение значений из каждой строки.

Foreach ($file in $files) {

    $outfile    = $path + "\" + ($file -replace ".{4}$") + ".csv"
    $ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines 

    ForEach ($Line in $ParseLines) {

        $ValueArray = ($Line -split "\s+")

        [array]$OutputData += [pscustomobject]@{
            header1 = $ValueArray[1]
            header2 = $ValueArray[2]
            header3 = $ValueArray[3]
        }

    } 

   $OutputData | Export-CSV -Path $outfile #-NoTypeInformation

}

Не уверен, что это оптимальный способ, если у вас очень большие файлы - уверен, что гуру регулярных выражений может придумать что-нибудь более эффективное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...