【问题标题】:PowerShell Delete or Skip Columns in CSVPowerShell 删除或跳过 CSV 中的列
【发布时间】:2020-09-14 12:48:49
【问题描述】:

首先:我是 PowerShell 新手。我几乎没有使用 PowerShell 修改或更改 CSV 文件的经验。

我们的系统给出了一种不常见的 CSV 格式,如下所示:

Example1;Example2;Name;Lastname;ContentOfExample1;ContentOfExample2;John;Doe

标题位于信息前面的每一行。我想去掉一些列,例如 Example1 和 Example2。

作为第二步,我需要分配一个新的标题

-Header Name,Lastname,Adress,Phone,.. 等等。

感谢任何提示 :-)

【问题讨论】:

  • 引用的标题和值是否与 CSV 文件中的典型值一样?
  • 您是否希望保留当前格式(每行都有这些“内联标题”)?

标签: powershell csv


【解决方案1】:

根据定义,这种模式会产生偶数个“;”分隔元素。您可以利用它来发挥自己的优势,通过算术将属性分配给对象,然后将它们重新发送到新的 CSV 文件。

可能看起来像:

Get-Content C:\Temp\InitialCSVFile.csv |
ForEach-Object{
    $TempArr  = $_.Split( ';' )
    $TempHash = [Ordered]@{}
    For($i = 0; $i -lt ($TempArr.Count / 2); ++$i)
    {
        $TempHash[ $TempArr[ $i ] ] = $TempArr[ $i+4 ]
    }
    [PSCustomObject]$TempHash
} |
Export-CSV -path C:\Temp\TestCSV.csv -NoTypeInformation -Append -Delimiter ';'

代码将文件内容作为纯字符串读取,而不是像 CSV 这样的半结构化格式。由于每一行都通过管道传送到ForEach-Object.Split() 字符串方法正在创建一个数组($_ -split ';' 也可以使用)。我们实例化一个 Hash/Dictionary 对象来保存一些键值对。完成后,将使用传统的 For 循环来引用 kay 名称和值。名称是元素 0,因此它的值应该是 0+4。注意:循环被编码为在数组的中点处停止。这就是为什么我之前提到的偶数个元素很重要!

哈希表完成后,代码会将其转换为[PSCustomObject],并将其沿管道发送到Export-CSV,这当然会处理对象。这应该会生成一个类似于以下内容的新 CSV 文件:

Example1          Example2          Name Lastname
--------          --------          ---- --------
ContentOfExample1 ContentOfExample2 John Doe
ContentOfExample1 ContentOfExample2 John Doe

注意:显然数据是多余的,因为我只是重复了您的 输入文件中的示例。这不应该是你生活的问题 数据。

注意:可能不需要重复重新创建$TempHash,因为我们将 在每个循环内部迭代中重新分配每个键的值。目前 我会让这个例子保持原样。

更新:排除属性:

$ExcludeProperties = @( 'Example1', 'Example2' )

Get-Content C:\Temp\InitialCSVFile.csv |
ForEach-Object{
    $TempArr  = $_.Split( ';' )
    $TempHash = [Ordered]@{}
    For($i = 0; $i -lt ($TempArr.Count / 2); ++$i)
    {
        $TempHash[ $TempArr[ $i ] ] = $TempArr[ $i+4 ]
    }
    [PSCustomObject]$TempHash
} |
Select-Object -Property * -ExcludeProperty $ExcludeProperties |
Export-CSV -path C:\Temp\TestCSV.csv -NoTypeInformation -Append -Delimiter ';'

【讨论】:

  • 谢谢!我从来没有想过这样做。我将使用原始文件对此进行测试。只是为了澄清,[ $i+4 ] 4 是标题的列数,对吧?不管怎样,这对我帮助很大。
  • 是的,所以从 [0+4] 开始就是 [4]。注意:您可以通过将其存储在变量中来与其他一些样本组合。这可能有助于使其更易于维护,并且可能类似于:$Offset = $TempArr.count / 2 然后[$i +$Offset]。如果文件更改...
【解决方案2】:

确实是一种奇怪的输出 CSV 的方式..

您可以做的是用分隔符 ; 分割第一行,以便获取每列的标题。

有了这些,剩下的就不难做到了:

$csv = Get-Content -Path 'D:\Test\blah.csv' | Where-Object {$_ -match '\S'}

$parts = $csv[0] -split ';'
# calculate the number of parts that make up the headers
[int]$numberOfHeaders = $parts.Count / 2
# join the headers into a string
$header = $parts[0..($numberOfHeaders - 1)] -join ';'
# cut off the headers from every line
$rows = foreach ($line in $csv) { $line.Substring($header.Length + 1) }

# convert to an array of objects, skip the first two columns and export to a new file
$header, $rows | ConvertFrom-Csv -Delimiter ';' | 
    Select-Object * -ExcludeProperty $parts[0..1] | 
    Export-Csv -Path 'D:\Test\blah2.csv' -Delimiter ';' -NoTypeInformation

【讨论】:

    【解决方案3】:

    假设列数可以是随机的并且要排除的属性是已知的,您可以执行以下操作将数据解析为自定义对象:

    Get-Content file.csv | Foreach-Object {
        $count = 0 # Tracks column counts to split the row evenly
        $cols = $_ -split ';'
        # $headers gets the first half of the columns. $data gets the remainder.
        $headers,$data = $cols.where({$count++ -lt $cols.count/2},'Split')
        # Uses calculated properties to add your new properties. You will need to fill in your own logic since you provided none here.
        ($headers -join ';'),($data -join ';') | ConvertFrom-Csv -Delimiter ';' |
            Select-Object *,@{n='Address';e={'Electric Avenue'}},@{n='Phone';e={'867-5309'}} -exclude example1,example2
    }
    

    如果 csv 文件中的所有数据都包含相同的标题,您可以使用 Export-Csv 从数据中创建正确的 CSV:

    Get-Content file.csv | Foreach-Object {
        $count = 0 # Tracks column counts to split the row evenly
        $cols = $_ -split ';'
        $headers,$data = $cols.where({$count++ -lt $cols.count/2},'Split')
        ($headers -join ';'),($data -join ';') | ConvertFrom-Csv -Delimiter ';' |
            Select-Object *,@{n='Address';e={'Electric Avenue'}},@{n='Phone';e={'867-5309'}} -exclude example1,example2
    } | Export-Csv output.csv -NoType
    

    如果每个单独的行可能有不同数量的列,则您可能需要每行一个 CSV 文件,除非您解析所有数据并确定所有可能的列名。如果您想保持与源相同的格式但只想操作列和数据,您可以执行以下操作,这将适用于不同数量的列:

    Get-Content file.csv | Foreach-Object {
        $count = 0 # Tracks column counts to split the row evenly
        $cols = $_ -split ';'
        $headers,$data = $cols.where({$count++ -lt $cols.count/2},'Split')
        $newObj = ($headers -join ';'),($data -join ';') | ConvertFrom-Csv -Delimiter ';' |
            Select-Object *,@{n='Address';e={'Electric Avenue'}},@{n='Phone';e={'867-5309'}} -exclude example1,example2
        "{0};{1}" -f ($newObj.psobject.properties.name -join ';'),($newObj.psobject.properties.value -join ';')
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-15
      • 1970-01-01
      • 2020-11-24
      • 1970-01-01
      • 1970-01-01
      • 2022-10-22
      相关资源
      最近更新 更多