如同我以前的一篇文章說的那樣,我沒有支持DTD與命名空間,css
當前實現出來的解析器,只能與xmlhttp對比,由於chrome瀏覽器解析大文檔有bug,至於其餘人實現的,我就不一一測試了,既然都決定本身實現了,我只選擇大公司的代碼作對比。node
測試文檔大小:3M bytes,約90000個節點。linux
aqx::xdoc :耗時 70-80ms,內存佔用30-40M bytes,30和40主要是32位和64位的區別,若是要追求最少的內存佔用,還能夠更極端一些,解析速度很難再有本質的提高了,後續要完善的支持,也不會影響解析速度。ios
xmlhttp: 耗時3000-4000 ms,內存佔用約 800M bytes。c++
目前測試過的系統有:chrome
windows vc++ (vs2017) x86 x64windows
linux centos7 g++ version(9.1.1) x86 x64centos
windows中支持3種編碼格式:utf-8 utf-16 對應操做系統的ascii編碼,在簡體中文windows中,也就是一般咱們說的gb2312了。瀏覽器
而後目前,我不太可能針對實現細節將原理講清楚,講真的,C++的可讀性真的很是糟糕,但對於這種需求,仍是得用它,這種代碼,我本身寫完看着不難受,但對別人來講極可能是噩夢,一樣的道理,我看別人的C++代碼,也會困惑,要讓看不懂代碼的人,也理解實現細節,這是很是不科學的事。。。數據結構
好了,廢話不說了,上代碼:
//xml.hpp #pragma once #include <setjmp.h> #include <set> #include <list> #include <map> #include <algorithm> #include <string> #include <fstream> #include <string.h> #if defined(_WIN32) || defined(_WIN64) //我只支持了windows中的編碼轉換,因此這兩個文件,僅在windows下使用。 #include "tcvt.h" #include "encode_adaptive.h" #endif #pragma warning( push ) #pragma warning( disable:4996) namespace aqx { namespace aqx_internal { #ifndef __AQX_UTF8_CHAR_LEN #define __AQX_UTF8_CHAR_LEN static unsigned char utf8_char_len[] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 }; #endif //單字節的字符狀態值,對應語法常量 static unsigned short xml_char_syntax[] = { 0,0,0,0,0,0,0,0,0,8,8,0,0,8,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 8,64,4,0,0,0,0,4,0,0,0,0,0,8208,16,128, 1552,1552,1552,1552,1552,1552,1552,1552,1552,1552,0,256,1,2048,2,0, 0,1072,1072,1072,1072,1072,1072,48,48,48,48,48,48,48,48,48, 48,48,48,48,48,48,48,48,48,48,48,4096,0,0,0,48, 0,1072,1072,1072,1072,1072,1072,48,48,48,48,48,48,48,48,48, 48,48,48,48,48,48,48,48,48,48,48,0,0,0,0,0, }; namespace XML_SYNTAX { //語法常量定義,渣英語,名稱定義湊合看吧。 static constexpr auto _X_LT{ static_cast<unsigned short>(0x01) }; // < static constexpr auto _X_GT{ static_cast<unsigned short>(0x02) }; // > static constexpr auto _X_STRING{ static_cast<unsigned short>(0x04) }; // ' " static constexpr auto _X_SPACE{ static_cast<unsigned short>(0x08) }; // \r\n\r空格 static constexpr auto _X_NAME{ static_cast<unsigned short>(0x10) }; // A-Z a-z 0-9 _ - . static constexpr auto _X_BEGINNAME{ static_cast<unsigned short>(0x20) }; // A-Z a-z _ static constexpr auto _X_EXCLAM{ static_cast<unsigned short>(0x40) }; // ! static constexpr auto _X_TAGEND{ static_cast<unsigned short>(0x80) }; // / static constexpr auto _X_ESCAPEEND{ static_cast<unsigned short>(0x100) }; // ; static constexpr auto _X_NUMBER{ static_cast<unsigned short>(0x200) }; // 數字0-9 static constexpr auto _X_HEX{ static_cast<unsigned short>(0x400) }; // 16進制0-9 A-F a-f static constexpr auto _X_EQUAL{ static_cast<unsigned short>(0x800) }; // = static constexpr auto _X_LB{ static_cast<unsigned short>(0x1000) }; // [ static constexpr auto _X_NEGATIVE{ static_cast<unsigned short>(0x2000) }; // - static constexpr auto _X_MULTIBYTE{ static_cast<unsigned short>(0x4000) }; // 多字節字符 } //保險起見,爲了將來考慮,定義一下xml文檔的最大長度,時代發展太迅猛,萬一我有生之年能用上128bit,到時候也許處理64bit長度的文檔就跟咱們如今解析小文檔同樣。 using xml_size_t = unsigned int; static constexpr auto _xnf{ static_cast<xml_size_t>(-1) }; //這個結構,用來儲存轉義符位置,以便於快速替換,備用,暫不實現,由於這關乎性能。 struct xml_escape_pos { xml_size_t pos, len; }; template<typename _XtsTy> class xparser_t; //xml文本迭代器的基本模板類 template<typename _Ty> class xts_t { public: using Basetype = _Ty; protected: const _Ty *text; xml_size_t size; xml_size_t index; _Ty c; unsigned char cl; unsigned short s; unsigned short flags; }; //解析錯誤信息結構 //解析時不處理行,列問題,有錯誤發生時後處理,由於,行,列處理,會使解析速度慢差很少一倍。 struct xerrorpos { xml_size_t pos; int number; std::string information; xml_size_t line; xml_size_t column; }; //這兩個結構用來儲存一些字符串常量,實現兩種字符串格式的快速引用,這兩個結構綁定到三種xts類中 struct xmultybyte_constvalue { static constexpr const char *br_tag = "<br/>"; static constexpr const char *crlf = "\r\n"; static constexpr const char *end_tag_syntax = "</"; static constexpr const char *autoend_tag_syntax = "/>"; static constexpr const char *comment_end = "--"; static constexpr const char *cdata_end = "]]>"; }; struct xwidechar_constvalue { static constexpr const wchar_t *br_tag = L"<br/>"; static constexpr const wchar_t *crlf = L"\r\n"; static constexpr const wchar_t *end_tag_syntax = L"</"; static constexpr const wchar_t *autoend_tag_syntax = L"/>"; static constexpr const wchar_t *comment_end = L"--"; static constexpr const wchar_t *cdata_end = L"]]>"; }; //utf8的文本迭代器,先基於這個來實現 class xts_utf8 : public xts_t<char> { public: using strtype = std::string; static constexpr int _encoding{ 2 }; using constval = xmultybyte_constvalue; //初始化 void init(const char *_Text, xml_size_t _Size) { text = _Text; size = _Size; index = 0; c = text[0]; cl = utf8_char_len[(unsigned char)c]; s = (cl != 1) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //處理下一個字符 void next() { index += cl; c = text[index]; cl = utf8_char_len[(unsigned char)c]; s = (cl != 1) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //向前回退n個字符,目前,只有在根節點以前的處理,有用到這個 void back(xml_size_t len) { index -= len; c = text[index]; cl = utf8_char_len[(unsigned char)c]; s = (cl != 1) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //next,並判斷語法 bool next_is_flags() { next(); return (flags & s) != 0; } //next, 並判斷下一個字符的值 bool next_is_char(char _Chr) { next(); return _Chr == c; } //解析錯誤時,用於獲取行,列。 void next_donot_syntax() { index += cl; c = text[index]; cl = utf8_char_len[(unsigned char)c]; } //設置容許的語法 void set_flags(unsigned short _Flags) { flags = _Flags; } private: friend class xparser_t<xts_utf8>; }; //asc的文本迭代器 class xts_asc : public xts_t<char> { public: using strtype = std::string; static constexpr int _encoding{ 0 }; using constval = xmultybyte_constvalue; //初始化 void init(const char *_Text, xml_size_t _Size) { text = _Text; size = _Size; index = 0; c = text[0]; cl = ((unsigned short)c >= 0x80) ? 2 : 1; s = (cl != 1) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //處理下一個字符 void next() { index += cl; c = text[index]; cl = ((unsigned short)c >= 0x80) ? 2 : 1; s = (cl != 1) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //向前回退n個字符,目前,只有在根節點以前的處理,有用到這個 void back(xml_size_t len) { index -= len; c = text[index]; cl = ((unsigned short)c >= 0x80) ? 2 : 1; s = (cl != 1) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //next,並判斷語法 bool next_is_flags() { next(); return (flags & s) != 0; } //next, 並判斷下一個字符的值 bool next_is_char(char _Chr) { next(); return _Chr == c; } void next_donot_syntax() { index += cl; c = text[index]; cl = ((unsigned short)c >= 0x80) ? 2 : 1; } //設置容許的語法 void set_flags(unsigned short _Flags) { flags = _Flags; } private: friend class xparser_t<xts_asc>; }; class xts_utf16 : public xts_t<wchar_t> { public: using strtype = std::wstring; static constexpr int _encoding{ 1 }; using constval = xwidechar_constvalue; xts_utf16() { //utf16的字符不是變長的,固定爲1 //雖然有4字節的utf16字符,但影響不到最終解析邏輯。 cl = 1; } private: void init(const wchar_t *_Text, xml_size_t _Size) { text = _Text; size = _Size; index = 0; c = text[0]; cl = 1; s = ((unsigned short)c >= 0x80) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //處理下一個字符 void next() { c = text[++index]; s = ((unsigned short)c >= 0x80) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } void back(xml_size_t len) { index -= len; c = text[index]; s = ((unsigned short)c >= 0x80) ? XML_SYNTAX::_X_MULTIBYTE | XML_SYNTAX::_X_BEGINNAME | XML_SYNTAX::_X_NAME : xml_char_syntax[(unsigned char)c]; } //next,並判斷語法 bool next_is_flags() { next(); return (flags & s) != 0; } //next, 並判斷下一個字符的值 bool next_is_char(char _Chr) { next(); return _Chr == c; } void next_donot_syntax() { c = text[++index]; } //設置容許的語法 void set_flags(unsigned short _Flags) { flags = _Flags; } private: friend class xparser_t<xts_utf16>; }; template<typename _XtsTy> class xdocument_t; template<typename _Ty> class xelement_t; template<typename _Ty> class xresource_t { public: //xml節點數據結構,由於結構相互依賴的緣由,因此嵌套在一塊兒 using Basetype = typename _Ty::value_type; class xnode; using xtagindex_t = std::list<xnode*>; using xtagindex_ref = typename xtagindex_t::iterator; using xdoctext_t = std::list<_Ty>; using xattrname_t = std::set<_Ty>; using xattrvalue_t = std::map<_Ty, xml_size_t>; using xtagtext_t = std::map<_Ty, xtagindex_t>; using xdoctext_ref = typename xdoctext_t::iterator; using xtagtext_ref = typename xtagtext_t::iterator; using xattrname_ref = typename xattrname_t::iterator; using xattrvalue_ref = typename xattrvalue_t::iterator; class xnode { public: using _Self_Reftype = typename std::list<xnode>::iterator; xnode() { parent = nullptr; } xnode(xnode *_Parent, xresource_t *_Resource) { parent = _Parent; ti.doc_body_ref = inner.end = inner.begin = _Resource->docs.end(); } private: void refactor_tag_body(int _Style, xml_size_t _PreSize, xresource_t *_Resource) { _Ty &_Tmp = _Resource->refactor_buffer; _Tmp.clear(); _Tmp.reserve(_PreSize); _Tmp += (Basetype)'<'; _Tmp += ti.name->first; for (auto it = attrs.begin(); it != attrs.end(); ++it) { _Tmp += (Basetype)' '; _Tmp += *it->name; _Tmp += (Basetype)'='; _Tmp += (Basetype)it->st; _Tmp += it->value->first; _Tmp += (Basetype)it->st; } if (_Style == 2) _Tmp += (Basetype)'/'; _Tmp += (Basetype)'>'; if (ti.doc_body_ref == _Resource->docs.end()) { _Resource->docs.push_back(_Tmp); ti.doc_body_ref = --(_Resource->docs.end()); parent->inner.end = ti.doc_body_ref; if (parent->inner.begin == _Resource->docs.end()) parent->inner.begin = ti.doc_body_ref; } else { *ti.doc_body_ref = _Tmp; } } private: friend class xresource_t; friend class xparser_t<xts_utf8>; friend class xparser_t<xts_utf16>; friend class xparser_t<xts_asc>; friend class xdocument_t<xts_utf8>; friend class xdocument_t<xts_utf16>; friend class xdocument_t<xts_asc>; friend class xelement_t<xts_utf8>; friend class xelement_t<xts_utf16>; friend class xelement_t<xts_asc>; struct xattr { xattrname_ref name; xattrvalue_ref value; char st; }; struct tag_info { xtagtext_ref name;//標籤名稱 xtagindex_ref name_index_ref;//在標籤名稱索引中的引用,本質上其實就是個指針 xdoctext_ref doc_body_ref;//整個標籤信息,包含屬性 <t ...> 在文檔中的實體 }ti; std::list<xattr> attrs; struct xinner { xdoctext_ref begin, end; }inner; std::list<xnode> child; xnode *parent; _Self_Reftype self; }; xresource_t() { //預約義的幾個轉義符實體:lt gt amp quot apos escape_bodys[{ (Basetype)'l', (Basetype)'t'}] = { (Basetype)'<' }; escape_bodys[{ (Basetype)'g', (Basetype)'t' }] = { (Basetype)'>' }; escape_bodys[{ (Basetype)'a', (Basetype)'m', (Basetype)'p' }] = { (Basetype)'&' }; escape_bodys[{ (Basetype)'q', (Basetype)'u', (Basetype)'o', (Basetype)'t' }] = { (Basetype)'"' }; escape_bodys[{ (Basetype)'a', (Basetype)'p', (Basetype)'o', (Basetype)'s' }] = { (Basetype)'\'' }; } void clear() { root.child.clear(); docs.clear(); tags.clear(); attr_names.clear(); attr_values.clear(); root.parent = nullptr; root.ti.doc_body_ref = root.inner.end = root.inner.begin = docs.end(); } xnode root; xdoctext_t docs; xtagtext_t tags; xattrname_t attr_names; xattrvalue_t attr_values; std::map<_Ty, _Ty> escape_bodys; _Ty refactor_buffer; }; template<typename _XtsTy> class xparser_t { public: using _StringTy = typename _XtsTy::strtype; using Basetype = typename _XtsTy::Basetype; xparser_t() { // 這裏忽悠一下編譯器,自動根據類型選擇:strstr 或 wcsstr typedef const char *(*STRSTRFUNC)(const char *, const char *); typedef const wchar_t *(*WSTRSTRFUNC)(const wchar_t *, const wchar_t *); typedef const Basetype *(*MYSTRSTRFUNC)(const void *, const void *); __multiec_strstr = ((sizeof(Basetype) == 1) ? ((MYSTRSTRFUNC)((STRSTRFUNC)strstr)) : ((MYSTRSTRFUNC)((WSTRSTRFUNC)wcsstr))); } private: void x_escape_number() { xml_size_t ebgn = xts.index - 1; long long x = 0; if (!xts.next_is_char('x')) { xts.index -= xts.cl; xts.set_flags(XML_SYNTAX::_X_NUMBER); if (!xts.next_is_flags()) err(xts.index, 22); xts.set_flags(XML_SYNTAX::_X_NUMBER | XML_SYNTAX::_X_ESCAPEEND); for (;;) { x = (x * 10) + (xts.c - '0'); if (!xts.next_is_flags()) err(xts.index, 22); if (xts.c == ';') break; } } else { xts.set_flags(XML_SYNTAX::_X_HEX); if (!xts.next_is_flags()) err(xts.index, 23); xts.set_flags(XML_SYNTAX::_X_HEX | XML_SYNTAX::_X_ESCAPEEND); for (int i = 0;; i++) { long long _Tmp; switch (xts.c) { case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': _Tmp = xts.c - '0'; break; case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': _Tmp = xts.c - 'a' + 10; break; case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': _Tmp = xts.c - 'A' + 10; break; } x += (_Tmp << (i << 2)); if (!xts.next_is_flags()) err(xts.index, 23); if (xts.c == ';') break; } } if (x < 0x20) { switch (x) { case '\t':case '\r':case '\n': break; default: err(ebgn, 24); } } else if (x > 0xD800 && x < 0xDFFF) err(ebgn, 25); else if (x > 0x10FFFF) err(ebgn, 26); } void x_escape_body() { xml_size_t nbgn = xts.index; for (;;) { xts.next(); if (xts.c == ';') { break; } else { if (!(xts.s & XML_SYNTAX::_X_BEGINNAME)) err(xts.index, 19); } } _StringTy &_Tmp = _strtmp[4]; _Tmp.assign(xts.text + nbgn, xts.index - nbgn); auto it = res->escape_bodys.find(_Tmp); if (it == res->escape_bodys.end()) { errinfobuffer.reserve(_Tmp.length() * 3); int n = sprintf((char*)errinfobuffer.data(), ((sizeof(Basetype) == 2) ? "%ls" : "%s"), _Tmp.c_str()); err(nbgn, 20, errinfobuffer.c_str()); } } void x_escape() { xts.next(); if (xts.c == '#') { x_escape_number(); } else { if (!(xts.s & XML_SYNTAX::_X_BEGINNAME)) err(xts.index, 18); x_escape_body(); } } void x_cdata() { xml_size_t cbgn = xts.index - 2; const char *pcdata = "CDATA["; for (int i = 0; i < 6; i++) { if (!xts.next_is_char(pcdata[i])) err(xts.index, 16); } // CDATA的結束符比註釋標籤還要省事,直接向後搜索]]> const Basetype *p = __multiec_strstr(xts.text + xts.index + 1, _XtsTy::constval::cdata_end); if (p) { xts.index = (xml_size_t)(p - xts.text + 3); res->docs.push_back(_StringTy(xts.text + cbgn, xts.index - cbgn)); cur->inner.end = --(res->docs.end()); if (cur->inner.begin == res->docs.end()) cur->inner.begin = cur->inner.end; } else { err(cbgn, 17); } } void x_comment() { xml_size_t cbgn = xts.index - 2; if (!xts.next_is_char('-')) err(xts.index, 13); /* 不太清楚爲何xml註釋中不容許存在--,我反正照作了。 從代碼此處看,其實是能夠容許的,就像是CDATA的結束符那樣。 utf8的狀況下,沒法雙字搜索。 utf16的狀況下,也沒法4字節搜索。 例如這種狀況: <--a-->,若是雙字搜索,從a開始,有一個-就被忽略掉了,若是要判斷這個問題,那實際上和單字節搜索同樣的性能。 */ const Basetype *p = __multiec_strstr(xts.text + xts.index + 1, _XtsTy::constval::comment_end); if (p) { if (p[2] == '>') { xts.index = (xml_size_t)(p - xts.text + 3); res->docs.push_back(_StringTy(xts.text + cbgn, xts.index - cbgn)); cur->inner.end = --(res->docs.end()); if (cur->inner.begin == res->docs.end()) cur->inner.begin = cur->inner.end; } else { err((xml_size_t)(p - xts.text), 15); } } else { err(cbgn, 14); } } void x_specifics_tag() { //特殊標籤,共有兩個分支,註釋和CDATA,DTD在根節點以前處理,不會進入這裏 xts.set_flags(XML_SYNTAX::_X_LB | XML_SYNTAX::_X_NEGATIVE); if (!xts.next_is_flags()) err(xts.index, 2); if (xts.c == '-') x_comment(); else x_cdata(); } void x_end_node() { //結束標籤處理 xts.set_flags(XML_SYNTAX::_X_BEGINNAME); if (!xts.next_is_flags()) err(xts.index, 10); xml_size_t nbgn = xts.index; xml_size_t nend; xts.set_flags(XML_SYNTAX::_X_NAME | XML_SYNTAX::_X_SPACE | XML_SYNTAX::_X_GT); bool _BackSpace = false; for (;;) { if (!xts.next_is_flags()) err(xts.index, 10); if (!(xts.s & XML_SYNTAX::_X_NAME)) { nend = xts.index; if (xts.s & XML_SYNTAX::_X_SPACE) _BackSpace = true; break; } } if (_BackSpace) { //後面還有空格 xts.set_flags(XML_SYNTAX::_X_SPACE | XML_SYNTAX::_X_GT); for (;;) { if (!xts.next_is_flags()) err(xts.index, 11); if (xts.c == '>') break; } } _StringTy &tmp = _strtmp[0]; tmp.reserve(nend - nbgn + 0x10); tmp.assign(xts.text + nbgn, nend - nbgn); if (tmp != cur->ti.name->first) { errinfobuffer.reserve((tmp.length() + cur->ti.name->first.length()) * 3 + 0x20); int n = sprintf((char*)errinfobuffer.data(), ((sizeof(Basetype) == 2) ? "%ls 與 %ls 不一致" : "%s 與 %s 不一致"), tmp.c_str(), cur->ti.name->first.c_str()); err(nbgn, 12, errinfobuffer.c_str()); } if (cur->inner.begin == res->docs.end()) { //若是這個節點的內容爲空,說明,它跟一個自結束的節點沒有區別 //直接在父節點中將它修改一個自結束節點便可。 cur->parent->inner.end->erase(cur->parent->inner.end->length() - 1); cur->parent->inner.end->append(_XtsTy::constval::autoend_tag_syntax); } else { res->docs.push_back(_XtsTy::constval::end_tag_syntax); auto it = --(res->docs.end()); it->append(tmp); (*it) += (Basetype)'>'; cur->inner.end = it; } cur = cur->parent; } int x_tag_name() { auto new_name = [this](xml_size_t left, xml_size_t right) { _StringTy &tmp = _strtmp[0]; tmp.assign(xts.text + left, right - left); auto it = res->tags.find(tmp); if (it == res->tags.end()) it = res->tags.insert({ tmp, std::list<_Nodetype*>() }).first; it->second.push_back(cur); cur->ti.name = it; cur->ti.name_index_ref = (--(it->second.end())); }; xts.set_flags( XML_SYNTAX::_X_NAME | //符合名稱規範的字符 XML_SYNTAX::_X_GT | // > XML_SYNTAX::_X_TAGEND | // /自結束標籤 XML_SYNTAX::_X_SPACE // 空白字符 ); xml_size_t name_begin = xts.index; for (;;) { if (!xts.next_is_flags()) err(xts.index, 1); switch (xts.c) { case '>': new_name(name_begin, xts.index); return 1; case '/': if (!xts.next_is_char('>')) err(xts.index, 4); new_name(name_begin, xts.index - 1); return 2; default: if (xts.s & XML_SYNTAX::_X_SPACE) { new_name(name_begin, xts.index); return 0; } break; } } } bool x_attr_name(_StringTy &_Name) { xts.set_flags( XML_SYNTAX::_X_NAME | //符合名稱規範的字符 XML_SYNTAX::_X_EQUAL | //等於號 XML_SYNTAX::_X_SPACE // 空白字符 ); xml_size_t name_begin = xts.index; for (;;) { if (!xts.next_is_flags()) err(xts.index, 5); if (xts.s & (XML_SYNTAX::_X_EQUAL | XML_SYNTAX::_X_SPACE)) { _Name.assign(xts.text + name_begin, xts.index - name_begin); return xts.c == '='; } } return false; } char x_attr_value(_StringTy &_Value) { xts.set_flags( XML_SYNTAX::_X_STRING | //字符串 " ' XML_SYNTAX::_X_SPACE // 空白字符 ); char _Style; for (;;) { if (!xts.next_is_flags()) err(xts.index, 7); if (xts.s & XML_SYNTAX::_X_STRING) { _Style = (char)xts.c; break; } } xml_size_t value_begin = xts.index + 1; if (_Style == '"') { for (;;) { xts.next(); switch (xts.c) { case 0: err(xts.index, 8); case '<': err(xts.index, 9); case '&': //處理轉義符 x_escape(); break; case '"': //字符串結束 _Value.assign(xts.text + value_begin, xts.index - value_begin); return _Style; default: break; } } } else { for (;;) { xts.next(); switch (xts.c) { case 0: err(xts.index, 8); case '<': err(xts.index, 9); case '&': //處理轉義符 x_escape(); break; case '\'': //字符串結束 _Value.assign(xts.text + value_begin, xts.index - value_begin); return _Style; default: break; } } } return _Style; } void x_attr(xml_size_t &_Presize) { _StringTy &name = _strtmp[0]; _StringTy &value = _strtmp[1]; if (!x_attr_name(name)) { //x_attr_name中沒有找到等於號,對應這種: <a x =... xts.set_flags( XML_SYNTAX::_X_EQUAL | //等號 XML_SYNTAX::_X_SPACE // 空白字符 ); for (;;) { if (!xts.next_is_flags()) err(xts.index, 7); if (xts.c == '=') break; } } char _Style = x_attr_value(value); _Presize += (xml_size_t)(name.length() + value.length() + 6); auto itn = res->attr_names.find(name); if (itn == res->attr_names.end()) itn = res->attr_names.insert(name).first; auto itv = res->attr_values.find(value); if (itv == res->attr_values.end()) itv = res->attr_values.insert({ value, 1 }).first; cur->attrs.push_back({ itn, itv, _Style }); } int x_preattr(xml_size_t &_Presize) { /* x_tag_name裏沒有找到 > 的狀況下,在標籤屬性解析開始以前, 對應下面這幾種狀況: <a > <a /> <a x=... */ xts.set_flags( XML_SYNTAX::_X_SPACE | //空白字符 XML_SYNTAX::_X_GT | // > XML_SYNTAX::_X_BEGINNAME | //名稱首字符 XML_SYNTAX::_X_TAGEND // /自結束標籤 ); for (;;) { if (!xts.next_is_flags()) err(xts.index, 5); switch (xts.c) { case '>': return 1; case '/': if (!xts.next_is_char('>')) err(xts.index, 4); return 2; default: if (xts.s & XML_SYNTAX::_X_BEGINNAME) { x_attr(_Presize); xts.set_flags( XML_SYNTAX::_X_SPACE | // 空白字符 XML_SYNTAX::_X_GT | // > XML_SYNTAX::_X_BEGINNAME | // 名稱首字符 XML_SYNTAX::_X_TAGEND // /自結束標籤 ); } break; } } } int node_size = 0; void x_new_node() { node_size++; cur->child.push_back(_Nodetype(cur, res)); auto it = (--cur->child.end()); cur = &(*it); cur->self = it; int n = x_tag_name(); xml_size_t _PreSize = (xml_size_t)(cur->ti.name->first.length() + 3); if (!n) n = x_preattr(_PreSize); cur->refactor_tag_body(n, _PreSize, res); if (n == 2) cur = cur->parent; } void x_tag() { //標籤開始後,下一個字符只能是 符合名稱規範的第一個字符,感嘆號 !,結束標籤 / xts.next(); switch (xts.c) { case '!': x_specifics_tag(); break; case '/': x_end_node(); break; default: if (!(xts.s & XML_SYNTAX::_X_BEGINNAME)) err(xts.index, 1); x_new_node(); break; } } void x_text() { //標籤以外的有效文本處理 xml_size_t tbegin = _xnf; _StringTy &tmp = _strtmp[3]; tmp.clear(); for (;;) { xts.next(); switch (xts.c) { case 0: return; case '&': if (tbegin == _xnf) tbegin = xts.index; x_escape(); break; case '<': //處理標籤以前,先處理有效文本 if (tbegin != _xnf) { if (tmp.length()) tmp += ' '; tmp.append(xts.text + tbegin, xts.index - tbegin); tbegin = _xnf; } if (tmp.length()) { res->docs.push_back(tmp); cur->inner.end = --(res->docs.end()); if (cur->inner.begin == res->docs.end()) cur->inner.begin = cur->inner.end; tmp.clear(); } x_tag(); break; default: if (!(xts.s & XML_SYNTAX::_X_SPACE)) { if (tbegin == _xnf) tbegin = xts.index; } else { //遇到空白字符時,若是有效文本開始位置已經記錄過了,則將這一段有效的東西添加到有效文本結 if (tbegin != _xnf) { if (tmp.length()) tmp += ' '; tmp.append(xts.text + tbegin, xts.index - tbegin); tbegin = _xnf; } } } } } void x_dtd() { xml_size_t pos = xts.index - 1; const char *p = "OCTYPE"; for (int i = 0; i < 6; i++) { if (!xts.next_is_char(p[i])) err(pos, 2); } int n = 1; int _StrType = 0; for (;;) { xts.next(); switch (xts.c) { case '"': case '\'': if (!_StrType) _StrType = xts.c; else if (_StrType == xts.c) _StrType = 0; break; case '<': n++; break; case '>': if (!_StrType) { if (!(--n)) { res->docs.push_back(_StringTy(xts.text + pos, xts.index - pos + 1)); cur->inner.end = --(res->docs.end()); if (cur->inner.begin == res->docs.end()) cur->inner.begin = cur->inner.end; //wprintf(L"%s\n", cur->inner.end->c_str()); return; } } break; default: break; } } } void x_declare() { xml_size_t pos = xts.index - 1; int _StrType = 0; for (;;) { xts.next(); switch (xts.c) { case '"': case '\'': if (!_StrType) _StrType = xts.c; else if (_StrType == xts.c) _StrType = 0; break; case '?': if (!_StrType) { if (!xts.next_is_char('>')) err(xts.index, 30); res->docs.push_back(_StringTy(xts.text + pos, xts.index - pos + 1)); cur->inner.end = --(res->docs.end()); if (cur->inner.begin == res->docs.end()) cur->inner.begin = cur->inner.end; return; } break; default: break; } } } int x_root() { if (setjmp(_Rem)) return -1; xts.set_flags(XML_SYNTAX::_X_LT | XML_SYNTAX::_X_SPACE); bool root_break = false; for (;;) { if (xts.c == '<') { xts.next(); switch (xts.c) { case '!': xts.next(); if (xts.c == '-') x_comment(); else if (xts.c == 'D') x_dtd(); else err(xts.index, 2); xts.set_flags(XML_SYNTAX::_X_LT | XML_SYNTAX::_X_SPACE); break; case '?': if (xts.index != 1) err(xts.index, 31); x_declare(); xts.set_flags(XML_SYNTAX::_X_LT | XML_SYNTAX::_X_SPACE); break; default: if (xts.s & XML_SYNTAX::_X_BEGINNAME) { xts.back(1); x_tag(); x_text(); root_break = true; } else { err(xts.index, 2); } break; } if (root_break) break; } if (!xts.next_is_flags()) err(xts.index, 3); } if (cur != &(res->root)) { //解析完字符串以後,若是當前標籤不爲null,則屬於錯誤。 err(xts.size, 28); } return 0; } void err(xml_size_t _Pos, int _Number, const char *_Info = "") { errp = { _Pos, _Number, _Info }; longjmp(_Rem, 1); } public: int load(const Basetype *_Text, int _Size, xresource_t<_StringTy> *pres) { xts.init((const Basetype*)_Text, _Size); errp.number = 0; errp.pos = 0; res = pres; cur = &(res->root); return x_root(); } void get_errp(xerrorpos &e) { e = errp; } void get_err_pos(xerrorpos &e) { e.line = 0; e.column = 0; if (!e.pos || !e.number) return; auto pos = xts.index - xts.cl; xts.index = 0; xts.c = xts.text[0]; e.line = 1; e.column = 1; for (; xts.index < e.pos; xts.next_donot_syntax()) { if (xts.c == '\n') { e.line++; e.column = 1; } else { e.column++; } } xts.index = pos; xts.next(); } private: friend class xelement_t<_XtsTy>; jmp_buf _Rem; _XtsTy xts; xerrorpos errp; using _Nodetype = typename xresource_t<_StringTy>::xnode; _Nodetype *cur; xresource_t<_StringTy> *res; _StringTy _strtmp[8];//因爲使用了jmp_buf來進行錯誤直接遠跳,爲了不內存泄漏,因此將棧中須要的字符串對象也儲存在這裏 const Basetype*(*__multiec_strstr)(const void*, const void*); std::string errinfobuffer; }; static const char *xml_error_information[] = { "", "開始標籤:無效的元素名稱", //1 "根節點以前的無效的特殊標籤", //2 "根節點以前的無效的字符", //3 "自結束標籤:此處應爲 >", //4 "標籤屬性:無效的標籤屬性名稱", //5 "標籤屬性:此處應爲 =", //6 "標籤屬性:此處應爲 \" 或 '", //7 "標籤屬性:未找到對應的屬性結束符(\" 或 ')", //8 "標籤屬性:< 不容許出如今屬性值中", //9 "結束標籤:無效的元素名稱", //10 "結束標籤:此處應爲 >", //11 "結束標籤:開始標籤與結束標籤不匹配,參考信息:%s", //12 "註釋標籤:無效的註釋標籤,此處或許應爲 -",//13 "註釋標籤:未找到註釋標籤結束符(-->)",//14 "註釋標籤:-- 不容許單獨出如今註釋標籤中",//15 "CDATA:無效的CDATA標籤",//16 "CDATA:未找到CDATA結束符(]]>)",//17 "轉義符:無效的轉義符名稱首字符",//18 "轉義符:無效的轉義符字符",//19 "轉義符:%s 是未定義的實體",//20 "轉義符:無效的轉義符字符",//21 "字符數值轉義:無效的10進制數字字符",//22 "字符數值轉義:無效的16進制數字字符",//23 "字符數值轉義:小於32(0x20)的字符僅容許\\t\\r\\n出如今xml中",//24 "字符數值轉義:0xD800-0xDFFF爲UNICODE代理字符,不容許單獨出如今xml中",//25 "字符數值轉義:字符值溢出,參考最大值(0x10FFFF)",//26 "轉義符:無效的轉義符",//27 "根節點未封閉",//28 "無效的文檔:%s",//29 "XML聲明種錯誤的符號,此處應爲 >",//30 "XML聲明前不容許存在其餘字符",//31 "未找到XML聲明結束符(?>)",//32 }; static xml_size_t XGI_FORMAT = 1; static xml_size_t XGI_COMMENT = 2; static xml_size_t XGI_CDATAONLY = 4; static xml_size_t XGI_TEXTONLY = 8; template<typename _XtsTy> class xelement_t { public: using _StringTy = typename _XtsTy::strtype; using _Nodetype = typename xresource_t<_StringTy>::xnode; using Basetype = typename xresource_t<_StringTy>::Basetype; bool eof() { return _Node == nullptr; } xelement_t(_Nodetype *_Val) { _Node = _Val; } _StringTy get_name() { return _Node->ti.name->first; } _StringTy get_attr(const _StringTy &_AttrName) { for (auto it = _Node->attrs.begin(); it != _Node->attrs.end(); ++it) { if (*(it->name) == _AttrName) return it->value->first; } return ""; } _StringTy get_inner_xml() { _StringTy _Tmp; for (auto it = _Node->inner.begin; it != _Node->inner.end; ++it) { if (it->length() > 3 && it->at(0) == '<' && it->at(1) == '!' && it->at(2) == '-') continue; if (it->length() > 4 && *it == _XtsTy::constval::br_tag) { _Tmp += _XtsTy::constval::crlf; continue; } if (it->length() > 1 && it->at(0) == '<' && it->at(1) != '/') _Tmp += _XtsTy::constval::crlf; _Tmp += it->c_str(); } return _Tmp; } private: _Nodetype *_Node; }; template<typename _XtsTy> class xdocument_t { public: xdocument_t() { } ~xdocument_t() { res.clear(); } using _StringTy = typename _XtsTy::strtype; using _ParserTy = xparser_t<_XtsTy>; using Basetype = typename _XtsTy::Basetype; using element = xelement_t<_XtsTy>; int load_file(const _StringTy &_Filename) { errp.pos = 0; errp.line = 0; errp.column = 0; res.clear(); std::ifstream fs(_Filename.c_str(), std::ios::binary); fs.seekg(0, std::ios::end); size_t s = (size_t)fs.tellg(); fs.seekg(0, std::ios::beg); if (!s) { errp.information.reserve(_Filename.length() * 3); sprintf((char*)errp.information.data(), (sizeof(Basetype) == 2) ? "%ls" : "%s", _Filename.c_str()); errp.number = 29; errp.pos = 0; return -1; } char *p = new char[s + 2]; p[s] = 0; p[s + 1] = 0; fs.read(p, s); fs.close(); size_t _Off = 0; //預測文檔編碼,並不必定準確,只能說想到的判斷都作了。 /*返回值有4種: 0 多字節編碼非utf-8 1 utf-16 2 utf-8 -1 錯誤 */ #if defined(_WIN32) || defined(_WIN64) _SrcEncode = encode_adaptive::xmlec_predict(p, s, &(errp.number), &_Off); if (_SrcEncode < 0) { delete p; errp.information.reserve(_Filename.length() * 3); sprintf((char*)errp.information.data(), (sizeof(Basetype) == 2) ? "%ls" : "%s", _Filename.c_str()); return -1; } //很遺憾的事情是,c++17刪除了編碼轉換庫,因此,只能使用操做系統的函數來完成了。 //雖然這個類庫並不依賴c++17,但爲了之後和新標準對接,因此只能本身實現跨平臺的轉換策略。 //另一點是,linux其實對轉碼沒有什麼需求。 _StringTy _Text; if (encode_adaptive::specifiy(p + _Off, _SrcEncode, _XtsTy::_encoding, _Text) == _nf) { delete p; errp.information.reserve(_Filename.length() * 3); sprintf((char*)errp.information.data(), (sizeof(Basetype) == 2) ? "%ls" : "%s", _Filename.c_str()); errp.number = 29; return -1; } delete p; _ParserTy xp; int _Result = xp.load(_Text.c_str(), (xml_size_t)s, &res); #else _ParserTy xp; int _Result = xp.load(p, (xml_size_t)s, &res); delete p; #endif res.root.inner.end = res.docs.end(); xp.get_errp(errp); if (errp.number) xp.get_err_pos(errp); return _Result; } element get_element(const _StringTy &_TagName) { auto it = res.tags.find(_TagName); if (it != res.tags.end()) return *(it->second.begin()); return nullptr; } std::string get_error_info() { char buf[256]; std::string _Result; if (errp.pos != 0) { sprintf(buf, "XML錯誤位於 行(%d), 列(%d):", errp.line, errp.column); _Result += buf; } sprintf(buf, xml_error_information[errp.number], errp.information.c_str()); _Result += buf; return _Result; } element root() { return &(res.root); } private: xresource_t<_StringTy> res; xerrorpos errp; int _SrcEncode; }; } #if defined(_WIN32) || defined(_WIN64) template<typename _Ty> using xdoc = aqx_internal::xdocument_t<_Ty>; using xts_utf8 = aqx_internal::xts_utf8; using xts_utf16 = aqx_internal::xts_utf16; using xts_asc = aqx_internal::xts_asc; #else using xdoc = aqx_internal::xdocument_t<aqx_internal::xts_utf8>; #endif } #pragma warning( pop )
//encode_adaptive.h - windows only #pragma once #include <string> #include "tcvt.h" #ifndef _nf #define _nf ((size_t)-1) #endif namespace aqx { namespace encode_adaptive { static constexpr auto unknow{ static_cast<int>(-1) }; static constexpr auto sys{ static_cast<int>(0) }; static constexpr auto utf16{ static_cast<int>(1) }; static constexpr auto utf8{ static_cast<int>(2) }; static int profile_predict(unsigned char *_Text, size_t _Size, int &_Off, int _Def = 0) { if (_Size >= 3) { if (_Text[0] == 0xEF && _Text[1] == 0xBB && _Text[2] == 0xBF) { _Off = 3; return 2; } } if (_Size >= 2) { if (_Text[0] == 0xFF && _Text[1] == 0xFE) { _Off = 2; return 1; } } _Off = 0; size_t s = _Size; if (s > 0x10) s = 0x10; int x = 0; for (size_t i = 0; i < s; i++) { if (_Text[i] == 0) x++; } if (_Size == s && x == 1) return _Def; if (!x) return _Def; return 1; } template<typename _Ty> static int profile_adaptive(char *_Text, size_t _Size, _Ty &_Result, int _Def = 0) { int _StartOff = 0; int _SrcCode = encode_adaptive::profile_predict((unsigned char*)_Text, _Size, _StartOff, _Def); size_t _TargetCode = 0; if (sizeof(decltype(*_Result.c_str())) == 2) _TargetCode = 1; std::wstring _utf16; if (_SrcCode == 2) aqx::utf16_from_utf8(_utf16, _Text + _StartOff); else if (_SrcCode == 1) _utf16 = (wchar_t*)(_Text + _StartOff); else aqx::utf16_from_asc(_utf16, _Text + _StartOff); auto _proc0 = [](void *_Res, std::wstring &_wstr) { asc_from_utf16(*(std::string*)_Res, _wstr); }; auto _proc1 = [](void *_Res, std::wstring &_wstr) { *(std::wstring*)(_Res) = _wstr; }; auto _proc2 = [](void *_Res, std::wstring &_wstr) { aqx::utf8_from_utf16(*(std::string*)(_Res), _wstr); }; if (_TargetCode == 0) _proc0(&_Result, _utf16); else _proc1(&_Result, _utf16); return _SrcCode; } template<typename _Ty> static size_t specifiy(char *_Text, int _Srcec, int _Targetec, _Ty &_Result) { if (sizeof(_Ty::_Mybase::_Alty::value_type) == 1 && _Targetec == 1) return _nf; if (sizeof(_Ty::_Mybase::_Alty::value_type) == 2 && _Targetec != 1) return _nf; if (_Srcec == 2) { if (_Targetec == 2) { *(std::string*)&_Result = (_Text); return _Result.length(); } else if (_Targetec == 1) return utf16_from_utf8(*(std::wstring*)&_Result, _Text); else return asc_from_utf8(*(std::string*)&_Result, _Text); } else if (_Srcec == 1) { if (_Targetec == 2) return utf8_from_utf16(*(std::string*)&_Result, (wchar_t*)_Text); else if (_Targetec == 1) { *(std::wstring*)&_Result = (wchar_t*)(_Text); return _Result.length(); } else return asc_from_utf16(*(std::string*)&_Result, (wchar_t*)_Text); } else { if (_Targetec == 2) return utf8_from_asc(*(std::string*)&_Result, _Text); else if (_Targetec == 1) return utf16_from_asc(*(std::wstring*)&_Result, _Text); else { *(std::string*)&_Result = (_Text); return _Result.length(); } } return _nf; } static void unknow_append(void *_Res, std::string _Str) { *(std::string*)(_Res) += _Str; } static void unknow_wappend(void *_Res, std::wstring _Str) { *(std::wstring*)(_Res) += _Str; } static int xmlec_nbom_wchar(wchar_t *_Text, size_t _Size) { if (_Size < 7) return -1;//小於7字節的xml文檔是不成立的 auto p = wcschr(_Text, L'<'); if (!p) return -1; if (p[1] == L'?') { if (p != _Text) return -3;//xml聲明沒有位於xml文件頭部 p = wcsstr(_Text + 2, L"?>"); if (!p) return -4;//沒有找到xml聲明結尾 } return 1; } static int xmlec_nbom_char(char *_Text, size_t _Size) { auto p = strchr(_Text, '<'); if (!p) return -1; if (!p[1]) //找到第一個<,若是他它以後一個字符是0,則考慮它是否是utf16 { if (p - _Text == _Size - 1) return -2;//若是它已是字符串最後一個有效字符,直接報錯。 if (_Size % 2) return -2; //長度不是偶數,說明絕對不多是utf16 return xmlec_nbom_wchar((wchar_t*)_Text, (_Size >> 1)); } if (p[1] == '?') { if (p != _Text) return -3;//xml聲明沒有位於xml文件頭部 p = strstr(_Text + 2, "?>"); if (!p) return -4;//沒有找到xml聲明結尾 auto s = (p - _Text) + 2; std::string str(_Text, p - _Text + 2); std::transform(str.begin(), str.end(), str.begin(), toupper); if (str.find("UTF-8") != _nf) return 2; if (str.find("GBK") != _nf) return 0; if (str.find("GB2312") != _nf) return 0; } return 2; } static int xmlec_predict(char *_Text, size_t _Size, int *err_number, size_t *_Off = NULL, int _Default = 2) { *err_number = 0; if (_Size < 7) { //小於7字節的xml文檔是不成立的 *err_number = 29; return -1; } //先基於bom判斷 if ((unsigned char)(_Text[0]) == 0xEF && (unsigned char)(_Text[1]) == 0xBB && (unsigned char)(_Text[2]) == 0xBF) { if (_Off) *_Off = 3; auto p = strchr(_Text + 3, '<'); if (!p) { *err_number = 29; return -1; } if (p[1] == '?') { if (p != _Text + 3) { *err_number = 31; return -1; } p = strstr(_Text + 5, "?>"); if (!p) { *err_number = 32; return -1; } } return 2; } else if ((unsigned char)(_Text[0]) == 0xFF && (unsigned char)(_Text)[1] == 0xFE) { if (_Off) *_Off = 2; auto p = wcschr((wchar_t*)_Text + 1, L'<'); if (!p) { *err_number = 29; return -1; } if (p[1] == L'?') { if (p != (wchar_t*)_Text + 1) { *err_number = 31; return -1; } p = wcsstr((wchar_t*)_Text + 3, L"?>"); if (!p) { *err_number = 32; return -1; } } return 1; } if (_Off) *_Off = 0; int n = xmlec_nbom_char(_Text, _Size); if (n < -1) { if (n == -2) *err_number = 29; else if (n == -3) *err_number = 31; else if (n == -4) *err_number = 32; return -1; } else if (n >= 0) return n; if (!(_Size % 2)) n = xmlec_nbom_wchar((wchar_t*)_Text, (_Size >> 1)); if (n < -1) { if (n == -2) *err_number = 29; else if (n == -3) *err_number = 31; else if (n == -4) *err_number = 32; return -1; } return _Default; } }; }
//tcvt.h - windows only #pragma once #if defined(_WIN32) || defined(_WIN64) #ifndef _WINDOWS_ #include <windows.h> #endif #endif namespace aqx { static size_t _mbs2wcs(int _Cp, const std::string &_Mbs, std::wstring &_Wcs) { int n = MultiByteToWideChar(_Cp, 0, _Mbs.c_str(), (int)_Mbs.length(), nullptr, 0); _Wcs.resize(n); return MultiByteToWideChar(_Cp, 0, _Mbs.c_str(), (int)_Mbs.length(), (wchar_t*)_Wcs.data(), (int)_Wcs.capacity()); } static size_t _wcs2mbs(int _Cp, const std::wstring &_Wcs, std::string &_Mbs) { int n = WideCharToMultiByte(_Cp, 0, _Wcs.c_str(), (int)_Wcs.length(), nullptr, 0, NULL, FALSE); _Mbs.resize(n); return WideCharToMultiByte(_Cp, 0, _Wcs.c_str(), (int)_Wcs.length(), (char*)_Mbs.data(), (int)_Mbs.capacity(), NULL, FALSE); } static size_t utf8_from_asc(std::string &_Result, const std::string &_Asc) { std::wstring _Tmp; _mbs2wcs(CP_ACP, _Asc, _Tmp); return _wcs2mbs(CP_UTF8, _Tmp, _Result); } static size_t utf16_from_asc(std::wstring &_Result, const std::string &_Asc) { return _mbs2wcs(CP_ACP, _Asc, _Result); } static size_t asc_from_utf8(std::string &_Result, const std::string &_U8s) { std::wstring _Tmp; _mbs2wcs(CP_UTF8, _U8s, _Tmp); return _wcs2mbs(CP_ACP, _Tmp, _Result); } static size_t utf16_from_utf8(std::wstring &_Result, const std::string &_U8s) { return _mbs2wcs(CP_UTF8, _U8s, _Result); } static size_t utf8_from_utf16(std::string &_Result, const std::wstring &_Wcs) { return _wcs2mbs(CP_UTF8, _Wcs, _Result); } static size_t asc_from_utf16(std::string &_Result, const std::wstring &_Wcs) { return _wcs2mbs(CP_ACP, _Wcs, _Result); } }
測試代碼:
#include "pch.h" #include <iostream> #include "xml.hpp" #include <time.h> int main() { setlocale(LC_ALL, ""); // 支持三種編碼格式:aqx::xts_utf16 aqx::xts_utf8 aqx::xts_asc aqx::xdoc<aqx::xts_utf16> doc; auto t = clock(); int err = doc.load_file(L"G:\\vs2017\\test\\生成\\test.xml"); printf("解析文檔耗時:%d ms\n", clock() - t); if (err) { printf("%s\n", doc.get_error_info().c_str()); return 0; } auto e = doc.get_element(L"CATALOG2"); printf("%ls\n", e.get_inner_xml().c_str()); system("pause"); return 0; }