Разбор файла для создания массива строк - PullRequest
0 голосов
/ 12 февраля 2019

Это кажется невероятно простым, но я что-то упустил.Мне просто нужно добавить массив в массив [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.

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Это должно делать именно то, что вы хотите:

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

}
0 голосов
/ 12 февраля 2019

tl; dr :

  • Вы не можете "увеличить" массив, присвоив несуществующий индекс ;если вы начинаете с @() - пустого массива - вы должны использовать += для "добавления" элементов (массивы являются коллекциями фиксированного размера, поэтому в действительности a новый массив должен выделяться каждый раз, когда содержит старые элементы, за которыми следует новый).

  • Поэтому использование += неэффективно в циклах , и есть две альтернативы :

    • Использование расширяемого типа списка .NET для более эффективного построения массива-подобной коллекции.

    • Предпочтительно - потому что это и удобнее, и быстрее - l et PowerShell создает массив для вас, просто захватывая выходные данные из цикла foreach в переменную
      ($array = @(foreach (...) { ... }))

Подробности ниже.


Ваш код действительно имеет проблему, хотя это признакэто будет отличаться от того, что ваш вопрос в настоящее время утверждает;используя упрощенный пример:

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

То есть @() создает пустой массив, который вы не можете неявно «расширить», обращаясь к несуществующему index .

Использование +=, как и для массива $contacts, работает:

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

Обратите внимание на использование оператора построения массива ,, чтобы гарантировать, чтоОперанд RHS добавляется в целом как одиночный новый элемент;без него было бы добавлено несколько элементов, по одному на каждый элемент.

Однако, хотя "расширение" массива с помощью += работает, в действительности вы создаете Каждый раз новый массив скрыт, потому что массивы по определению фиксированных коллекций.

В больших коллекциях это может стать проблемой производительности, и лучше использоватьвместо этого list тип данных, такой как [System.Collections.Generic.List[object]] [1] :

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

Обратите внимание, что необходимо добавить массив для добавления - как один списокэлемент - в (...), чтобы метод .Add() распознал его как один аргумент.


Шаг назад: вы можете позволить PowerShell собрать подпункт $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=@()
    }
})

$allcontacts в итоге получат обычный массив PowerShell с типом [object[]].Использование оператора подвыражения массива (@(...)) необходимо только в том случае, если необходимо убедиться, что $allcontacts является массивом, даже если файл *.vcf содержит только одно определение контакта.


[1] Неуниверсальная альтернатива - [System.Collections.ArrayList], но ее недостатком является то, что ее .Add() метод возвращает значение , требующее, чтобы вы подавили это значение, например,, $null = $arrayList.Add(...), чтобы не загрязнять выходной поток PowerShell.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...