XMLのパース
お次は得られたXMLのパース(解析)、<word>要素の"en"/"ja"属性を抽出します。XMLパーサは定番Apache Xerces。Xercesはメモリ上の文字列をパースできるのでおあつらえ向きです。
Apache Xercesのダウンロードページからxerces-c_3.1.1のバイナリ版をダウンロードし、include/libパスを通して…と、ここで問題が。POP3からテキストメールを取り出すと、多くの場合日本語はiso-2022-jp(いわゆるJIS)が使われています。なればこそデコードの手間を省くためXMLの文字コードも<?xml ... encoding='iso-2022-jp' ?>としたのですが、Xercesのバイナリ版ではiso-2022-jpに対応していないためにパース時にエラー(例外)となります。幸いなことにXercesをソースからビルドすればICUを文字コードの変換に使ってくれます。ICUは巷で使われているおよそありとあらゆる文字コードとUNICODEとの相互変換をやってくれる優れモノ。ICUのプロジェクトページからバイナリ版ICUをダウンロードし、ソースコード版xerces-c_3.1.1のVC++用ソリューションprojects/Win32/VC10/xerces-allを開けて、プロジェクト:XercesLibのプロパティにICUのinclude/libパスを設定して"ICU Release"をビルドすれば、Build/Win32/VC10/ICU Releaseにlibとdllが生成されます。
Xercesはイベント駆動型のSAXパーサと木構造を構築するDOMパーサをサポートしています。今回はDOMパーサを使って<word>要素の"en"/"ja"属性値を抽出します。
#include <iostream>
#include <fstream>
#include <locale>
#include <string>
#include <vector>
#include <tuple>
#include <iterator>
#include <algorithm>
// Xerces
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/SAXException.hpp>
using namespace std;
using namespace xercesc;
typedef tuple<wstring,wstring> word_type;
template<typename InputIterator, typename OutputIterator>
OutputIterator extract_words(InputIterator first, InputIterator last, OutputIterator out) {
while( first != last ) {
try {
XercesDOMParser parser;
MemBufInputSource source(reinterpret_cast<const XMLByte*>(first->data()), first->size(), "in_memory");
parser.parse(source);
DOMDocument* doc = parser.getDocument();
DOMElement* root = doc->getDocumentElement();
// <word> 要素の "en","ja"属性値を取り出す
DOMNodeList* words = root->getElementsByTagName(L"word");
for ( XMLSize_t i = 0; i < words->getLength(); ++i ) {
DOMElement* word = static_cast<DOMElement*>(words->item(i));
*out++ = word_type(word->getAttribute(L"en"),word->getAttribute(L"ja"));
}
} catch ( const SAXException& ex ) {
wcerr << ex.getMessage() << endl;
} catch ( const XMLException& ex ) {
wcerr << ex.getMessage() << endl;
} catch ( const DOMException& ex ) {
wcerr << ex.getMessage() << endl;
}
++first;
}
return out;
}
int main() {
locale ja("japanese");
wcerr.imbue(ja);
wcout.imbue(ja);
try {
XMLPlatformUtils::Initialize();
} catch ( const XMLException& ex ) {
wcerr << ex.getMessage();
return 1;
}
vector<string> msgs;
ifstream stream("fruit.xml");
string msg;
copy(istreambuf_iterator<char>(stream), istreambuf_iterator<char>(), back_inserter(msg));
msgs.push_back(msg);
vector<word_type> words;
extract_words(begin(msgs), end(msgs), back_inserter(words));
wcout << L"----------- I'found these words in the XMLs ------------------" << endl;
for_each(begin(words), end(words),
[](const word_type& word) { wcout << get<0>(word) << L',' << get<1>(word) << endl; });
XMLPlatformUtils::Terminate();
}
