Powershell 解析文件以创建行数组

Powershell 解析文件以创建行数组,powershell,Powershell,这看起来非常简单,但我遗漏了一些东西。我只需要在数组[0]、数组[1]等中添加一个数组。 我正在读取一个vcard文件,并尝试读取一个vcard的所有行,将它们放入一个数组中,然后将该数组放入一个数组中,这样数组[0]将是vcard 1,数组[1]将是下一个,以此类推 $c = Get-Content -Path C:\temp\Contacts_Backup.vcf $counter=0 $contact=@() $allcontacts=@() Foreach ($line in $c){

这看起来非常简单,但我遗漏了一些东西。我只需要在数组[0]、数组[1]等中添加一个数组。 我正在读取一个vcard文件,并尝试读取一个vcard的所有行,将它们放入一个数组中,然后将该数组放入一个数组中,这样数组[0]将是vcard 1,数组[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类型的对象。

tl;博士

$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=@()
    }
})
  • 不能通过分配给不存在的索引来“增长”数组;如果以
    @()
    -空数组开始,则必须使用
    +=
    来“附加”元素(数组是固定大小的集合,因此实际情况是每次必须分配一个新数组,该数组包含旧元素,后跟新元素)

  • 因此,使用
    +=
    在循环中效率很低
    ,有两种替代方法

    $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=@()
        }
    })
    
    • 使用.NET可扩展列表类型可以更高效地构建类似数组的集合

    • 最好-因为它更方便快捷-让PowerShell为您创建阵列,只需捕获变量中
      foreach
      循环的输出即可
      $array=@(foreach(…){…})

详情如下


您的代码确实存在问题,尽管它产生的症状与您的问题当前所述的不同;使用一个简化的示例:

PS> $allcontacts=@(); $allcontacts[0] = 'one', 'two'
Index was outside the bounds of the array.  # ERROR
...
也就是说,
@()
创建一个空数组,您不能通过访问不存在的索引来隐式地“扩展”该数组

使用
+=
,就像使用
$contacts
阵列一样,可以:

$allcontacts=@(); $allcontacts += , ('one', 'two')
注意使用数组构造运算符
,以确保将RHS操作数作为一个整体添加为单个新元素;如果没有它,将添加多个元素,每个元素一个

然而,当使用
+=
来“扩展”数组时,实际上每次都是在幕后创建一个新数组,因为根据定义,数组是固定大小的集合

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

请注意,需要将要添加的数组作为单个列表元素括在
(…)
中,以便
.add()
方法将其识别为单个参数


退一步:您只需捕获整个
foreach
命令的输出,就可以让PowerShell在整个
$allcontacts
数组中收集
$contact
子数组:

$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()
方法返回一个值,要求您使用例如
$null=$ArrayList.Add(…)
来抑制该值,以免污染PowerShell的输出流。

这应该完全满足您的要求:

Add-Type -AssemblyName System.Collections

[System.Collections.Generic.List[object]]$allContacts = @()
[System.Collections.Generic.List[string]]$contact = @()

$filePath  = 'C:\temp\Contacts_Backup.vcf'
$endMarker = 'END:VCARD'

foreach($line in [System.IO.File]::ReadLines($filePath))
{
        if( $line -eq $endMarker ) {
            $allContacts.Add( $contact.ToArray() )
            $contact.Clear()
        }
        else {
            $contact.Add( $line )
        }
}

# Ready. Show result.

foreach( $vcf in $allContacts ) {

    "Contact: "
    $vcf

}

通常需要在子数组前面加逗号,才能将其作为数组添加到另一个数组中。//不过,我想你最好还是制作一个VCF内容的自定义对象,并将其添加到你的主收藏中。你能发布整个错误吗。这样我就能知道你在哪一行上出错了。唯一的问题是联系人中不包括最后一行(结束:VCARD),因为它永远不会到达else子句。