Xerces/Xalan-C++ で XML

Xerces-C++ で日本語を使うまで

最終更新: 2015-03-31 (火) 22:05:09 (1359d)

概要

このページで説明するコード

注意

unsigned int 型となっている箇所を XMLSize_t に変更した。

これは Xerces-C++ 3.0.0 以降のリリースに影響しての変更である。

Xerces-C++ 2 系の利用者は、XMLSize_t を unsigned int と読み替えること。

XMLSize_t 型について調査した結果は以下のブログ記事にて言及している

fileフルセット(VC++ 6.0/Xerces-C++ 2 系用)では unsigned int を用いているが、それ以外のファイルは XMLSize_t を利用している。

日本語のファイルを読み込ませて見よう

SAXで単純なプログラミングで紹介したプログラムに 日本語を含む XML ファイル(fileexample.xml)を読み込ませて見よう。 すると正常に日本語を表示してくれない。 これは XMLString::transcode メソッドが適切なエンコーディング、 すなわち Shift_JIS でエンコードしてくれなかったことによる。 Shift_JIS でエンコードされた文字列じゃないと画面には正常に表示されないのだ。

Xerces-C++ では Shift_JIS にエンコードする方法は無いのか? というとそんなことは全く無く SAXPrint などのサンプルでは Shift_JIS のエンコードを指定することができ、 以下のコマンドを入力することで正常に表示されるのが確認できるだろう。

SAXPrint -x=Shift_JIS example.xml

XMLTranscoderクラスを使おう

XMLTranscoderを生成する

今まで XMLCh 型から char 型への変換に XMLString::transcode メソッドを使っていたが、 XMLCh 型からShift_JIS にエンコードされたchar 型への変換には XMLTranscoder クラスを使う。 XMLTranscoder クラスを生成しているコードとして、 fileSampleHandler.cpp の一部を以下に示す。

  // トランスコーダを生成する
  const XMLSize_t bufferSize=1024;
  XMLTransService::Codes failReason;
  XMLTranscoder* transcoder=XMLPlatformUtils::fgTransService->makeNewTranscoderFor(
    "Shift_JIS", failReason, bufferSize, XMLPlatformUtils::fgMemoryManager
    );

  // XMLTranscoderを使う ...

  delete transcoder;

XMLTranscoder を生成するためには XMLPlatformUtils::fgTransService クラスの makeNewTranscoderFor メソッドを使う。 使い終わったらちゃんと XMLTranscoder を delete することを忘れてはいけない。

バッファを確保する

XMLTranscoder を使った XMLCh 型から char 型への変換は、 XMLString::transcode メソッドのように入れただけの文字列が全て変換された結果が返ってくるわけではない。 XMLTranscoder の処理には限界があり、ある一定の長さの文字列しか処理することはできない。 例えば 94 文字の文字列を変換したいとして、XMLTranscoder が一回で処理できる文字列の長さが 10 文字だったとすると、

10 + 10 + 10 + 10 + 10 + 10 + 10 + 10 + 10 + 4

という感じに 10 回の処理に分けて与えられた文字列を変換しなければならない。 今回は XMLTranscoder が一回に処理できる文字列の長さを 1024 文字とした。 XMLTranscoder が文字列を変換しているコードとして、 fileSampleHandler.cpp の一部を以下に示す。

  // 変数を初期化する
  const XMLCh* srcPtr=toTranscode;
  const XMLCh* const endPtr=toTranscode+XMLString::stringLen(toTranscode);
  char* result=new char[1];result[0]='\0';

  // トランスコードを行う
  while(srcPtr < endPtr) {
    XMLByte buffer[bufferSize+4];
    const XMLSize_t srcCount=endPtr-srcPtr;
    const XMLSize_t srcChars=srcCount > bufferSize ? bufferSize : srcCount;

    // bufferを使って少しずつトランスコードを行う
    XMLSize_t charsEaten;
    const XMLSize_t outBytes=transcoder->transcodeTo(
      srcPtr, srcChars, buffer, bufferSize, charsEaten, XMLTranscoder::UnRep_Throw
      );
    if(outBytes) {
      buffer[outBytes]=buffer[outBytes+1]=buffer[outBytes+2]=buffer[outBytes+3]=0;
    }

    // トランスコード結果を少しずつ積んでいく
    srcPtr+=charsEaten;
    const XMLSize_t newSize=xercesc::XMLString::stringLen(result)+
      xercesc::XMLString::stringLen((char*)buffer);
    char* newBuffer=new char[newSize+1];
    xercesc::XMLString::copyString(newBuffer, result);
    xercesc::XMLString::catString(newBuffer, (char*)buffer);
    delete[] result;
    result=newBuffer;
  }

srcPtr がこれから XMLTranscoder を使って変換する XMLCh 型の文字列へのポインタを、 srcChars 変数がこれから XMLTranscoder を使って変換する文字列の長さを示している。 一気に処理できる文字列長には限界があるため、bufferSize を上限としていることがわかると思う。

XMLTranscoder を使って XMLCh 型から Shift_JIS にエンコードされた char 型を取得したら、 後は今までに得られた変換結果と今得られた変換結果とを繋げて一つの文字列にしている。

SampleHandler::transcode メソッドを定義する

以上のコードを SampleHandler::transcode メソッドとして定義し、 XMLString::transcode メソッドの代わりに使えるようにする。

新しい transcode メソッドで日本語を表示しよう

XMLString::transcode メソッドの代わりに新しく定義した SampleHandler::transcode メソッドを使った例として、 fileSampleHandler.cpp の一部を以下に示す。

void SampleHandler::startElement(const XMLCh* const uri, const XMLCh* const localname,
                                 const XMLCh* const qname, const Attributes& attrs) {
  char* name=transcode(localname);
  cout << "start  : " << name << endl;
  delete[] name;
}

XMLString::transcode メソッドの代わりに transcode メソッドを、XMLString::release メソッドの代わりに delete を使う。 これで日本語がちゃんと表示されるようになったはずだ。

コメント

コメントはありません。 コメント/xerces/use/japanese

お名前: