解析文件以创建行数组 [英] Parsing a file to create an array of lines
问题描述
这看起来非常简单,但我遗漏了一些东西.我只需要向数组[0]、数组[1]等添加一个数组.我正在获取一个 vcard 文件并尝试读取一个 vcard 的所有行并将它们放入一个数组中,然后将该数组放入一个数组中,这样数组 [0] 将是 vcard 1,数组 [1] 将是下一个,等等.
This seems so incredibly simple but I am missing something. I just need to add an array to array[0], array[1], etc. I am taking a vcard file and trying to read all the lines of one vcard and put them in an array and then place that array in an array so array[0] will be vcard 1, array[1] will be the next, etc.
$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 类型的对象.
Result: Unable to index into an object of type System.String.
推荐答案
tl;dr:
你不能通过分配给一个不存在的索引来增长"一个数组;如果你以
@()
开头 - 一个空数组 - 你必须使用+=
来追加"元素(数组是固定大小的集合,所以真正发生的是每次都必须分配一个新数组,其中包含旧元素,然后是新元素).
You cannot "grow" an array by assigning to a nonexistent index; if you start with
@()
- an empty array - you must use+=
to "append" elements (arrays are fixed-size collections, so what really happens is that a new array must be allocated every time that contains the old elements followed by the new one).
因此在循环中使用 +=
效率低下,并且有两种替代方法:
使用 .NET 可扩展列表类型更有效地构建类似数组的集合.
Use a .NET extensible list type to build an array-like collection more efficiently.
最好 - 因为它既方便又快捷 - 让 PowerShell 为您创建数组,只需从 foreach
捕获输出在变量中循环
($array = @(foreach (...) { ... })
)
Preferably - because it is both more convenient and faster - let PowerShell create the array for you, simply by capturing the output from a foreach
loop in a variable
($array = @(foreach (...) { ... })
)
详情如下.
您的代码确实有问题,尽管它产生的症状与您的问题当前陈述的不同;使用一个简化的例子:
Your code indeed has a problem, though the symptom it would produce differs from what your question currently states; using a simplified example:
PS> $allcontacts=@(); $allcontacts[0] = 'one', 'two'
Index was outside the bounds of the array. # ERROR
...
也就是说,@()
创建一个 空 数组,你不能通过访问一个不存在的索引来隐式地扩展"它.
That is, @()
creates an empty array, which you cannot implicitly "extend" by accessing a non-existent index.
使用 +=
,就像使用 $contacts
数组一样,确实有效:
Using +=
, as you do with your $contacts
array, does work:
$allcontacts=@(); $allcontacts += , ('one', 'two')
注意数组构造运算符,
的使用,以确保RHS操作数作为一个整体添加为一个单个新元素;没有它,将添加多个元素,每个元素一个.
Note the use of array-construction operator ,
to ensure that the RHS operand is added as a whole as a single new element; without it, multiple elements would be added, one for each element.
然而,虽然使用 +=
来扩展"一个数组是有效的,但实际上你每次都在幕后创建一个 新 数组,因为数组是根据定义固定大小集合.
However, while "extending" an array with +=
works, in reality you're creating a new array behind the scenes every time, because arrays are by definition fixed-size collections.
对于较大的集合,这可能会成为性能问题,最好使用 list 数据类型代替,例如 [System.Collections.Generic.List[object]]
[1]:
With larger collections, this can become a performance issue, and it is better to use a list data type instead, such as [System.Collections.Generic.List[object]]
[1]:
$allcontacts = New-Object Collections.Generic.List[object]
$allcontacts.Add(('one', 'two'))
注意需要将要添加的数组 - 作为单个列表元素 - 在 (...)
中,以便 .Add()
方法将其识别为一个参数.
Note the need to enclose the array to add - as a single list element - in (...)
so that the .Add()
method recognizes it as a single argument.
退一步:您可以让 PowerShell 收集整个 $allcontacts
数组中的 $contact
子数组简单地捕获整个 foreach
命令的输出:
Taking a step back: You can let PowerShell collect the $contact
sub-arrays in the overall $allcontacts
array by simply capturing the output from the entire foreach
command:
$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
文件仅包含 一个 联系人定义.
$allcontacts
will end up as a regular PowerShell array, typed [object[]]
.
Use of the array-subexpression operator (@(...)
) is only necessary if you need to ensure that $allcontacts
is an array even if the *.vcf
file contains only one contact definition.
[1] 一个非通用的替代方案是 [System.Collections.ArrayList]
,但它的缺点是它的 .Add()
方法 返回一个值,要求您使用例如 $null = $arrayList.Add(...)
来抑制该值,以免污染 PowerShell 的输出流.
[1] A non-generic alternative is [System.Collections.ArrayList]
, but its downside is that its .Add()
method returns a value, requiring you to suppress that value with, e.g., $null = $arrayList.Add(...)
so as not to pollute PowerShell's output stream.
这篇关于解析文件以创建行数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!