ICU C/C++ Unicode Library

Конкорданс к тексту А. С. Пушкина プーシキン全集見出語コンコーダンスの KWIC コンテキスト(キーワード前後のテクスト)出力様式を見直している。現行仕様ではキーワードを含む前後三詩行(散文の場合はパラグラフ)を出力しており,長大になることが多く,指定した文字数でこれを切り詰めて出力できるようにする。

このプログラムを開発したとき,Boost/Regex 正規表現ライブラリや Wt Web C++ Toolkit の WString クラスの扱いを通して C/C++ での Unicode 文字列操作を一通り試したように思っていたが,もう開発からすでに一年以上経過してしまい,例によって,ことごとくノウハウを失念してしまっていた。今回は Boost ライブラリの Unicode 基盤を支えている ICU C/C++ Unicode ライブラリを試してみた。備忘録を残しておく。

Unicode は最近では UTF-8 で符号化する場合が多いと思う。UTF-8 Unicode は文字によって長さが可変であり,いわゆる英数字は ASCII と同じ 1 オクテット(バイト),アクセント付きラテン文字,キリル文字,ギリシア文字など多くの言語では 2 オクテット,漢字は原則 3 オクテット。可変長だということは,文字列の文字数をカウントする,文字列の一部分を取り出す,等々の扱いにおいてきわめて面倒で,スクラップからプログラムを書くとなると,いちいち当該 Unicode 文字の長さを判定しながら文字配列を走査しなければならないわけである。ICU ライブラリの UnicodeString クラスを使うとこのあたりがきわめてすっきりする。

icu::UnicodeString クラスには,文字数(バイトサイズではなく)を返す length() メソッドや,substr() に相当する tempSubString() メソッドなどが用意されている。文字列を走査するためのイテレータ(オブジェクト配列反復子)icu::StringCharacterIterator もある。プーシキン・コンコーダンス・プログラム改変にはこれら機能でとりあえず十分である。これらを試験するコードを以下に掲げる。ソースファイルは UTF-8 エンコードである。

/* -*- coding: utf-8; mode: c++; -*-
   ICU Unicode 文字操作 utest.cpp
 */
#include <iostream>
#include <unicode/ustream.h>
#include <unicode/schriter.h>
 
int main(int argc, char** argv)
{
    // Unicode文字列の文字数カウントと標準出力
    icu::UnicodeString us = icu::UnicodeString::fromUTF8("Ж鷗ZßÇῇž");
    int32_t len = us.length(); // 物理的長さではなく文字数を返す
    std::cout << "Text: |" << us << "| Length: " << len << std::endl;
 
    // iteratorで1文字ずつ処理
    // next()メソッドで文字長を意識せずに次の文字を取得できる
    typedef icu::StringCharacterIterator USIterator;
    USIterator it(us);
    for (UChar uc = it.first(); uc != USIterator::DONE; uc = it.next()) {
        // UChar出力はUnicodeStringにしてから行う
        std::cout << "Char: " << icu::UnicodeString(uc) << std::endl;
    }
 
    // 文字列の抽出 substring
    // - icu::UnicodeString::tempSubString(start_pos, length);
    // - icu::UnicodeString::tempSubStringBetween(start_pos, limit_pos);
    // 先頭から2文字取り出す
    icu::UnicodeString ust = us.tempSubString(0, 2);
    std::cout << "trim forward: |" << ust << "|" << std::endl;
    // 後方から5文字取り出す
    ust = us.tempSubString((len - 5), 5);
    std::cout << "trim reverse: |" << ust << "|" << std::endl;
 
    return 0;
}

unicode/ustream.h ヘッダをインクルードすれば,標準出力 std::couticu::UnicodeString を印字できるようになる。icu::UnicodeString クラスそのものは unicode/unistr.h ヘッダファイルのなかで定義されているが,unicode/ustream.h からもインクルードされるので,ここではソースファイルにしるす必要はない。unicode/schriter.h ヘッダファイルはイテレータのために必要である。コンパイルは,この場合,libicuuclibicuio をリンクすればよい。ICU ライブラリ API の詳細は,ICU ドキュメントを参照のこと。

% g++ -g -I/usr/local/include -L/usr/local/lib utest.cpp -o utest \
  -licuuc -licuio

実行結果は以下のとおり。

isolde:/Users/isao[2017] % g++ -g -I/usr/local/include -L/usr/local/lib \
  utest.cpp -o utest -licuuc -licuio
isolde:/Users/isao[2018] % ./utest
Text: |Ж鷗ZßÇῇž| Length: 7
Char: Ж
Char: 鷗
Char: Z
Char: ß
Char: Ç
Char: ῇ
Char: ž
trim forward: |Ж鷗|
trim reverse: |ZßÇῇž|
isolde:/Users/isao[2019] % 

キリル文字,複式アクセント付きギリシア文字,ラテン文字(Latin-1, Latin-2),漢字,ASCII 文字を含む文字列例において,文字数カウント,1 文字ずつの取り出し,文字列の抽出が,このように,簡単にできる。Boost と併用すれば,正規表現操作もたやすい。

ICU ライブラリのインストール,Unicode 正規表現については,それぞれ,「Boost C++ Library インストール」,「Boost.Regex ロシア語正規表現」にしるしたのでそちらも参照いただきたい。