タグつきの文書って応用が利いて便利なんですけど、
「XML」のブランド名がついてると、
面倒な しがらみがたくさんありますよね?
DOCTYPE宣言? なにそれ?
DTD? なにそれ?
おいらはタグつきの文書を読み込みたいだけなのですー! がおー!
//========================================================= // // 簡易XMLパーサークラス(宣言) // //========================================================= #ifndef _NOTXML_ #define _NOTXML_ #include <vector> #include <map> #include <string> using namespace std; class NotXML { public: //========================================================= // タグ構造体 //========================================================= struct Tag { string name; // タグ名 string value; // 値 map<string, string> attribute; // 属性 vector<Tag> childList; // 子要素 Tag() { name = ""; value = ""; } }; //========================================================= // 読み込む //========================================================= vector<Tag> Read (string fileName); Tag ReadRoot (string fileName); //========================================================= // Utilities //========================================================= static Tag GetTagFromName( const Tag& parent, string name ) { Tag result; for (unsigned int i = 0 ; i < parent.childList.size(); ++i) { if ( parent.childList[i].name == name) { result = parent.childList[i]; break; } } return result; } static vector<Tag> GetTagListFromName( const Tag& parent, string name ) { vector<Tag> result; for (unsigned int i = 0 ; i < parent.childList.size(); ++i) { if ( parent.childList[i].name == name) { result.push_back( parent.childList[i] ); } } return result; } static string GetAttribute( const Tag& tag, string name, string defaultValue = "" ) { string result = defaultValue; if (tag.attribute.find(name) != tag.attribute.end()) { result = tag.attribute.find(name)->second; } return result; } private: ///// タグの種類(読み取り中専用) enum PURE_TAG_TYPE { NOT_PURE_TAG_TYPE, // 無効値 OPEN, // 開始タグ <***> CLOSE, // 閉じタグ </***> EMPTY, // 空要素タグ <***/> COMMENT, // コメント <!-- *** --> PROCESS, // 処理命令 <?***?> Processing Instruction DOCTYPE, // DOCTYPE宣言 <!DOCTYPE ****/> VALUE // 値 上記以外(タグではない) }; //========================================================= // ファイルを読み込んで文字列にする //========================================================= string ReadFile_toString(string fileName); //========================================================= // 文字列からタグっぽい箇所を抽出 //========================================================= vector<string> Parse_PureTagList(string str); //========================================================= // タグの種類を判別する //========================================================= PURE_TAG_TYPE JadgePureTag( string pureTag ); //========================================================= // 属性を解釈 //========================================================= map<string, string> GetAttribute(string str); //========================================================= // タグとしての情報を取得する //========================================================= Tag GetTagInfo(string str, PURE_TAG_TYPE type); //========================================================= // タグ間の構造を解釈 //========================================================= vector<Tag> Parse(vector<string> pureTagList); //========================================================= // 前後Trim //【引数】 // string //【戻り値】 // 前後の半角スペース、\n、\r、\t を取り除いた文字列 //========================================================= string Trim(const string &str); }; #endif
//========================================================= // // 簡易XMLパーサークラス(実装) // //========================================================= #include "NotXML.h" #include <stdio.h> #include <fstream> #include <vector> #include <stack> using namespace std; //========================================================= // 読み込む //========================================================= vector<NotXML::Tag> NotXML::Read(string fileName) { // 戻り値となる変数を用意 vector<Tag> result; // 指定されたファイルを読み込んで、中身を string型で持つ string fileContent = ReadFile_toString( fileName ); // 「<」「>」で囲われた要素のリストを取り出す vector<string> pureTagList = Parse_PureTagList(fileContent); // タグの開始・閉じ、入れ子構造などを読み取って出来上がり result = Parse(pureTagList); // 召し上がれー return result; } //========================================================= // 読み込む // 最上位要素を一個だけ // ルート要素を持たせている場合はこっちを呼ぶと楽かも。 //========================================================= NotXML::Tag NotXML::ReadRoot(string fileName) { /* Read() に丸投げして、結果から一番最初の要素を取り出しているだけです。 */ Tag result; vector<Tag> rootList = Read(fileName); if (rootList.size() > 0) { result = rootList[0]; } return result; } //========================================================= // ファイルを読み込んで文字列にする //========================================================= string NotXML::ReadFile_toString(string fileName) { string result = ""; bool isFirstRow = true; std::ifstream ifs; ifs.open( fileName.c_str() ); string row; while ( getline(ifs, row) ) { if (!isFirstRow) { result += '\n'; } result += row; isFirstRow = false; } ifs.close(); return result; } //========================================================= // 文字列からタグっぽい箇所を抽出 //========================================================= vector<string> NotXML::Parse_PureTagList(string str) { vector<string> result; string temp = ""; for (unsigned int i = 0; i < str.size(); ++i) { char c = str[i]; // 別のタグが始まるならば if (c == '<') { // 現在の状態で保存 if (temp != "") { result.push_back(temp); temp = ""; } } temp += c; // 閉じましたか? if (c == '>') { // 現在の状態で保存 result.push_back(temp); temp = ""; } /* ファイル末尾に余った文字列は無視されるのですね。 どのタグからも閉じられていない値ということで。 */ } return result; } //========================================================= // タグの種類を判別する //========================================================= NotXML::PURE_TAG_TYPE NotXML::JadgePureTag( string pureTag ) { PURE_TAG_TYPE type = NOT_PURE_TAG_TYPE; /* ・開始タグ :開始が「<」 、末尾が「>」である。 ・閉じタグ :開始が「</」 、末尾が「>」である。 ・自己完結タグ:開始が「<」 、末尾が「/>」である。 ・コメント :開始が「<!--」、末尾が「-->」である。 ・処理命令 :開始が「<?」 、末尾が「?>」である。 ・DOCTYPE宣言 :開始が「<!DOCTYPE」、末尾が「/>」である。 ・値 :「<」~「>」ではない生の文字列 ・なんでもない:開始が「</」 、末尾が「/>」である */ do { ///// コメント? string commentStart = pureTag.substr(0, 4); string commentEnd = (pureTag.size() < 3) ? "" : pureTag.substr( pureTag.size() - 3); if (commentStart == "<!--" && commentEnd == "-->") { type = COMMENT; break; } string closeTagStart = pureTag.substr(0, 2); string nonValueTagEnd = (pureTag.size() < 2) ? "" : pureTag.substr( pureTag.size() - 2); ///// こんなのはおかしい! if (closeTagStart == "</" && nonValueTagEnd == "/>") { type = NOT_PURE_TAG_TYPE; break; } string processStart = pureTag.substr(0, 2); string processEnd = (pureTag.size() < 2) ? "" : pureTag.substr( pureTag.size() - 2); ///// 処理命令? if (processStart == "<?" && processEnd == "?>") { type = PROCESS; break; } string docTypeStart = pureTag.substr(0, 9); ///// DOCTYPE宣言? if (docTypeStart == "<!DOCTYPE" && nonValueTagEnd == "/>") { type = DOCTYPE; break; } string nomalTagStart = pureTag.substr(0, 1); string nomalTagEnd = (pureTag.size() < 1) ? "" : pureTag.substr( pureTag.size() - 1); ///// 自己完結タグ? if (nomalTagStart == "<" && nonValueTagEnd == "/>") { type = EMPTY; break; } ///// 閉じタグ? if (closeTagStart == "</" && nomalTagEnd == ">") { type = CLOSE; break; } ///// 開始タグ? if (nomalTagStart == "<" && nomalTagEnd == ">") { type = OPEN; break; } ///// 値? if (type == NOT_PURE_TAG_TYPE) { type = VALUE; } } while(false); //printf("%s:%d\n", pureTag.c_str(), type); return type; } //========================================================= // 属性を解釈 //========================================================= map<string, string> NotXML::GetAttribute(string str) { map<string, string> result; // 現在、何を読み取っている最中なのか? string currentParseMode; string PARSE_MODE_KEY = "key"; string PARSE_MOSE_VALUE = "value"; currentParseMode = PARSE_MODE_KEY; unsigned int index = 0; string tempKey = ""; string tempValue = ""; bool isStartedAddValue = false; while ( index < str.size() ) { ///// キーを読み取っているところです。 if ( currentParseMode == PARSE_MODE_KEY ) { // 「=」が出たら、キー終了 if (str[index] == '=') { currentParseMode = PARSE_MOSE_VALUE; ++index; continue; } // キーに文字を追加していく tempKey += str[index]; ///// 値を読み取っているところです } else if ( currentParseMode == PARSE_MOSE_VALUE ) { // まだ値に文字を追加し始めていない if (!isStartedAddValue) { // 空白じゃないものが出た if ( !isspace( str[index])) { isStartedAddValue = true; } } // 値に文字を追加し始めているところ if (isStartedAddValue) { // 終了判定の微妙さ。現在の文字は追加していいの? bool isAddCurrentChar = true; // クォーテーションで囲んで終了した? bool isEndByQuort = false; // 終了判定 if ( tempValue.size() > 0 ) { // 値の最初の文字が クォーテーションである場合 if ( tempValue[0] == '"' || tempValue[0] == '\'' ) { // クォーテーションが現れたら、値終了 if ( str[index] == '"' || str[index] == '\"' ) { // エスケープされているか? bool isEscape = false; if ( tempValue.size() > 1 && tempValue[ tempValue.size() - 1] == '\\' ) { isEscape = true; // しかし「\」記号がエスケープされていたら判定は覆る。 if ( tempValue.size() > 2 && tempValue[ tempValue.size() - 2] == '\\' ) { isEscape = false; } } // エスケープされてないなら値終了 if (!isEscape) { currentParseMode = PARSE_MODE_KEY; isStartedAddValue = false; isAddCurrentChar = true; isEndByQuort = true; } } // 値の最初の文字が クォーテーションではない場合 } else { // 空白が現れたら終了 if ( isspace( str[index] ) ) { currentParseMode = PARSE_MODE_KEY; isStartedAddValue = false; isAddCurrentChar = false; } // 最後まで達していても終了 if ( index == str.size() - 1 ) { currentParseMode = PARSE_MODE_KEY; isStartedAddValue = false; isAddCurrentChar = true; } } } // if 値読み込み 終了判定 // 値に文字を追加していく if (isAddCurrentChar) { tempValue += str[index]; } // 値読み込みが終了している場合 if (currentParseMode != PARSE_MOSE_VALUE) { // 値が空白じゃないなら、加工する if (tempValue != "") { // 値の前後のクォーテーションを外す if ( isEndByQuort) { tempValue = tempValue.substr( 1, tempValue.size() - 2 ); // エスケープ記号を外す /* \" → " \' → ' \\ → \ \\" → ??? \\\ → ??? あまり中途半端に気を利かさない方がいい? そんな気がしてきました。 よし。処理しません。 */ } } // キーの前後はトリムする tempKey = Trim( tempKey ); // キーと値のペアを属性に追加する string key = tempKey; string value = tempValue; result.insert( pair<string, string>( key, value ) ); tempKey = ""; tempValue = ""; } } // if 値に文字を追加し始めているところ } ++index; }; return result; } //========================================================= // タグとしての情報を取得する //========================================================= NotXML::Tag NotXML::GetTagInfo(string str, PURE_TAG_TYPE type) { Tag result; ///// 「<」「>」を外した中身部分 string content = ""; // 開始タグです。<***> if ( type == OPEN ) { content = str.substr(1, str.size() - 2); // 閉じタグです。</***> } else if ( type == CLOSE ) { content = str.substr(2, str.size() - 3); // 空要素タグです。<***/> } else if ( type == EMPTY ) { content = str.substr(1, str.size() - 3); // 処理命令タグです。<?***?> } else if ( type == PROCESS ) { content = str.substr(2, str.size() - 4); // DOCTYPE宣言です。<!DOCTYPE ***/> } else if ( type == DOCTYPE ) { content = str.substr(9, str.size() - 11); ///// DOCTYPEの場合は特殊。 result.name = ""; result.value = content; return result; } // タグ名終了時点のインデックス int tagNameEndIndex = -1; // タグ名を取り出す for (unsigned int i = 0; i < content.size(); ++i) { // 空白じゃないなら、タグ名として文字を追加していく if (!isspace( content[i] )) { result.name += content[i]; // 空白が出た場合 } else { // すでにタグ名を認識し始めていたなら、終了 if (result.name != "") { // タグ名終了時点のインデックスを覚えておく。 tagNameEndIndex = i; // 終了 break; // まだタグ名を認識し始めていないなら、続行 } else { // 続行 } } } // タグ名以降の部分から属性を取り出す string attributeString = (tagNameEndIndex > 0) ? content.substr( tagNameEndIndex ) : ""; result.attribute = GetAttribute(attributeString); return result; } //========================================================= // タグ間の構造を解釈 //========================================================= vector<NotXML::Tag> NotXML::Parse(vector<string> pureTagList) { ///// 根元タグリスト vector<Tag> rootTagList; ///// 開き中のタグ stack<Tag> tagStack; for (unsigned int i = 0; i < pureTagList.size(); ++i) { string pureTag = pureTagList[i]; // 分類する PURE_TAG_TYPE type = JadgePureTag( pureTag ); // 開始タグでした if ( type == OPEN ) { // タグの名前とかを取得して、 Tag tag = GetTagInfo( pureTag, type); // スタックに乗せる tagStack.push( tag ); // 閉じタグでした } else if ( type == CLOSE ) { // タグの名前とかを取得 Tag tag = GetTagInfo( pureTag, type); // 開き中のタグがありますよね? if (tagStack.size() > 0 ) { // 開き中のタグと、名前が一致してますよね? if ( tagStack.top().name == tag.name ) { // タグを取り出して、閉じる。 Tag closedTag = tagStack.top(); tagStack.pop(); // そして、開き中のタグがありますか? if ( tagStack.size() > 0 ) { // 開き中のタグの子要素として追加する tagStack.top().childList.push_back( closedTag ); } else { // 最上位タグのリストに追加する rootTagList.push_back( closedTag ); } // え、一致してないの? それは困りましたね。 } else { printf("閉じタグに対して、開き中のタグがない:%s\n", pureTag.c_str()); } // え、ないの? それは困りましたね。 } else { printf("閉じタグに対して、開き中のタグがない:%s\n", pureTag.c_str()); } // 空要素タグでした } else if ( type == EMPTY ) { // タグの名前とかを取得して、 Tag tag = GetTagInfo( pureTag, type); // 一応念を押して要素を空にしておく。 tag.value = ""; // 開き中のタグがありますか? if ( tagStack.size() > 0 ) { // 開いているタグの子要素として追加する tagStack.top().childList.push_back( tag ); // 開き中のタグがないならば、 } else { // 最上位タグのリストに追加する rootTagList.push_back( tag ); } // 値でした } else if ( type == VALUE ) { // 開き中のタグがあるはずなのです。 if ( tagStack.size() > 0 ) { // この次には閉じタグが来るはず… tagStack.top().value += pureTag; // え、開き中のタグがないの? } else { //printf("開き中のタグがない・値:%s\n", pureTag.c_str()); } // コメントでした } else if ( type == COMMENT ) { // 無視 // 処理命令でした } else if ( type == PROCESS ) { // 無視してしまいます。 // DOCTYPE宣言でした } else if ( type == DOCTYPE ) { // 無視してしまいます。 // 無効値でした } else if ( type == NOT_PURE_TAG_TYPE ) { // 無視 } } ///// 根元タグリスト rootTagList; ///// 開き中のタグ tagStack; return rootTagList; } //========================================================= // Trim //【引数】 // string //【戻り値】 // 前後のホワイトスペースを取り除いた文字列 //========================================================= string NotXML::Trim(const string &str){ if (str == "") { return ""; } string result = ""; unsigned int firstIndex = 0; unsigned int lastIndex = str.length() - 1; // 開始位置を見つける while (true) { // 全部空白 if ( firstIndex >= str.length() ) { return ""; } // こんなんでいいの? if( str[firstIndex] == ' ' || str[firstIndex] == '\r' || str[firstIndex] == '\n' || str[firstIndex] == '\t'){ ++firstIndex; } else { break; } } // 終了位置を見つける while (true) { // 再びこんなんでいいの? if( lastIndex > 0 && str[lastIndex] == ' ' || str[lastIndex] == '\r' || str[lastIndex] == '\n' || str[lastIndex] == '\t'){ --lastIndex; } else { break; } } // 開始位置から終了位置までを取り出す for ( unsigned int i = firstIndex; i <= lastIndex; ++i ) { result += str[i]; } return result; }ソースをコピーして、アナタのアプリケーションに混ぜてあげてね。
#include "NotXML.h" int main () { ///// ファイルを読み込む vector <NotXML::Tag> tagList = NotXML().Read( "yourStructuredDocumentFile.txt" ); for ( unsigned int i = 0; i < tagList.size(); ++i ) { // タグ名="TAG_A" ? if ( tagList[i].name == "TAG_A") { printf ("値 : %s\n", tagList[i].value.c_str() ); } // タグ名="TAG_B" ? else if (tagList[i].name == "TAG_B") { printf ("値 : %s\n", tagList[i].value.c_str() ); } } return 0; }ファイル名を指定して Read関数を実行すると、Tag構造体のリストが取得できます。