#html #cpp #регулярные_выражения #парсер
К примеру, у нас есть следующий html-код:
Hello!
Block Content
Text
Content
Как можно с помощью регулярных выражений получить содержание оперделенного тэга,
указав его имя в этом выражении. Имена тэгов в файле зарание известны и могут передваться
в качестве строки или параметра функции, которая и будет осуществлять выборку.
К примеру, если я хочу получить содержимое тэга html, то результат выполнения регулярки
должен быть следующим:
Hello!
Block Content
Text
Content
Для тэга body результат должен быть таким:
Block Content
Text
Content
и тд.
Я попробовал решить эту задачу, написав следующее регулярное выражение:
([\w\s]*)<\/html>
Однако, я не получил необходимый результат.
Далее, попробовал еще один способ:
<.+>\s*(.+)\s*<\/.+>
В целом, результат получился почти необходимым, однако, если делать выборку содержимого
тэга html, то результат будет таким:
Hello!
Block Content
Text
Content
т.е. нет начального тэга .
EDIT
Реализация второго способа выглядит вот так:
std::regex m_RegexValue("<.+>\\s*(.+)\\s*<\\/.+>")
std::vector result {};
try
{
std::sregex_iterator next(userData.begin(), userData.end(), m_RegexValue);
std::sregex_iterator end;
while (next != end)
{
result.push_back(std::smatch(*next).str());
++next;
}
}
catch (const std::regex_error& e)
{
std::cout << e.what() << std::endl;
}
Как можно это исправить и получить желаемый результат, т.е. чтобы по имени тэга получить
все его содержимое?
UPDATE
После того, как принял советы, которые мне здесь подсказали, возникла потребность
выбрать не всё содержимое родительского тега, а лишь чистый текст/контент, без остальных
дочерних тегов.
Т.е. к примеру, есть следующее содержимое html-страницы:
Hello!
Some content/data/text
Block Content
Text
Content
Как можно выбрать только Some content/data/text?
Данная проблема возникла потому, что в некоторых случаях нужно выбирать абсолютно
все содержимое родительского тега, а в других - только чистый текст/контент родительского
тега.
Ответы
Ответ 1
Правильно в таких случаях использовать специальные парсеры DOM. Я приведу пример регулярного выражения, который может не всегда сработать, например: если в атрибутах есть < или > если требуется найти соответсвтующий закрывающий тег для открывающего (т.е. без поддержки вложенных тегов. Держа вышесказанное в уме, посмотрите на m_RegexValue("<([a-zA-Z_][\\w.-]*)[^>]*>\\s*([\\w\\W]*?)\\s*\\1>") Шаблон ([a-zA-Z_][\w.-]*)[^>]*>\s*([\w\W]*?)\s*\1> находит: ([a-zA-Z_][\w.-]*) - Захватывающая подмаска №1 (техническая, нужна для того, чтобы потом использовать обратную ссылку на значение, захваченное этой группой): ASCII-буква или _, за которой может следовать 0 и более букв, цифр, _, . и - [^>]* - 0 и более символов, отличных от > >\s* - > и 0 и более пробельных символов ([\w\W]*?) - Захватывающая подмаска №2: 0 и более любых символов, как можно меньше \s*\1> - 0 и более пробельных символов, а затем , обратная ссылка на значение первой захватывающей группы и >. Для нахожения определённых тегов, можно указывать их внутри первых скобок, а после добавлять границу слова. Пример для body: <(body)\b[^>]*>\s*([\w\W]*?)\s*\1> ^^^^ ^^ См. онлайн-демо этого выражения. Однако использовать эту регулярку не рекомендуется, используйте соответствующий парсер. Сссылки на разные HTML-парсеры для С++ можно найти в этом ответе на английском SO.Ответ 2
Решение задачи можно получить проще если использовать SAX/DOM XML-парсеры. Например, в Qt есть замечательный метод QDomElement::elementsByTagName(...), который возвращает список всех дочерних элементов с указанным именем, а QDomElement::toString() возвращает содержимое элемента в качестве строки. Это все что нужно для того, что бы решить Вашу задачу. Конечно, никто не заставляет использовать Qt, и Вы можете применить здесь любой другой XML-парсер.Ответ 3
Для подобного разложения используются лексеры и парсеры. Пример сочетания - flex+bison. Хорошая статья , описывающая работу лексера и парсера вместе. P. S. В лексере, для улавливания морфем используются регулярные выражения.
Комментариев нет:
Отправить комментарий