このページでは STL の string の使い方について解説します。
string は、「要素が char 型の文字列」を表現するための型である、と考えている人が多いでしょう。
しかし、これは、ほんの少しだけ違います。
文字列というと、普通は "abc" などのリテラルで表現される 0 で終端された、いわゆる「0 終端文字列」を思い浮かべますが、string は「0 終端文字列」ではありません。
string の中味はもっと単純な char 型の配列と考える方が実体に合っており、途中に 0 を含むことも出来ます。
これは、string をバイナリデータの塊として使うこともできることを示しています。
この意味では string は vector<char> に良く似ていると言えるでしょう。
string に文字列を入れるには、次のようにします。強調した部分に注目ください。
#pragma warning(disable:4786) #include <iostream> #include <string> using namespace std; int main() { string strText0; // 引数なしの初期化 string strText1 = "Hello"; // 文字列リテラルで初期化 string strText2("Hello"); // 文字列リテラルで初期化 cout << strText0 << endl; // 空行になる。 cout << strText1 << endl; // Hello になる。 cout << strText2 << endl; // Hello になる。 return 0; }
Windows プログラミングで VisualC++ を使っていると、いつの間にか MFC の CString を使っていて、これと string との間のコピーが必要になることもあります。
この場合、次のようにします。
CString cstr = "AAA"; // CString オブジェクト string str = "BBB"; // string オブジェクト str = cstr; // CString から string へのコピー cstr = str.c_str(); // string から CString へのコピー
CString から string へのコピーは、昔は str = LPCTSTR(cstr); と書かないとコピー出来ませんでしたが、最近は、単純に = で結べばできるようです。
CString に上記のように char を入れるためには、VisualC++ の設定で、「マルチバイト文字列を使用する」ようにする必要があります(念のため)。
冒頭でも述べたように、string はバイナリデータの塊として扱うことも出来ます。
次のように、途中に 0 を入れることも出来ます。
string strData = "AAA"; // AAA で初期化する strData += char(0); // バイト 0 を追加する。 strData += "BBB"; // BBB を追加する。 cout << strData << endl; // AAA としか表示されないが、 int iSize = strData.size(); // 実際の長さは、途中に 0 を挟んだ 7 である。
応用範囲は無限ですね。
二つの string オブジェクトを連結するには + 演算子を使用します。
string strA = "AAA"; string strB = "BBB"; string strC = strA + strB; // AAABBB になる。
これは vector<char> では出来ないことです。
+ 演算子は、string のデータの中に 0 が挟まれていても問題なく動作します。つまり、バイナリデータの塊を連結することもできるということですね。
元のデータを書き換えて良いのであれば、+= 演算子を使用します。
string strA = "AAA"; string strB = "BBB"; strA += strB; // strA は AAABBB になる。
MFC の CString にある機能で string では使えないものがあります。
例えば、次のようなものです。
一番困るのが Format() でしょうか。これを実装してみましょう。
string から派生した STRING というクラスに実装することにします。
#pragma warning(disable:4996) #include "stdafx.h" #include <string> using namespace std; // Format() を定義してみる。 class STRING : public string { public: STRING Format(const char* pcFormat, ...) { va_list valist; va_start(valist, pcFormat); const int iBufferSize = 5000; char acTable[iBufferSize]; { vsprintf_s(acTable, iBufferSize, pcFormat, valist); assign(acTable); // 基本クラス string の関数 } va_end(valist); return *this; } }; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { STRING str; str.Format("%d", 3); // Format() が使えるようになった。 printf("str = %s\n", str.c_str()); // プリントしてみる。 return 0; }
同様に MakeUpper() を実装してみましょう。
#pragma warning(disable:4996) #include "stdafx.h" #include <string> using namespace std; class STRING : public string { public: STRING() {} STRING(const string& rstr) { assign(rstr); } STRING& MakeUpper() { for (int iPos = 0; iPos < size(); iPos++) { at(iPos) = toupper(at(iPos)); } return *this; } }; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { STRING str = string("abc"); str.MakeUpper(); // MakeUpper() が使えるようになった。 printf("str = %s\n", str.c_str()); // ABC になる。 return 0; }
TrimLeft() などは、上記と大差ないので、宿題にしておきましょう。
ナオ : って、手抜き?
店主 : ...(押し黙る店主であった。)
文字列の "2.3" を、double 型の 2.3 に変換したいとき、次のようにしますが、
double dValue = atof("2.3");
atof() を陽に使用しないで、変換したいという要望があります。つまり、"2.3" と 2.3 を区別しないようなバリアント型の変数を使いたいというような場合、次のようにします。
#pragma warning(disable:4996) #include "stdafx.h" #include <string> using namespace std; class STRING : public string { public: ...Format() の定義略... operator double() { return atof(c_str()); } STRING operator=(double dValue) { Format("%f", dValue); return *this; } }; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { STRING strValue; strValue = 2.3; // STRING に double を代入する。 double dValue = strValue; // STRING を double に代入する。 printf("strValue = %s\n", strValue.c_str()); // strValue = 2.300000 printf("dValue = %f\n", dValue); // dValue = 2.300000 return 0; }
int との間の変換などの定義も追加すればそれなりに動作するということですね。
ナオ : 店長、これは string で CString を越えようとしているわけですか?
店主 : ......そう見えるかね?
ナオ : 店長は CString が嫌い?
店主 : ......(なぜか、押し黙る店主であった。)