首页 > 解决方案 > Powershell 在单个管道中更新多个 CSV 列值

问题描述

我正在处理 CSV 中的“修复”数据并使用 PowerShell,因为行数超出了 Excel 的喜好,而且我通常在一个文件夹中也有多个 CSV,我需要对其进行相同的更新。我很难找到一种简洁的方法来检查各个列的数据,然后根据需要进行更新。我可以使用多个管道/命令来完成它,但我想一次完成所有操作,而且我也对做类似的事情持谨慎态度,$csvData = Import-CSV...因为有很多行。

下面是我开始使用的 3 个命令,关于如何一次运行它们的任何想法?

Import-CSV $Filepath | %{if([string]::IsNullOrEmpty($_."Type of Admission")){$_."Type of Admission" = "9"} ; $_} | Export-CSV $NewFilepath -UseQuotes AsNeeded
Import-CSV $Filepath | %{if([string]::IsNullOrEmpty($_."Service from Dates") -And !([string]::IsNullOrEmpty($_."Service to Dates"))){$_."Service from Dates" = $_."Service to Dates"} ; $_} | Export-CSV $NewFilepath -UseQuotes AsNeeded
Import-CSV $Filepath | %{if(($_."Financial Class" -eq "Self-Pay")){$_."Payer" = $_."Health Plan Identification Number" = $_."Financial Class"} ; $_} | Export-CSV $NewFilepath -UseQuotes AsNeeded

我正在尝试尝试的一件事是使用 Switch cmdlet 而不是超级嵌套的 If 函数。我对 Switch 的想法有点像在 VBA 中使用“With”语句和执行所有真实案例的 Select Case。因此,而不是Switch($_.FirstProperty)甚至If($_.FirstProperty)可以我只是说Switch($_)然后将每个属性/列引用为.FirstProperty?唉,我不认为它会那样工作 - 但如果确实如此,那么我想我的代码将如下所示:

Import-CSV $Filepath | %{
    Switch($_) {
        [string]::IsNullOrEmpty($_."Type of Admission"){$_."Type of Admission" = "9"}
        [string]::IsNullOrEmpty($_."Service from Dates") -And !([string]::IsNullOrEmpty($_."Service to Dates")){$_."Service from Dates" = $_."Service to Dates"}
        ($_."Financial Class" -eq "Self-Pay"){$_."Payer" = $_."Health Plan Identification Number" = $_."Financial Class"}
    }
Export-CSV $NewFilepath -UseQuotes AsNeeded}

编辑:对于计划使用Cpt.Whale将数据保存在内存中而不是每行写入磁盘的建议的任何人;它的工作方式与您预期的相似,但 CSV 数据与您的标准 Get-Content/Set-Content 工作流程相比有点特别。具体来说,变量 like$report将需要特殊特性。我目前的建议是使用$outFile = New-Object System.Collections.ArrayList.Add 方法来进行填充。有人会说这很糟糕,但这个问题可能不会在列表中排名太靠前,而且速度快而且有效。

标签: powershellcsv

解决方案


首先,有一些方法可以一次读取一行文件,但它们通常是不必要的,除非内存不足,例如千兆字节以上的 csv 文件。如果是这种情况,那么我相信您想使用System.IO.File这样的加速器而不是Import-CSV

foreach($row in [System.IO.File]::ReadLines($Filepath)) {
    # Do Stuff to $row
}

此问题的答案中有更多示例,但您需要小心文件流锁定,这可能会很痛苦。

此外,可以通过同时处理所有if语句来节省大量时间,而不是遍历整个文件 3 次。例如:

# Import once if you have the memory.
$data = Import-CSV $Filepath

# Iterate once
Foreach ($row in $data) {
    if ( [string]::IsNullOrEmpty($row."Type of Admission") ) { 
        $row."Type of Admission" = "9" 
    }
    if ( [string]::IsNullOrEmpty($row."Service from Dates") -And -Not ([string]::IsNullOrEmpty($row."Service to Dates")) ) {
        $row."Service from Dates" = $row."Service to Dates"
    }
    if ( $row."Financial Class" -eq "Self-Pay" ) {
        $row."Payer" = $row."Health Plan Identification Number" = $row."Financial Class"
    }
    
    # export finalized row
    $row | Export-CSV $NewFilepath -UseQuotes AsNeeded
}

对于你关于Switch的最后一个问题,它只比较单个值,而不是对象。它对很多事情都很好,但除非你有更多的陈述要添加,否则这里就不适用。它更适合处理一列数据,例如if 'Type of Admission' is 1 then A; 2 then B;等。


推荐阅读