Ранее в этой серии постов:
...18.
PowerShell: улучшаю преобразование данных из XML-RPC в хеш-таблицу19.
PowerShell: улучшаю преобразование данных из хеш-таблицы в XML-RPC20.
PowerShell и ЖЖ: аутентификация с «cookie», интерфейс «XML-RPC» Окружение: операционная система «Windows 10», программа-оболочка «PowerShell» версии 7.
Ранее я
написал для преобразования многострочной строки (multiline string), которую я получаю от программы-сервера ЖЖ («Живого Журнала») по интерфейсу «flat», в хеш-таблицу следующую функцию:
function toHashTable($str) {
$arr = $str -split '\r?\n'
$hash = @{}
for ($i = 0; $i -lt $arr.Length; $i += 2) {
$hash[$arr[$i]] = $arr[$i + 1]
}
return $hash
}
Описание проблемы
Конечно, эта функция, как и бо́льшая часть любого кода, неидеальна. Я писал эту функцию с расчетом на то, что разработчики программы-сервера ЖЖ каждый раз будут присылать данные, которые полностью соответствуют
заявленному ими в документации формату. То есть число линий в многострочной строке должно быть чётным. Пример такой многострочной строки из 6 линий:
$content = @"
ключ1
значение1
ключ2
значение2
ключ3
значение3
"@
На практике выяснилось, что возвращаемые в этом формате данные могут заканчиваться дополнительным символом новой строки, из-за чего общее число линий в многострочной строке получается нечетным. Например:
$content = @"
ключ1
значение1
ключ2
значение2
ключ3
значение3
"@
В показанной выше многострочной строке получилось 7 линий из-за символа новой строки в конце, после значения значение3. Наша функция toHashTable в этом случае у меня выдает следующий результат:
PS C:\> $ht = toHashTable $content
PS C:\> $ht
Name Value
---- -----
ключ1 значение1
ключ2 значение2
ключ3 значение3
PS C:\> $ht.Count
4
Обратите внимание, что при выводе полученной хеш-таблицы в окно программы-оболочки «PowerShell» выведено четыре пары «ключ-значение». Четвертая пара не видна, так как во входящих данных ключ и значение этой пары представляют собой пустые значения. Но видно, что эта пара тоже выведена в окно, так как после пары ключ3 значение3 видно большое пустое место, состоящее из двух пустых строк (на одной выведена четвертая пара «ключ-значение», а следующая за ней строка отделяет вывод хеш-таблицы от следующей команды в командной строке).
Напомню, хеш-таблица по умолчанию не хранит порядок входящих в нее пар «ключ-значение». Поэтому пара с пустыми ключом и значением может в вышеприведенном выводе оказаться не на последнем месте, а в середине вывода хеш-таблицы или в начале, то есть в любой позиции.
Раньше эта лишняя пара «ключ-значение» мне не мешала, так как я обращался к значениям параметров по их названиям (ключам). В дальнейших моих экспериментах с ЖЖ приобретает значение количество полученных параметров, поэтому лишняя пара «ключ-значение» теперь становится неприемлемой.
Почему этот код вообще работает?
Если посмотреть на код функции toHashTable внимательно, то можно заметить, что в строке
$hash[$arr[$i]] = $arr[$i + 1]
при нечетном количестве элементов в массиве происходит обращение к несуществующему элементу. В нашем примере в многострочной строке 7 линий, и для четвертой пары «ключ-значение» есть пустой ключ, но нет 8 линии со значением для этого ключа.
Дело тут в том, что по умолчанию в программе-оболочке «PowerShell» включен щадящий по отношению к некоторым ошибкам режим. Например, в случае обращения к несуществующему элементу массива (наш случай) будет возвращено значение $null, а ошибка не будет выдана. В программе-оболочке «PowerShell» есть командлет «
Set-StrictMode», с помощью которого можно включить строгий по отношению к ошибкам режим. После этого сообщение об ошибке будет выдано. Пример:
PS C:\> Set-StrictMode -Version Latest
PS C:\> $ht = toHashTable $content
OperationStopped:
Line |
5 | $hash[$arr[$i]] = $arr[$i + 1]
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Index was outside the bounds of the array.
PS C:\> Set-StrictMode -Off
PS C:\> $ht = toHashTable $content
PS C:\> $ht.Count
4
В блоке кода выше видно, что я сначала включил «строгий режим», попытался запустить функцию toHashTable и получил ошибку. После этого я выключил «строгий режим» (как и было по умолчанию) и снова запустил функцию toHashTable. В этот раз функция отработала без ошибок.
Как исправить
Я решил, что если функция toHashTable получает на вход многострочную строку с нечетным числом линий, то она должна игнорировать последнюю линию, обработав только четное количество линий в многострочной строке. Вот какая исправленная версия функции toHashTable у меня получилась (я отметил изменения зеленым цветом):
function toHashTable($str) {
$arr = $str -split '\r?\n'
$hash = @{}
$len = if ($arr.Length % 2) { $arr.Length - 1 } else { $arr.Length }
for ($i = 0; $i -lt $len; $i += 2) {
$hash[$arr[$i]] = $arr[$i + 1]
}
return $hash
}
Оператор % возвращает остаток от целочисленного деления двух чисел. В случае целочисленного деления любого положительного целого числа на 2 в остатке получается число 1, если делимое является нечетным, и число 0, если делимое является четным (или нулем). Это известная формула определения четности/нечетности целого положительного числа.
Я проверил, теперь для многострочной строки $content в обоих показанных выше вариантах (с 6 линиями и с 7 линиями) обновленная функция toHashTable выдаст правильный (нужный нам) результат:
PS C:\> $ht = toHashTable $content
PS C:\> $ht
Name Value
---- -----
ключ2 значение2
ключ3 значение3
ключ1 значение1
PS C:\> $ht.Count
3
Обновленный вариант функции toHashTable будет работать без ошибок и при включении «строгого режима» с помощью командлета «Set-StrictMode».