Tools.h++ 7.0 ユーザーズガイド: 2 - 文字列クラスの使用

Copyright 1999 Rogue Wave Software
Copyright 1999 Sun Microsystems, Inc.

# 文字列クラスの使用 | # 2 |

文字列の操作は、開発者にとって最も一般的で、しかも誤りやすい作業の 1 つです。Tools.h++ クラスの RWCString と RWWString は、文字列の作成、操作、削除を簡単に行なうために必要なコンストラクタ、演算子、メンバー関数を提供します。

クラス RWCString のメンバー関数は、RWCString オブジェクトと `char*` の読み取り、比較、格納、復元、連結、付加、追加を行ないます。その演算子を使用すると、境界検査付き、または境界検査なしで各文字にアクセスすることができます。また、クラスは自動的にメモリー管理を行ないます。ユーザーが、文字列の文字の記憶領域を作成または削除する必要はありません。

クラス RWWString は RWCString に似ていますが、RWWString はワイド文字を処理する点が異なります。この 2 つのクラスのインタフェースは似ているため、簡単に交換することができます。これらのクラスの詳細は、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』に説明してあります。この章では、RWCString の動作を示すいくつかの一般的な例をあげて、次に文字列クラスの一部の機能を説明します。

## 一般的な例

次の例では、文字列クラスの基本的な機能をいくつか呼び出しています。ここでは、あるマニュアル中の古いバージョン番号を新しいバージョン番号に置換するために RWCString がとる手順が示されています。

    #include <rw/cstring.h>
    #include <rw/regexp.h>
    #include <rw/rstream.h>

    main(){
      RWCString a;                     //1 文字列オブジェクト a を作成する

      RWCRegexp re("V[0-9]\\.[0-9]+"); //2 正規表現を定義する
      while( a.readLine(cin) ){        //3 標準入力から a に読み込む
        a(re) = "V4.0";                //4 一致した表現を置換する
        cout << a << endl;
      }
      return 0;
    }

#### プログラムへの入力

    This text describes V1.2.  For more
    information see the file install.doc.
    The current version V1.2 implements...

#### プログラムからの出力

    This text describes V4.0.  For more
    information see the file install.doc.
    The current version V4.0 implements...

ここに示すコードは、クラスの動作を説明しています。RWCString は、文字列オブジェクト a を作成し、標準入力から a に行を読み込んで、定義された正規表現の「`"V[0-9]\\.[0-9]+"`」に一致するパターンを検索します。一致するものは、`V0` から `V9` の間のバージョン番号になります。たとえば、`V1.2` と `V1.22` は一致しますが、`V12.3` は一致しません。見つかった一致は、文字列「`V4.0`」で置換されます。

この操作のポイントは次の式にあります。

      a(re) = "V4.0";

ここで、`()` は多重定義された演算子の例を示します。多重定義された演算子とは、コンテキストまたは引数に応じて、複数の関数を実行できる演算子のことをいいます。

この例では、関数呼び出し演算子の `RWCString::operator()` は、正規表現の型 RWCRegexp の引数をとるように多重定義されています。演算子は、正規表現に一致する範囲を示す部分文字列か、または一致する表現が見つからない場合はヌルの部分文字列を返します。するとプログラムは、部分文字列代入演算子を呼び出して区切られた文字列を右辺の内容で置換します。ヌルの部分文字列の場合は何もしません。Tools.h++ には多重定義された演算子があるため、定義された正規表現の検索と置換を 1 つの行だけで行うことができます。

特殊文字「`.`」が、小数点として文字どおり読まれるようにするには、「`V[0-9]\\.[0-9]+`」に 2 つのバックスラッシュが必要なことがわかります。これは、コンパイラがリテラル文字列を評価するときに、バックスラッシュを 1 つ削除するためです。残ったバックスラッシュは、正規表現の評価ルーチンに対して、後に続く文字すべてを文字どおり読むように指示するものです。

次の例では、RWCString は、別の多重定義された演算子 + を使用して、文字列 `s1` と `s2` を連結しています。`toUpper` メンバー関数で文字列を小文字から大文字に変換し、その結果を `cout` に送ります。

      RWCString s1, s2;
      cin >> s1 >> s2;
      cout << toUpper(s1+s2);

文字列クラスの詳細については、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』を参照してください。

## 照合

ある辞書を照合する場合は、RWCString の照合演算子が便利です。これらの演算子は次のとおりです。

      RWBoolean operator==(const RWCString&, const RWCString&);
      RWBoolean operator!=(const RWCString&, const RWCString&);
      RWBoolean operator< (const RWCString&, const RWCString&);
      RWBoolean operator<=(const RWCString&, const RWCString&);
      RWBoolean operator> (const RWCString&, const RWCString&);
      RWBoolean operator>=(const RWCString&, const RWCString&);

これらの演算子は大文字小文字を区別します。大文字小文字を区別しないで比較を行ないたい場合は、次のメンバー関数を使用することができます。

      int RWCString::compareTo(const RWCString& str,
                               caseCompare = RWCString::exact) const;

この関数は、`str` が自己と比較して辞書的に小さいか、等しいか、大きいかによって、ゼロ未満、ゼロ、またはゼロよりも大きい整数を返します。型 `caseCompare` は次の値をとる enum です。

  `exact` |  | 大文字小文字を区別する
  `ignoreCase` | 大文字小文字を区別しない

省略時の設定は "`exact`" であり、これは論理演算子の `==` や、`!=` などと同じ結果をもたらします。

ロケール固有の文字列照合には、次のメンバー関数を使用します。

        int RWCString::collate(const RWCString& str) const;

この関数は標準 C ライブラリ関数 `strcoll()` をカプセル化したものです。この関数は、標準 C ライブラリ関数 `setlocale()` のカテゴリ `LC_COLLATE` で設定されたロケール固有の照合規約に従って計算された結果を返します。これは比較的時間のかかる計算なので、次の大域関数を使用して文字列を事前に変換し、その結果に対して`compareTo()` または論理演算子 (`==`、`!=` など) の 1 つを使用する方がよいでしょう。

        RWCString strXForm(const RWCString&);

RWCString については、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』の項目を参照してください。関数 `strxForm` は、関連する大域関数に示しています。

## 部分文字列

独立した RWCSubString クラスは、部分文字列抽出と変更をサポートしています。公開コンストラクタはありません。RWCSubString は、RWCString の各種のメンバー関数によって間接的に作成され、機会があればすぐに破棄されます。

さまざまな状況で部分文字列を使用することができます。たとえば、`RWCString::operator()` で部分文字列を作成して、それを使用して RWCString を初期化することができます。

      RWCString s("this is a string");
      // 部分文字列から RWCString を作成する
      RWCString s2 = s(0, 4);                               // "this"

この結果、s の最初の 4 文字を含む文字列 s2 が作成されます。

RWSubString は、文字列あるいは RWCString や RWCSubString への代入の中で左辺値として使用することもできます。

        // RWCString を作成する
        RWCString article("the");
        RWCString s("this is a string");
        s(0, 4) = "that";                        // "that is a string"
        s(8, 1) = article;                     // "that is the string"

部分文字列への代入は文字列の長さを保存する演算ではありません。代入演算子の両辺の文字数が同じである必要はありません。

## パターン照合

クラス RWCString は文字列検索に便利なインタフェースをサポートします。以下にその例を示します。

        RWCString s("curiouser and curiouser.");
        size_t i = s.index("curious");

このコードの断片は、`s` の中から "`curious`" の最初の出現の先頭を探します。この比較では大文字と小文字が区別されます。結果として、`i` が 0 に設定されます。次の出現のインデックスを見つけるには、次のように記述します。

        i = s.index("curious", ++i);

この結果、`i` が 14 に設定されます。大文字と小文字を区別しない比較の場合は、次のように記述します。

        RWCString s("Curiouser and curiouser.");
        size_t i = s.index("curious", 0, RWCString::ignoreCase);

この結果、`i` が 0 に設定されます。

パターンが文字列中になければ、`index()` は `RW_NPOS` という特別な値を返します。

### 単純な正規表現

パターン照合機能の一部として、Tools.h++ クラスライブラリは、正規表現の検索をサポートしています。正規表現の構文の詳細については、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』の「RWCRegexp」を参照してください。正規表現を使用すると、部分文字列を返すことができます。たとえば、次に、すべての Windows メッセージ (接頭辞 `WM_`) を照合する方法を示します。

    #include <rw/cstring.h>
    #include <rw/regexp.h>
    #include <rw/rstream.h>

    main(){
      RWCString a("A message named WM_CREATE");

      // 正規表現を作成して Windows メッセージを照合する
      RWCRegexp re("WM_[A-Z]*");
      cout << a(re) << endl;

      return 0;
    }

#### プログラムからの出力

    WM_CREATE

RWCString の関数呼び出し演算子は、RWCRegexp 型の引数をとるように多重定義されています。これは指定された式と一致する RWCSubString を返すか、そのような式がなければヌルの部分文字列を返します。

### 拡張正規表現

Tools.h++ クラスライブラリのこのバージョンは、POSIX.2 にもとづく拡張正規表現検索をサポートしています (付録 D「参考文献」を参照)。拡張正規表現とは、UNIX ユーティリティの lex と awk で使用される正規表現のことです。正規表現の構文の詳細は、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』の「RWCRExpr」に記載されています。

> 注 - RWCRExpr は、コンパイラが例外処理と C++ 標準ライブラリをサポートしている場合にのみ使用できます。

拡張正規表現は、任意の長さにすることができますが、使用可能なメモリーによって制限されます。部分表現をグループ化するには丸かっこを使用し、「二者択一」正規表現を作成してパターンマッチングを行なうには記号 | を使用できます。

次の例は、拡張正規表現の機能の一部を示しています。

    #include "rw/rstream.h"
    #include "rw/re.h"

    main (){
      RWCRExpr  re("Lisa|Betty|Eliza");
      RWCString s("Betty II is the Queen of England.");

      s.replace(re, "Elizabeth");
      cout << s << endl;

      s = "Leg Leg Hurrah!";
      re = "Leg";
      s.replace(re, "Hip", RWCString::all);
      cout << s << endl;
    }

#### プログラムからの出力

    Elizabeth II is the Queen of England.
    Hip Hip Hurrah!

RWCString の関数呼び出し演算子は、型 RWCRExpr の引数をとるように多重定義されていることに注意してください。これは、指定された式と一致する RWCSubString を返すか、そのような式がなければヌルの部分文字列を返します。

## 文字列入出力

クラス RWCString は、iostream と Rogue Wave 仮想ストリームの両方に対する充実した入出力機能を備えています。

### iostream

標準の左シフト演算子と右シフト演算子は、iostream および RWCString を処理するように多重定義されています。

    ostream&    operator<<(ostream& stream, const RWCString& cstr);
    istream&    operator>>(istream& stream, RWCString& cstr);

この意味合いは次の演算子と一致します。

    ostream&    operator<<(ostream& stream, const char*);
    istream&    operator>>(istream& stream, char* p);

これらは、コンパイラに付属の C++ 標準ライブラリで定義されています。つまり、左シフト (`<<`) 演算子はヌル文字で終端している文字列を所定の出力ストリームに書き込みます。右シフト (`>>`) 演算子は、空白で区切られているトークンを入力ストリームから RWCString に読み込み、内容を置き換えます。

その他の関数では、RWCString 入力の微妙な調整が可能です1。関数 `readline()` では文字列を改行 (newline) で区切ることができます。またオプションのパラメータを持ち、文字を格納する前に空白をスキップするかどうかを制御することができます。次にこの例を示します。

    #include <rw/cstring.h>
    #include <iostream.h>
    #include <fstream.h>

    main()
    {
        RWCString line;

        {
            int count = 0;
            ifstream istr("testfile");

            while (line.readLine(istr))
            // デフォルト値を使用する: skipwhitespace (空白をスキップ)
                count++;
        cout << count << " lines, skipping whitespace.\n";
        }
        {
            int count = 0;
            ifstream istr("testfile");
            while (line.readLine(istr, FALSE))
            // NB: 空白をスキップしない
                count++;
            cout << count << " lines, not skipping whitespace.\n";
        }

        return 0;
    }

#### プログラムからの入力

    line 1

    line 5

#### プログラムからの出力

    2 lines, skipping whitespace.
    5 lines, not skipping whitespace.

### 仮想ストリーム

仮想ストリームに対する文字列演算子もサポートされています。

      Rwvistream&  operator>>(RWvistream& vstream, RWCString& cstr);
      Rwvostream&  operator<<(RWvostream& vstream,
                              const RWCString& cstr);

これにより、使用すべき書式を知らなくても文字列を保存したり復元することができます。仮想ストリームについての詳細は、第 16 章「エラー処理」を参照してください。

## トークナイザ

クラス RWCTokenizer を使用すると、適切な空白で区切られた複数のトークンに文字列を分割できます。次に例を示します。

    #include <rw/ctoken.h>
    #include <rw/cstring.h>
    #include <rw/rstream.h>

    main(){
      RWCString a("a string with five tokens");

      RWCTokenizer next(a);

      int i = 0;

      // 文字列が返されるまで前進する
      while( !next().isNull() ) i++;

      cout << i << endl;
      return 0;
    }

#### プログラムからの出力

    5

このプログラムは文字列中のトークンの個数を数えます。クラス RWCTokenizer の関数呼び出し演算子は、他の反復子と同様に "次のトークンに進み、それを RWCSubString として返す" ように多重定義されています。トークンがそれ以上存在しない場合は、ヌルの部分文字列を返します。クラス RWCSubString はメンバー関数 `isNull()` を持っていますが、この関数は部分文字列がヌルの部分文字列である場合に `TRUE` を返します。したがって、そこでループが終了します。詳細は『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』の「RWCTokenizer」を参照してください。

## 複数バイト文字列

クラス RWCString は、各種の文字を表わすために使用される複数バイト文字列を部分的にサポートしています (368 ページの「RWCString と RWWString による文字の地域化」を参照)。複数バイト文字は複数のバイトからなるため、バイト数による文字の長さが、文字列内の実際の文字数以上になる場合があります。

RWCString に複数バイト文字が含まれる場合は、文字数を調べるために、メンバー関数 `mbLength()` を使用する必要があります。これに対して、RWCString に複数バイト文字が含まれない場合、`lengh()` と `mbLengh()` の結果は同じになります。この場合は、`lengh()` の方が処理速度がはるかに速いため、これを使用できます。次に、複数バイト文字列 `Sun` を使用する例を示します。

      RWCString Sun("\306\374\315\313\306\374");
      cout << Sun.length();                               // "6" を出力
      cout << Sun.mbLength();                             // "3" を出力

`Sun` の中にある文字列は EUC (拡張 UNIX コード) の複数バイトのコードセットを使用して「日曜日」を漢字で示したものです。EUC では、1 つの文字が 1 〜 4 バイト長になります。この例では文字列 `Sun` は 6 バイトですが、実際の文字数は 3 文字です。

一般的には、複数バイト文字のうち第 2 またはそれ以降のバイトはヌル文字になることがあります。これは、文字列のバイト長が `strlen()` から渡される長さと必ずしも一致しないことを意味しています。内部的には RWCString は埋め込まれたヌルを受け付けないため 2、第 2 バイト以降の符号にヌル文字を使用する文字セットでも安全に使用することができます。また、`RWCString::data()` がヌル文字で終端する文字列を返すときは常に、その文字列の前の方にヌル文字があることも考えられますので注意してください。これらを次のプログラムにまとめます。

    #include <rw/cstring.h>
    #include <rw/rstream.h>
    #include <string.h>
    main() {
    RWCString a("abc");                                          // 1
    RWCString b("abc\0def");                                     // 2
    RWCString c("abc\0def", 7);                                  // 3

    cout << a.length();                                 // "3" を出力
    cout << strlen(a.data());                           // "3" を出力

    cout << b.length();                                 // "3" を出力
    cout << strlen(b.data());                           // "3" を出力

    cout << c.length();                                 // "7" を出力
    cout << strlen(c.data());                           // "3" を出力
    return 0; }

この例では、2 つの異なるコンストラクタが使用されています。1 行目と 2 行目のコンストラクタは "`const char*`" の引数で、ヌル文字で終端する文字列をとります。これは引数を 1 つしかとらないので、型変換にも使用できます (ARM 12.3.1)。この結果の長さは通常の方法で決まります。すなわちヌル文字の前にあるバイト数です。3 行目のコンストラクタは "`const char*`" および文字列の長さをとります。コンストラクタは、これらのバイト (埋め込まれたヌルを含む) をコピーします。

RWCString のバイト長は常に `RWCString::length()` によって渡されます。この文字列には埋め込まれたヌルも含まれていることがあるので、この長さは `strlen()` から渡される結果とは一致しないことがあります。

インデックスと他の演算子 (基本的に `size_t` 型の引数を使用したすべての関数) はバイト単位で動作します。したがって、これらの演算子は複数バイト文字列を含む RWCString には無効です。

## ワイド文字列

クラス RWWString は、各種の文字を表わすためにも使用され、ワイド文字を処理する点を除けば RWCString に似ています。ワイド文字はすべて `wchar_t` と同じサイズであるため、複数バイト文字よりもはるかに操作しやすいものです。

Tools.h++ を使用すると、複数バイト文字列とワイド文字列の間の変換を簡単に行なうことができます。次に、前の項で説明した `Sun` の例でその方法を示します。

    #include <rw/cstring.h>
    #include <rw/wstring.h>
    #include <assert.h>
    main() {
    RWCString Sun("\306\374\315\313\306\374");
    RWWString wSun(Sun, RWWString::multiByte); // 複数バイト文字列からワ
                                                  イド文字列へ
    RWCString check = wSun.toMultiByte();
    assert(Sun==check);                                         // OK
    return 0; }

基本的には、次の特殊な RWWString コンストラクタを使用して、複数バイト文字列からワイド文字列に変換します。

      RWWString(const char*, multiByte_);

例に示すように、パラメータ `multiByte_` は、列挙型で、とりうる唯一の値 `multiByte` を持ちます。`multiByte` 引数は、この比較的時間がかかる変換が間違って行われないように保証します。関数 `toMultiByte()` を使用する、ワイド文字列から複数バイト文字列への変換も同様に時間がかかります。

RWCString 全体が ASCII 文字からなる場合は、どちらへの変更もムダはかなり削減されます。これは、変換が単一の上位ビットの操作によるものであるからです。

    #include <rw/cstring.h>
    #include <rw/wstring.h>
    #include <assert.h>
    main() {
    RWCString EnglishSun("Sunday");                   // Ascii 文字
    assert(EnglishSun.isAscii());                               // OK

    // Ascii からワイド文字への変換
    RWWString wEnglishSun(EnglishSun, RWWString::ascii);

    assert(wEnglishSun.isAscii());                              // OK
    RWCString check = wEnglishSun.toAscii();
    assert(check==EnglishSun);                                  // OK
    return 0; }

メンバー関数 `RWCString::isAscii()` と `RWWString::isAscii()` が、ASCII 文字のみからなる文字列であることを確かめるため、どのように使用されているかに注意してください。ASCII からワイド文字への変換には、次のような RWWString コンストラクタが使用されていました。

      RWWString(const char*, ascii_);

パラメータ `ascii_` は、列挙型で、唯一のとりうる値が `ascii` (上記の例を参照) です。

メンバー関数 `RWWString::toAscii()` はこの逆の変換に使用されています。

1 メソッド `readFile()`、`readLine()`、`readString(istream&)`、`readToDelim()`、`readToken()` に関する詳細は、『Tools.h++ 7.0 クラスライブラリ・リファレンスマニュアル』の「RWCString」の項にあります。

2 ただし、複数バイト文字列を転送するシステム関数は、このように想定する場合があります。RWCString は、このような関数を呼び出して、そのような変換を提供するだけです。