解析文件以创建行数组

时间:2019-02-12 03:22:14

标签: powershell

这似乎非常简单,但我缺少一些东西。我只需要向array [0],array [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类型的对象。

2 个答案:

答案 0 :(得分:1)

tl;博士

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

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

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

    • 最好-因为它既方便又快捷-l PowerShell 为您创建数组,只需通过捕获foreach循环中的输出即可变量
      $array = @(foreach (...) { ... })

下面的详细信息。


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

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

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

像使用+=数组那样使用$contacts确实可以:

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

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

但是,虽然可以使用+=“扩展”数组,但实际上您每次都在幕后创建一个 new 数组,因为根据定义,数组是固定的 -size 集合。

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

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

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


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

foreach

$c = Get-Content -Path C:\temp\Contacts_Backup.vcf $contact=@() $allcontacts=@() $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 $contact=@() } }) 最终将成为常规的PowerShell数组,键入$allcontacts。 仅当您需要确保[object[]]是一个数组时,才需要使用array-subexpression运算符(@(...)),即使$allcontacts文件仅包含一个联系人定义。


[1]一种非通用的替代方法是*.vcf,但缺点是其[System.Collections.ArrayList]方法返回一个值,需要您禁止该值与.Add()一起使用,以免污染PowerShell的输出流。

答案 1 :(得分:1)

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

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

}