首页 > 解决方案 > 解析文件以创建行数组

问题描述

这看起来非常简单,但我错过了一些东西。我只需要向数组 [0]、数组 [1] 等添加一个数组。我正在获取一个 vcard 文件并尝试读取一个 vcard 的所有行并将它们放入一个数组中,然后将该数组放入一个数组中所以 array[0] 将是 vcard 1,array[1] 将是下一个,等等。

$c = Get-Content -Path C:\temp\Contacts_Backup.vcf
$counter=0
$contact=@()
$allcontacts=@()

Foreach ($line in $c){
    $contact += $line
    if ($line -eq 'END:VCARD'){
        $allcontacts[$counter++] = $contact
        $contact=@()
        }
}

结果:无法索引到 System.String 类型的对象。

标签: powershell

解决方案


tl;博士

  • 您不能通过分配给不存在的索引来“增长”数组;如果你从@()- 一个空数组开始 - 你必须使用+=“附加”元素(数组是固定大小的集合,所以真正发生的是每次都必须分配一个数组,其中包含旧元素,然后是新元素) .

  • +=因此在循环中使用是低效的,有两种选择

    • 使用 .NET 可扩展列表类型更有效地构建类似数组的集合。

    • 最好 - 因为它更方便和更快 -PowerShell为您创建数组,只需捕获foreach
      变量 ( $array = @(foreach (...) { ... }))中循环的输出

详情如下。


您的代码确实有问题,尽管它会产生的症状与您当前的问题不同;使用一个简化的例子:

PS> $allcontacts=@(); $allcontacts[0] = 'one', 'two'
Index was outside the bounds of the array.  # ERROR
...

也就是说,@()创建一个数组,您不能通过访问不存在的index来隐式地“扩展”它。

使用+=,就像你对你的$contacts数组所做的那样,确实有效:

$allcontacts=@(); $allcontacts += , ('one', 'two')

请注意使用数组构造运算符,来确保将 RHS 操作数作为一个整体添加为单个新元素;没有它,将添加多个元素,每个元素一个。

然而,虽然用作品“扩展”了一个数组,但+=实际上你每次都是在幕后创建一个新数组,因为数组根据定义是固定大小的集合。

对于较大的集合,这可能会成为性能问题,最好使用列表数据类型,例如[System.Collections.Generic.List[object]][1]

$allcontacts = New-Object Collections.Generic.List[object]
$allcontacts.Add(('one', 'two'))

请注意,需要将要添加的数组作为单个列表元素括起来,(...)以便该.Add()方法将其识别为单个参数。


退后一步:您可以让PowerShell通过简单地捕获整个命令的输出来收集$contact整个数组中的子数组$allcontactsforeach

$c = Get-Content -Path C:\temp\Contacts_Backup.vcf
$contact=@()

$allcontacts = @(foreach ($line in $c){
    $contact += $line
    if ($line -eq 'END:VCARD'){
        # Output the $contact array as a *single* object,
        # using ",", the array-construction operator
        , $contact
        # Reset for the next contact.
        $contact=@()
    }
})

$allcontacts最终将成为一个常规的 PowerShell 数组,类型为[object[]]. @(...)仅当您需要确保它$allcontacts是一个数组(即使*.vcf文件仅包含一个联系人定义)时,才需要使用数组子表达式运算符 ( )。


[1] 一个非通用的替代方法是[System.Collections.ArrayList],但它的缺点是它的.Add()方法返回一个 value,要求您使用例如抑制该值,$null = $arrayList.Add(...)以免污染 PowerShell 的输出流。


推荐阅读