gpt4 book ai didi

powershell - 使用 Powershell 创建/填充 csv 文件

转载 作者:行者123 更新时间:2023-12-02 22:26:08 25 4
gpt4 key购买 nike

我在使用 powershell 创建/填充 csv 文件时遇到了一些麻烦。我是 powershell 的新手,所以我可能会遗漏一些明显的东西,所以请放轻松。这是情况:

首先我创建一个数组(?)作为我的表

#Create output table with headers
$output = @()
$row = New-Object System.Object
$row | Add-Member -MemberType NoteProperty -Name "Example Header 1" -Value $null
$row | Add-Member -MemberType NoteProperty -Name "Example Header 2" -Value $null
$row | Add-Member -MemberType NoteProperty -Name "Example Header 3" -Value $null
$output += $row

我正在使用 $output | Export-Csv new.csv -NoTypeInformation
这似乎制作了一个带有我想要的标题的 csv 文件。如果有更好的方法来做到这一点,请告诉我。下一步是我遇到问题的地方。我现在需要以编程方式用数据填充表。导入现有的 csv 文件时,我可以像数组一样访问/修改表中的数据(即 $output[rowIndex]."Header Name" = "new data" )。

所以我尝试将数据添加到我新创建的表中。我写了 $ouput[0]."Example Header 1" = "Test Data" .这按我的预期工作,并使用带有“测试数据”的指定标题填充列中的第一行。但是,我只能访问 [0]。 $output[1]等等会导致错误,因为我猜它们不存在。我尝试使用 $output += $row再次添加更多行,但它根本不起作用并导致发生一些奇怪的错误(如果我写入一行,它会写入所有行,可能是因为它们都是相同的对象)。

所以基本上我的问题是, 如何从头开始创建一个 csv 文件,向其中添加一些标题,然后开始写入所有(未知/可变数量)行 ?我确信有更好的方法来做到这一点,但就像我说的,我对 powershell 很陌生。理想情况下,我希望能够通过索引(0、1、2 等)访问行,但我对任何事情都持开放态度。

基本解决方案 (改编自 Martin Brandl's answer)

这基本上是从一个 csv 文件中读取数据,并将其插入到另一个具有新指定标题的文件中。
$csv = Import-Csv "MyCsv.csv"
$newCsv = @()
foreach($row in $csv) {
$newCsv += [PSCustomObject]@{
"New Column Header1" = $row."Original Column Header1"
"New Column Header2" = $row."Original Column Header2"
}
}

最佳答案

补充Martin Brandl's helpful answer带有 症状说明 (强调):

I tried using $output += $row again to add more rows, but it does not work at all and causes some strange errors to happen (if I write to a row, it writes to all rows, probably because it's all the same object).


事实上,这就是发生的事情:在 .NET 术语中,类型 (class) [pscustomobject]是引用类型而不是值类型 - 正如 [pscustomobject].IsValueType 所证明的那样返回 $false .
如果您 添加引用类型的给定实例(对象)到一个数组 多次,所有这些元素都指向同一个实例 .
这是简短的演示。
$obj = [PSCustomObject] @{
'Example Header 1' = $null
'Example Header 2' = $null
}

$array = @()
foreach ($ndx in 1..2) {
# By working with the original $obj every time, you
# keep modifying the same instance's property values.
$obj.'Example Header 1' = "h1-$ndx"
$obj.'Example Header 2' = "h2-$ndx"
# Adding $obj to an array does NOT create a COPY of $obj
# but stores a REFERENCE directly to $obj in the array
# (similar to storing a pointer in unmanaged languages such as C++).
$array += $obj
}

# Output the array.
$array
这产生以下结果:
Example Header 1 Example Header 2
---------------- ----------------
h1-2 h2-2
h1-2 h2-2
如您所见,只有分配给 .Example Header 1 的最后一个值和 .Example Header 2生效,因为两个数组元素都引用了同一个对象。

基于类的解决方案
Martin的方法是解决这个问题的最简单的方法 : 在每次迭代中创建自定义对象的新实例 (将哈希表文字转换为 [pscustomobject] ,如问题本身所示: $array += [pscustomobject] @{ ... } )。
如果您不想或无法在循环内从头开始重新创建实例,您有两个基本选择:
  • 在每次循环迭代中克隆一个模板自定义对象或简单地使用 [pscustomobject] @{ ... }在循环内创建对象,每次都会隐式创建一个新实例 :
  • this answer

  • PSv5+ 替代方案:定义一个自定义类并在每次循环迭代中实例化它 - 见下文。

  • 在 PSv5+ 中,一个 custom class允许 优雅的解决方案 那也是 表现更好 而不是使用文字语法在循环中创建实例。
    # Define a custom class that represents the rows of the
    # output CSV.
    # Note: [object] is being used here as the properties' type.
    # In real life, you'd use more specific types such as [string]
    # or [int].
    class CsvRow {
    [object] ${Example Header 1}
    [object] ${Example Header 2}
    }

    $array = @()
    foreach ($ndx in 1..2) {
    # Instantiate the custom class.
    $rowObj = [CsvRow]::new()
    # Set the values.
    $rowObj.'Example Header 1' = "h1-$ndx"
    $rowObj.'Example Header 2' = "h2-$ndx"
    # Add the instance to the array.
    $array += $rowObj
    }

    # Output the array.
    $array

    性能注意事项
    两个因素决定性能:
  • 数组在每次循环迭代中扩展的速度:
  • 使用 $array += ... 逐个扩展数组元素很方便,但是速度慢,效率低 ,因为每次都必须创建一个新的数组(数组是固定大小的集合,不能直接扩展)。
  • 对于可能无关紧要的小迭代计数,但数字越大,性能受到的影响就越大,并且在某些时候这种方法变得不可行。
  • 下一个最佳解决方案是使用 [System.Collections.Generic.List[object]]代替构建数组的实例 - 此类列表旨在有效扩展。
  • 然而,最好和最简单的解决方案是简单地让 PowerShell 从数组中的类似循环的语句中收集多个输出,只需分配给变量 。 - 见下文。

  • 在每次循环迭代中实例化新对象的速度:
  • 实例化自定义类的实例比通过哈希表文字创建实例更快,但前提是 [CsvRow]::new()用于实例化 ;功能等效 New-Object CsvRow由于涉及 cmdlet 调用 ,速度要慢得多.


  • 的以下变体自定义类解决方案使用隐式数组创建来确保可接受的性能,即使迭代计数更高 :
    # Define the custom class.
    class CsvRow {
    [object] ${Example Header 1}
    [object] ${Example Header 2}
    }

    # Determine the iteration count.
    $count = 1000

    # Loop and let PowerShell collect the outputs
    # from all iterations implicitly in variable $array
    [array] $array = foreach ($ndx in 1..$count) {
    # Instantiate the custom class.
    $rowObj = [CsvRow]::new()
    # Set the values.
    $rowObj.'Example Header 1' = "h1-$ndx"
    $rowObj.'Example Header 2' = "h2-$ndx"
    # Simply output the row object
    $rowObj
    }

    # Output the array.
    $array
    注意: [array]仅当您需要确保 $ToWrite 时才需要类型约束总是一个数组;没有它,如果碰巧只有一个循环迭代并因此输出对象, $ToWrite将按原样存储该输出对象,而不是包装在数组中(此行为是 PowerShell 管道的基础)。

    关于powershell - 使用 Powershell 创建/填充 csv 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44113740/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com