【问题标题】:Exporting Hashtable to CSV将哈希表导出为 CSV
【发布时间】:2019-02-04 20:07:37
【问题描述】:

我正在尝试编写一个 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

【问题讨论】:

  • 您能否分享(部分)您的输入文件(包括标题)并将其添加到问题中?

标签: powershell csv hashtable


【解决方案1】:

避免缓慢的操作,例如在循环中附加到字符串(或数组)。改变这个:

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

【讨论】:

  • 感谢您的评论,它非常快。但是,输出不是 CSV 格式。它生成单列数据。我是否需要像原始脚本一样将这些值重新连接在一起?
  • ($_ -split "\s+")[1..3] -join ","ForEach-Object?如果标题不在输入文件中,您需要单独处理它们。
  • 完美,做到了!我需要的确切输出,并且速度极快。看起来它至少比我原来的解决方案快 12 倍。
  • @gms0ulman 这个问题看起来没有标题。至少没有应该出现在输出文件中。
  • @AnsgarWiechers 很公平 - 在处理这些问题时,我确实有 psobject 偏见。
【解决方案2】:

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

}

如果您有非常大的文件,不确定这是否是最佳方式 - 我确信正则表达式专家可以提出更有效的方法。

【讨论】:

  • 感谢您的建议 - 它有效,非常棒!不幸的是,它的速度和我原来的基于字符串的解决方案差不多。
  • 我担心文件很大会成为问题 - 不过对于较小的数据集值得了解。 Ansgar's answer 是我希望发布的那种正则表达式大师解决方案!
【解决方案3】:

Ansgar Wiechers 上面的解决方案效果最好,但我还在this SO question. 找到了第二种方法,它使用 ArrayList 存储哈希表,然后写入 ArrayList。这种方法几乎,但不如 Ansgar 的解决方案那么快。 (比字符串方法快 10 倍左右,而 regex 方法快 12 倍)

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
 }

【讨论】:

    猜你喜欢
    • 2018-01-22
    • 2015-10-06
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 2016-10-17
    • 2020-01-24
    相关资源
    最近更新 更多