Powershell 解析文件以创建行数组
这看起来非常简单,但我遗漏了一些东西。我只需要在数组[0]、数组[1]等中添加一个数组。 我正在读取一个vcard文件,并尝试读取一个vcard的所有行,将它们放入一个数组中,然后将该数组放入一个数组中,这样数组[0]将是vcard 1,数组[1]将是下一个,以此类推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){
$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子句。