Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
863 views
in Technique[技术] by (71.8m points)

powershell - Parsing a file to create an array of lines

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:empContacts_Backup.vcf
$counter=0
$contact=@()
$allcontacts=@()

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

Result: Unable to index into an object of type System.String.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).

  • Using += is therefore inefficient in loops, and there are two alternatives:

    • Use a .NET extensible list type to build an array-like collection more efficiently.

    • 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 (...) { ... }))

Details below.


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.

Using +=, as you do with your $contacts array, does work:

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

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.

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'))

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.


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:empContacts_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 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] 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...