Продолжаю писать скрипт для анализа текстового файла с кодом на языке HTML. Скрипт должен работать в программе-оболочке «Windows PowerShell» версии 5.1 (и в «PowerShell» версии 7) в операционной системе «Windows 10». Ранее я вывел в консоль схему HTML-дерева из текстового файла с кодом на языке HTML (в кодировке UTF-8 без метки BOM). После этого я решил вывести на схеме HTML-дерева названия классов CSS для HTML-элементов, у которых есть атрибут class. Также я решил, что эти названия должны быть подсвечены, потому что в данный момент моя цель — анализ именно названий классов CSS.   Библиотека «HTML Agility Pack», которую я использую, дает возможность запросить названия классов CSS для каждого узла HTML-дерева с помощью метода GetClasses. Подсветить названия классов можно с помощью управляющих кодов ANSI (я буду подсвечивать названия классов, меняя цвет текста). Меняю код обработки узла при обходе HTML-дерева: Я добавил переменную $classes и ее обработку. От названия узла (HTML-элемента) названия классов отделяются точкой. С помощью одного из управляющих кодов ANSI названия классов подсвечиваются желтым цветом, взятым из слота «Желтый» палитры текущей цветовой схемы, выбранной пользователем эмулятора терминала. Управляющий символ определен выражением [char]27, а не выражением `e или `u{1B}, потому что последние появились в программе-оболочке «PowerShell» с версии 6, а в программе-оболочке «Windows PowerShell» версии 5.1 не работают. Такая подсветка цветом с помощью управляющих кодов ANSI работает как в программе-«эмуляторе терминала» «Windows Terminal», так и в программе-«эмуляторе терминала» «Windows Console». Я пока убрал узлы-комментарии и текстовые узлы из вывода, так как сейчас меня интересуют HTML-элементы и классы CSS, привязанные к этим HTML-элементам. Сначала я думал как-то разбирать строку, полученную из метода GetClasses узла. А потом я подумал, что этот метод не просто так, наверное, называется «получить классы» в переводе на русский. Так и оказалось: этот метод возвращает не строку, а коллекцию классов, которую можно перебрать, например, в цикле foreach. Почему значение атрибута class HTML-элемента требует разбора? Хоть атрибут называется просто «класс» в переводе на русский, он может содержать не одно название класса CSS, а ряд названий классов CSS, разделенных пробельными символами. В пробельные символы, кстати, входит не только пробел, но и горизонтальная табуляция, и символы новой строки. Из-за этого названия классов можно не только писать одной строкой, разделяя пробелом разные названия, а располагать названия классов в столбик, для лучшей читаемости. Например, вот названия классов CSS перечислены в одной строке и разделены пробелом в коде на языке HTML: А ниже названия классов CSS перечислены в нескольких строках и разделены пробелами, символами горизонтальной табуляции и символами новой строки (это тоже код на языке HTML): В вышеприведенном коде перед названием класса class-name2 введены три символа горизонтальной табуляции, а перед названием класса class-name3 — 12 символов пробела. В хорошем редакторе кода должна быть возможность настройки ввода отступов — символами горизонтальной табуляции или символами пробела. В последнем случае при нажатии клавиши «Tab» на клавиатуре вводится не символ горизонтальной табуляции, а пробелы, количество которых тоже может быть настроено (обычно это 2 или 4 пробела). Кстати, для публикации кода в интернете я предпочитаю вводить отступы в коде только пробелами. Символы горизонтальной табуляции на разных сайтах отображаются по-разному. У меня в редакторе «Visual Studio Code» в вышеприведенном коде названия классов отображаются в ровный столбик, так как три символа табуляции (каждый шириной в 4 пробела) по ширине соответствуют 12 символам пробела. А при публикации этого кода на Хабре (в новом редакторе) название класса class-name2 оказалось сдвинуто влево. Как я понимаю, из-за того, что Хабр придает символам горизонтальной табуляции ширину в два символа пробела. (После окончательной публикации статьи названия классов в столбце таки выравниваются.) При публикации кода, к примеру, на «GitHub» символ горизонтальной табуляции представляется шириной в 8 символов пробела, из-за чего код, наоборот, оказывается сдвинутым вправо. Вернемся к нашей теме. Итак, к приведенному выше в этой статье коду добавим следующее: Я подготовил файл с кодом на языке HTML для тестирования своего скрипта: В этом файле есть четыре блока div с атрибутом class. Они содержат разные варианты ввода названий классов CSS для блока. Этот файл успешно прошел проверку в известном валидаторе HTML, то есть все эти варианты валидны. Метод GetClasses узла из библиотеки «HTML Agility Pack» во всех этих четырех вариантах выдаст одинаковый результат. То есть ненужные разделители в виде вышеупомянутых пробельных символов (пробелы, символы горизонтальной табуляции, символы новой строки) будут откинуты (будут заменены одним пробелом в местах, где требуется разделитель), а мы получим коллекцию «очищенных» названий классов CSS для перебора и анализа. Вот как у меня выглядит вывод в консоль для вышеуказанного тестового файла: Как известно, значение атрибута class HTML-элементов может состоять из текста (символы Юникода) с вкраплениями ссылок на символы вида &#1102; (это пример ссылки на строчную русскую букву «ю»). Ссылки на символы кроме числа (десятичный код символа в Юникоде) еще могут содержать названия символов из таблицы, приведенной в стандарте HTML. Нужно иметь в виду, что показанный выше метод GetClasses узла из библиотеки «HTML Agility Pack» не обрабатывает ссылки на символы, встречающиеся в названиях классов. Что значит «не обрабатывает»? Это значит, что он не преобразовывает ссылки на символы в символы, которые эти ссылки на символы представляют. А зачем преобразовывать ссылки на символы в символы? Приведу один пример. Предположим, в файле с кодом на языке HTML есть такой HTML-элемент: Сколько тут классов CSS? На первый взгляд кажется, что к этому HTML-элементу привязано два класса CSS: имя-класса1 и имя&#160;класса2. Метод GetClasses тоже так считает и возвращает коллекцию из двух указанных имен классов. Однако, при проверке в браузере (я использую для тестирования браузер «Microsoft Edge» на движке «Chromium» и браузер «Google Chrome») выясняется, что браузер видит здесь три класса: имя-класса1, имя и класса2. Почему? Потому что ссылка на символ &#160; представляет символ пробела, а символ пробела является одним из возможных разделителей имен классов в атрибуте class HTML-элементов согласно стандарта HTML! Я хочу, чтобы мой скрипт показал и исходную строку с именами классов, содержащую не конвертированные в символы ссылки на символы, и перебор имен классов с учетом конвертирования в символы ссылок на символы. Как это сделать? Библиотека «HTML Agility Pack» имеет в своем составе служебный класс (по-английски «utility class») HtmlAgilityPack.HtmlEntity, с помощью которого можно выполнить, в частности, конвертацию строки со ссылками на символы в строку, в которой все ссылки на символы заменены на представляемые ими символы. Это можно сделать с помощью метода DeEntitize. Данный метод работает как с числовыми ссылками на символы, так и с именованными ссылками на символы. Объект указанного класса для этого создавать не нужно. Дополним код перебора названий классов: До этого кода мы уже вывели в консоль исходное значение атрибута class HTML-элемента. Теперь просто удаляем старое значение этого атрибута и заменяем его на значение, в котором все ссылки на символы конвертированы в представляемые ими символы. После этой операции воспользуемся методом GetClasses библиотеки «HTML Agility Pack» как ни в чем не бывало. Таким образом, получается, что мы немного доработали функциональность библиотеки. Вот как работа скрипта выглядит у меня в консоли: Для работы с атрибутом class HTML-элементов в библиотеке «HTML Agility Pack» есть метод GetClasses. С его помощью можно получить значение атрибута class одной строкой, в которой убраны лишние символы-разделители, а, где это необходимо, оставлен один разделитель в виде символа пробела. Также с помощью метода GetClasses можно получить коллекцию имен классов CSS из значения атрибута class HTML-элемента. Эту коллекцию можно перебирать в цикле foreach для анализа отдельных имен классов CSS. Метод GetClasses не конвертирует ссылки на символы в представляемые ими символы. Для такой конвертации можно воспользоваться методом DeEntitize служебного класса HtmlAgilityPack.HtmlEntity.