Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
79 / 67 / 28
Регистрация: 22.04.2016
Сообщений: 384

Правильное сохранение родителя и ребенка тэга

16.07.2018, 17:20. Показов 1132. Ответов 7
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет!
Пишу свой html-парсер. Столкнулся с проблемой правильного сохранения указателя на родительский и дочерний тэг.

В общем, есть базовый абстрактный класс, который определяет интерфейс для реализации основных парсеров:
- парсер имени и контента тэга;
- парсер аттрибутов тэга;
- парсер значения аттрибутов тэга;

Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef DOMPARSER_BASEPARSER_H
#define DOMPARSER_BASEPARSER_H
 
#include <vector>
#include <string>
 
#include "DataParser.h"
 
class BaseParser
{
public:
    BaseParser(const std::string& str)
    : userData(str)
    {}
    virtual ~BaseParser() = default;
 
    virtual std::vector<DataParser> parse() = 0;
 
    void setData(const std::string& str)
    {
        userData = str;
    }
 
protected:
    std::string userData;
};
 
#endif //DOMPARSER_BASEPARSER_H

Где DataParser - класс, который хранит полураспаршенные данные для дальнейшей обработки и обмена данными между парсерами.
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef DOMPARSER_DATAPARSER_H
#define DOMPARSER_DATAPARSER_H
 
#include <string>
 
class DataParser
{
public:
    DataParser() = default;
    ~DataParser() = default;
 
    void setContentData(const std::string&, const std::string&, const std::string&);
    void setAttribute(const std::string&);
    void setAttributeValue(const std::string&);
    void setTegName(const std::string&);
 
    std::string getTegName() const;
    std::string getNotParsingAttributes() const;
    std::string getContent() const;
    std::string getAttribute() const;
    std::string getAttributeValue() const;
 
private:
    std::string m_TegName {};
    std::string m_NotParsingAttributes {};
    std::string m_Content {};
    std::string m_Attribute {};
    std::string m_AttributeValue {};
};
 
 
#endif //DOMPARSER_DATAPARSER_H

Хранимой еденицей является класс Teg, который хранит информацию о каждом тэге. Его интерфейс:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#pragma once
#include <string>
#include <vector>
 
class Teg
{
public:
    Teg(const std::string& = "");
    ~Teg();
 
    void setTegName(const std::string&);
    std::string getTegName() const;
 
    void setParent(Teg*);
    Teg* getParent() const;
 
    void setChildren(Teg*);
    std::vector<Teg*> getChildren() const;
 
    void setAttributeTeg(const std::string&);
    std::vector<std::string> getAttributeTeg() const;
 
    void setAttributeValueTeg(const std::string&);
    std::vector<std::string> getAttributeValueTeg() const;
 
    void setContent(const std::string&);
    std::string getContent() const;
 
private:
    std::string m_Name {};
    Teg* m_Parent;
    std::string m_Content {};
    std::vector<Teg*> m_Childrens {};
    std::vector<std::string> m_AttributeTeg {};
    std::vector<std::string> m_AttributeValueTeg {};
};

Далее, всемя этими парсерами управляет класс ProcessPage:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef DOMPARSER_PROCESSPAGE_H
#define DOMPARSER_PROCESSPAGE_H
 
#include <string>
#include <memory>
 
#include "BaseParser.h"
#include "PageData.h"
 
class ProcessPage
{
public:
    ProcessPage(const std::string&);
    ~ProcessPage() = default;
 
    void setWebPage(const std::string&);
    void process();
    PageData getData() const;
 
private:
    void processInputPageHelper(const std::string&);
    PageData processHelper(const std::string&, PageData&);
 
private:
    std::shared_ptr<BaseParser> m_BasePtr;
    std::string m_InputPage {};
    PageData m_PageData;
};
 
 
#endif //DOMPARSER_PROCESSPAGE_H

PageData - класс, который хранит уже распаршенные тэги из страницы.
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef DOMPARSER_PAGEDATA_H
#define DOMPARSER_PAGEDATA_H
 
#include <vector>
#include "Teg.h"
 
class PageData
{
public:
    PageData() = default;
    ~PageData() = default;
 
    void add(const Teg&);
    std::vector<Teg> getData() const;
    bool empty() const;
    bool find(const std::string&);
 
    Teg& first();
    Teg& last();
 
private:
    std::vector<Teg> m_Page;
};
 
 
#endif //DOMPARSER_PAGEDATA_H

Возвращаясь к классу ProcessPage, главными методами здесь есть process и processHelper:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void ProcessPage::process()
{
    processHelper(m_InputPage, m_PageData);
}
 
PageData ProcessPage::processHelper(const std::string& input, PageData& pageData)
{
    if (!input.empty())
    {
        m_BasePtr = std::make_shared<ContentParser>(input);
        std::vector<DataParser> tempVec = m_BasePtr->parse();
 
        for (const auto& i : tempVec)
        {
            Teg teg;
            teg.setTegName(i.getTegName());
            teg.setContent(i.getContent());
 
            m_BasePtr.reset();
            m_BasePtr = std::make_shared<AttributeParser>(i.getNotParsingAttributes());
 
            auto attributes = m_BasePtr->parse();
 
            for (const auto& attributesIter : attributes)
            {
                teg.setAttributeTeg(attributesIter.getAttribute());
            }
 
            m_BasePtr.reset();
            m_BasePtr = std::make_shared<AttibuteValueParser>(i.getNotParsingAttributes());
 
            auto attributesValue = m_BasePtr->parse();
 
            for (const auto& attributesValueIter : attributesValue)
            {
                teg.setAttributeValueTeg(attributesValueIter.getAttributeValue());
            }
 
            if (!pageData.empty())
            {
                pageData.last().setChildren(&teg);
                teg.setParent(&pageData.last());
            }
 
            pageData.add(teg);
 
            processHelper(i.getContent(), pageData);
        }
    }
    return pageData;
}

В этом коде определение родителей и детей тэга происходит в этом участке кода:
C++
1
2
3
4
5
 if (!pageData.empty())
 {
      pageData.last().setChildren(&teg);
      teg.setParent(&pageData.last());
 }
Но данный способ:
1. Определяет родителей и детей тэга не правильно;
2. После того, как метод processHelper завершает свою работу, данные в m_PageData, где элементами являются обьекты класса Teg и содержат указатели на своих родителей и наследников, указывают на уже недействительную область памяти.

Также попробовал и другой вариант, слегка модифицировав метод processHelper:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
PageData ProcessPage::processHelper(const std::string& input, PageData& pageData, Teg* tegPtr)
{
    if (!input.empty())
    {
        m_BasePtr = std::make_shared<ContentParser>(input);
        std::vector<DataParser> tempVec = m_BasePtr->parse();
 
        for (const auto& i : tempVec)
        {
            Teg teg;
            teg.setTegName(i.getTegName());
            teg.setContent(i.getContent());
 
            m_BasePtr.reset();
            m_BasePtr = std::make_shared<AttributeParser>(i.getNotParsingAttributes());
 
            auto attributes = m_BasePtr->parse();
 
            for (const auto& attributesIter : attributes)
            {
                teg.setAttributeTeg(attributesIter.getAttribute());
            }
 
            m_BasePtr.reset();
            m_BasePtr = std::make_shared<AttibuteValueParser>(i.getNotParsingAttributes());
 
            auto attributesValue = m_BasePtr->parse();
 
            for (const auto& attributesValueIter : attributesValue)
            {
                teg.setAttributeValueTeg(attributesValueIter.getAttributeValue());
            }
 
            //if (!pageData.empty())
            //{
            //    pageData.last().setChildren(&teg);
            //    teg.setParent(&pageData.last());
            //}
 
            if (!pageData.empty())
            {
                tegPtr->setChildren(&teg);
                tegs.push_back(tegPtr);
 
                std::cout << tegPtr->getTegName() << std::endl;
                
                for (const auto& iter : tegPtr->getChildren())
                {
                    std::cout << iter->getTegName() << std::endl;
                }
            }
            pageData.add(teg);
 
            processHelper(i.getContent(), pageData, &teg);
        }
    }
    return pageData;
}

Второй вариант уже правильно определяет детей тэга, но, если у нас есть тэг
Code
1
2
3
4
5
6
7
8
<body>
        <div class="nameCl">
            Block Content
        </div>
        <p>Text</p>
        <p>Another text</p>
        <i name="nameI" size = '2' with =6>Content</i>
</body>
То после указания первого ребенка получим:
Code
1
2
body // вывод имени текущего тэга
div // ребенок
После второго:
Code
1
2
3
body // вывод имени текущего тэга
p 
p
Третьего:
Code
1
2
3
4
body // вывод имени текущего тэга
p
p
p
И четвертого:
Code
1
2
3
4
5
body // вывод имени текущего тэга
i
i
i
i
Т.е. все элементы вектора в котором хранятся указатели на детей данного тэга имеют один адрес.

Подскажите/скиньте ссылку на материал/дайте совет, как можно правильно добавлять родителей и детей для текущего тэга?
Буду очень благодарен всем, кто откликнется!
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
16.07.2018, 17:20
Ответы с готовыми решениями:

связь родителя и ребенка
cMap -родитель #include &lt;QtGui&gt; class cMap: public QObject, public QGraphicsItem { public: void setYellow(); //лишнее...

Зависимость родителя от ребёнка.
Как сделать так, чтобы, если div-дитё увеличивался (за пределы родителя), то вместе с ним увеличивался и div-родитель?

Экземпляр класса ребенка в классе родителя
Здравствуйте, прошу помощи в организации приложения, так как в ООП относительно зеленый. Есть у меня некий спорт плагин, который выводит...

7
Диванный эксперт
Эксперт С++
 Аватар для Max Dark
2550 / 2064 / 971
Регистрация: 09.10.2013
Сообщений: 4,793
Записей в блоге: 4
16.07.2018, 21:44
Цитата Сообщение от igdev Посмотреть сообщение
указывают на уже недействительную область памяти.
C++
1
2
//Teg teg;
Tag *tag = new Tag{};
И дальше работать с указателем.
Цитата Сообщение от igdev Посмотреть сообщение
как можно правильно добавлять родителей и детей для текущего тэга?
Если я не ошибаюсь, для таких деревьев часто используют структуру вида
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct Node
{
    struct Data {}; // "полезная нагрузка"
    
    Data data;
 
    Node* nextSibling; // следующий элемент одного уровня
    Node* firstChild;  // первый дочерний элемент
};
 
/*
  root
    ^
    |
  level0 -> sibling1 -> sibling2 ... -> siblingK
    |          |           |
    |          |         child -> sibling1 -> sibling2 ... -> siblingL
    |        child
  level1 -> sibling1 -> sibling2 ... -> siblingM
    |          |           |
    |          |         child -> sibling1 -> sibling2 ... -> siblingN
    |        child
  level2 -> sibling1 -> sibling2 ... -> siblingX
  
  ..................................
*/
0
79 / 67 / 28
Регистрация: 22.04.2016
Сообщений: 384
16.07.2018, 22:02  [ТС]
Max Dark,
Цитата Сообщение от Max Dark Посмотреть сообщение
Если я не ошибаюсь, для таких деревьев часто используют структуру вида
А есть какой-то еще способ с использованием STL например? Просто мне бы хотелось использовать уже те инструменты, которые есть в составе языка, а не городить какой-то свой велосипед.
0
Диванный эксперт
Эксперт С++
 Аватар для Max Dark
2550 / 2064 / 971
Регистрация: 09.10.2013
Сообщений: 4,793
Записей в блоге: 4
16.07.2018, 22:50
Цитата Сообщение от igdev Посмотреть сообщение
А есть какой-то еще способ с использованием STL например?
Извините, нет идей.

Добавлено через 4 минуты

Не по теме:

Цитата Сообщение от igdev Посмотреть сообщение
не городить какой-то свой велосипед.
Может быть задействовать готовые библиотеки?
Разбор HTML то еще занятие...
Ведь кроме самого HTML нужно как то пропускать теги в тех же <script></script>



Добавлено через 20 минут
замечание к строчке
C++
1
m_BasePtr = std::make_shared<ContentParser>(input);
и ей подобным:
Вы подменяете поле m_BasePtr, хотя здесь нужна локальная переменная.
Да и std::shared_ptr тут лишний.
1
79 / 67 / 28
Регистрация: 22.04.2016
Сообщений: 384
16.07.2018, 23:01  [ТС]
Добавлено через 4 минуты
Max Dark,
Цитата Сообщение от Max Dark Посмотреть сообщение
Может быть задействовать готовые библиотеки?
Разбор HTML то еще занятие...
Ведь кроме самого HTML нужно как то пропускать теги в тех же <script></script>

Не по теме:

Конечно, можно. Но данный проект я делаю для того, чтобы попрактиваться в составлении архитектуры приложения, написание тестов и прочее. Так-то понятно, что есть уже готовые библиотеки, которые выполняют данную задачу. :)



Добавлено через 4 минуты
Цитата Сообщение от Max Dark Посмотреть сообщение
Да и std::shared_ptr тут лишний.
А почему лишний? Я наоборот старался использовать smart pointers для того, чтобы в случаи, если полетит исключение ресурс корректно освободился и не было потерь. Обьясните, пожалуйста.
0
Диванный эксперт
Эксперт С++
 Аватар для Max Dark
2550 / 2064 / 971
Регистрация: 09.10.2013
Сообщений: 4,793
Записей в блоге: 4
16.07.2018, 23:35
Цитата Сообщение от igdev Посмотреть сообщение
А почему лишний?
std::shared_ptr предназначен для подсчета ссылок на разделяемый ресурс.
Причем он содержит атомарный счетчик, так как ссылки могут быть в разных потоках.

У вас же выполнения в разных потоках не видно да и std::shared_ptr никуда не передаются.
Следовательно здесь подсчет ссылок не нужен, как и сам std::shared_ptr

Например, код
C++
1
2
m_BasePtr = std::make_shared<ContentParser>(input);
std::vector<DataParser> tempVec = m_BasePtr->parse();
можно упростить до
C++
1
auto tempVec = ContentParser(input).parse();
C++
1
2
 m_BasePtr = std::make_shared<AttributeParser>(i.getNotParsingAttributes());
auto attributes = m_BasePtr->parse();
Превращается в
C++
1
auto attributes = AttributeParser(i.getNotParsingAttributes()).parse();
и так далее...

Цитата Сообщение от igdev Посмотреть сообщение
ресурс корректно освободился и не было потерь.
Локальные(созданные на стеке) переменные и так освобождаются при выходе из области видимости.
Для выделенной динамически памяти можно использовать std::unique_ptr
1
 Аватар для Nishen
1358 / 856 / 366
Регистрация: 26.02.2015
Сообщений: 3,814
17.07.2018, 00:06
Чувак, не совсем по вопросу, но пишется вроде tag.
0
79 / 67 / 28
Регистрация: 22.04.2016
Сообщений: 384
17.07.2018, 09:54  [ТС]
Nishen,

Не по теме:

Я уже осознал эту ошибку и в своих исходниках уже исправил. :)


А если по вопросу: есть какие-то мысли или идеи? А то я пока в ступоре.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
17.07.2018, 09:54
Помогаю со студенческими работами здесь

При наведении на ребёнка, теряется стиль у родителя
Здравствуйте. Есть боковое меню с выпадающим меню. Есть меню : &lt;li class=&quot;mm-drop&quot;&gt; &lt;a...

Почему margin-top ребёнка сдвигает родителя - как избежать этого?
Здравствуйте, начал изучать CSS и столкнулся с такой проблемой. Есть div у которого margin-top: 50px и он сдвигает родительский блок body...

Правильное общение ребенка с родителем в фабрике
Доброе время суток. Уже неделю бьюсь с одной неприятной проблемой: в ходе реализации фабрики возникла необходимость слушать в родителе...

В чем сходство процесса-родителя и процесса-ребенка?
В чем состоит сходство процесса родителя и процесса ребенка?

При обращении к полю родителя взять правильное значение
Есть класс-родитель, в котором создается и заполняется массив. В классе-потомке узнаем размер этого массива и поэлементно проверяем,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru