PowerShell: улучшаю преобразование данных от ЖЖ (интерфейс «flat») в хеш-таблицу

Feb 21, 2023 05:28

Ранее в этой серии постов:
...
18. PowerShell: улучшаю преобразование данных из XML-RPC в хеш-таблицу
19. PowerShell: улучшаю преобразование данных из хеш-таблицы в XML-RPC
20. 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».

Инструмент, Образование, Программирование, Английский язык, ЖЖ

Previous post Next post
Up