MSDN.WhiteKnight - Stack Overflow answers
Ответ на "C++ парсинг тегов в std::string"
Answer 904331
Процесс парсинга, в простейшем случае, можно представить в виде трех этапов:
Разделение XML на список элементов (элементами являются теги и текстовые фрагменты).
Преобразование списка элементов в дерево узлов XML.
Выделение отдельных свойств (атрибутов) для каждого узла и разэкранирование спецсимволов.
Первый этап достаточно просто делается с помощью find_first_of и substr (ну, или регулярок, для их любителей). То, что внутри скобок - теги, то, что снаружи - текстовые фрагменты.
Второй этап осуществляется путем последовательного обхода списка элементов с сохранением всех открытых на данный момент тегов в коллекцию. По мере появления закрывающих тегов, теги достаются из коллекции и добавляются в дерево узлов. На данном этапе также осуществляется контроль корректности (если что-то отклоняется от правил структуры XML, парсер должен просто упасть).
Третий этап, аналогично первому, может использовать обычные методы обработки строк.
Вот пример кода для 1 и 2 этапов:
#include <stdio.h> #include <stdlib.h> #include <exception> #include <iostream> #include <string> #include <list> enum TokenType { TT_TAG = 1, TT_TEXT = 2 }; enum TagType { TAG_OPEN = 1, TAG_CLOSE = 2, TAG_SELFCLOSING = 3 }; //Представляет элемент XML (тэг или текстовый фрагмент) struct XmlToken { std::string text; TokenType type; }; //Представляет узел в дереве XML struct XmlNode { std::string name; //имя тега std::string properties; //свойства std::string value; //значение узла std::list<XmlNode*> children; //дочерние узлы }; //Преобразует строку XML в последовательность элементов std::list<XmlToken> XmlToTokens(const std::string& xml) { std::list<XmlToken> tokens; XmlNode result; size_t pos=0; size_t startindex, endindex; std::string token_text; XmlToken token; while (true) { startindex = xml.find_first_of('<', pos); endindex = xml.find_first_of('>', startindex); if (startindex == std::string::npos || endindex == std::string::npos) { break; } if (startindex > pos) { token.text = xml.substr(pos, startindex - pos); token.type = TT_TEXT; tokens.push_back(token); } token.text = xml.substr(startindex+1, endindex - (startindex+1)); token.type = TT_TAG; tokens.push_back(token); pos = endindex + 1; if (pos >= xml.length())break; } return tokens; } //возвращает тип тега TagType GetTagType(const XmlToken& token) { if (token.type != TT_TAG || token.text.length()==0) return (TagType)0; char first = token.text.at(0); char last = token.text.at(token.text.length() - 1); if (first == '/' && last != '/') return TAG_CLOSE; if (first != '/' && last == '/') return TAG_SELFCLOSING; if (first != '/' && last != '/') return TAG_OPEN; else return (TagType)0; } //возвращает имя тега std::string GetTagName(const XmlToken& token) { std::string res; if (token.type != TT_TAG || token.text.length() == 0) return res; size_t pos = 0; char first = token.text.at(0); if (first == '/')pos = 1; size_t index = token.text.find_first_of(" /", pos); if (index == std::string::npos) index = token.text.length(); return token.text.substr(pos, index - pos); } //возвращает свойства тега std::string GetTagProperties(const XmlToken& token) { std::string res; if (token.type != TT_TAG || token.text.length() == 0) return res; size_t pos = 0; size_t len = token.text.length(); char first = token.text.at(0); if (first == '/')pos = 1; char last = token.text.at(len-1); if (last == '/')len--; size_t index = token.text.find_first_of(" ", pos); if (index == std::string::npos) index = len - 1; return token.text.substr(index+1, len - (index + 1)); } //возвращает текст элемента без пробелов std::string GetTrimmedText(const XmlToken& token) { std::string res; if (token.type != TT_TEXT || token.text.length() == 0) return res; for (size_t i = 0; i < token.text.length(); i++) { char c = token.text.at(i); if (iswspace(c) == 0 && c!='\n' && c!='\r') res += c; } return res; } //выводит дерево XML на экран void PrintXml(const XmlNode& node, int depth = 0) { for (int i = 0; i < depth;i++) putc('-', stdout); printf("%s Properties: [%s]; Value: [%s]; Children: [%d]\n", node.name.c_str(), node.properties.c_str(),node.value.c_str(),(int)node.children.size() ); for (auto x : node.children) PrintXml(*x, depth + 1); } //********************************** int main(int argc, char **argv) { try { std::string xml = "<rectangle left=\"100\" top=\"100\" width=\"200\" height=\"200\">\ <rectangle left = \"100\" top = \"100\" width = \"200\" height = \"200\"></rectangle>\ <rectangle left = \"100\" top = \"100\" width = \"200\" height = \"200\"></rectangle>\ </rectangle>"; printf("\n****** Source XML: ****** \n"); puts(xml.c_str()); printf("*********************** \n"); //разбивка на элементы std::list<XmlToken> tokens = XmlToTokens(xml); printf("\n****** Tokens: ****** \n"); for (XmlToken& x : tokens) { printf("%s\n", x.text.c_str()); } printf("*********************** \n"); //создаем корневой узел XmlNode root; root.name = std::string("(XML root)"); root.properties = std::string(""); root.value = std::string(""); std::list<XmlNode*> currentpath; //текущий путь в иерархии XML bool between_tags = false; std::string tagname; std::string trimmed; TagType type; XmlNode* node = &root; XmlNode* new_node; //строим дерево XML... for (XmlToken& x : tokens) { switch (x.type) { case TT_TEXT: //текстовый фрагмент trimmed = GetTrimmedText(x); if (trimmed.length() == 0) { continue; } if (!between_tags) { printf("Parse error: Unexpected text outside of XML tags.\n"); throw std::exception(); } new_node = currentpath.back(); if (new_node->children.size() > 0) { printf("Parse error: Element cannot contain both text and child elements\n"); throw std::exception(); } new_node->value = x.text; //устанавливаем значение узла break; case TT_TAG: //тег type = GetTagType(x); tagname = GetTagName(x); if (tagname.length() == 0) { printf("Parse error: Tag name empty\n"); throw std::exception(); } switch (type) { case TAG_OPEN: //открывающий тег //создаем новый узел new_node = new XmlNode(); new_node->name = tagname; new_node->properties = GetTagProperties(x); new_node->value = std::string(""); //добавляем узел в текущий путь between_tags = true; currentpath.push_back(new_node); break; case TAG_CLOSE: //закрывающий тег if (currentpath.size() == 0) { printf("Parse error: Unexpected closing tag\n"); throw std::exception(); } new_node = currentpath.back(); if (tagname != new_node->name) { printf("Parse error: Closing tag does not match opening tag.\n"); throw std::exception(); } between_tags = false; currentpath.pop_back(); //находим родительский узел if (currentpath.size() > 0) node = currentpath.back(); else node = &root; //добавляем дочерний узел if (node->value.length() > 0) { printf("Parse error: Element cannot contain both text and child elements\n"); throw std::exception(); } node->children.push_back(new_node); break; case TAG_SELFCLOSING: //самозакрывающийся //создаем новый узел new_node = new XmlNode(); new_node->name = tagname; new_node->properties = GetTagProperties(x); new_node->value = std::string(""); //находим родительский узел if (currentpath.size() > 0) node = currentpath.back(); else node = &root; //добавляем дочерний узел if (node->value.length() > 0) { printf("Parse error: Element cannot contain both text and child elements\n"); throw std::exception(); } node->children.push_back(new_node); break; } break; }//end switch }//end for if (currentpath.size() > 0) { printf("Parse error: Not all tags are closed\n"); throw std::exception(); } //выводим результат printf("\n****** XML Tree: ****** \n"); PrintXml(root, 0); printf("*********************** \n"); } catch (std::exception) { printf("Parsing failed!\n"); } getchar(); return 0; } /* Вывод: ****** Source XML: ****** <rectangle left="100" top="100" width="200" height="200"><rectangle left = "100" top = "100" width = "200" height = "200"></rectangle><rectangle left = "100" top = "100" width = "200" height = "200"></rectangle></rectangle> *********************** ****** Tokens: ****** rectangle left="100" top="100" width="200" height="200" rectangle left = "100" top = "100" width = "200" height = "200" /rectangle rectangle left = "100" top = "100" width = "200" height = "200" /rectangle /rectangle *********************** ****** XML Tree: ****** (XML root) Properties: []; Value: []; Children: [1] -rectangle Properties: [left="100" top="100" width="200" height="200"]; Value: []; Children: [2] --rectangle Properties: [left = "100" top = "100" width = "200" height = "200"]; Value: []; Children: [0] --rectangle Properties: [left = "100" top = "100" width = "200" height = "200"]; Value: []; Children: [0] *********************** */
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.