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.