今回は標準ライブラリ (STL) の中から「文字列 (string)」を取り上げます。STL には basic_string というテンプレートがあり、string は basic_string<char> の別名として定義されています。string は vector とよく似ていますが、文字列を操作するのに都合がよいメンバ関数が追加されているところが異なります。C++の場合、Cスタイル文字列よりも string を使ったほうが便利です。本ページでは string の基本的な使い方を簡単に説明します。
string はCスタイル文字列や他の string、それらの部分文字列を使って初期化することができます。文字や整数で string を初期化することはできませんが、文字と長さを指定して string を初期化することは可能です。
簡単な例を示しましょう。
リスト : string の宣言と初期化 (sample2901.cpp) #include <iostream> #include <vector> using namespace std; void print_string(string& s) { cout << "[" << s << "]\n"; } int main() { string s0; // 空文字列 print_string(s0); string s1("hello, world"); // リテラル print_string(s1); const char* a = "Hello, World"; // ポインタ string s2(a); print_string(s2); string s3(s1); // s1 をコピー print_string(s3); string s4(8, 'a'); print_string(s4); // 文字と長さの指定 const char* b = "foo bar baz oops"; string s5(b + 4, 3); // b[4] から 3 文字 print_string(s5); vector<char> c = {'a', 'b', 'c', 'd', 'e', 'f'}; string s6(c.begin(), c.end()); print_string(s6); }
$ clang++ sample2901.cpp $ ./a.out [] [hello, world] [Hello, World] [hello, world] [aaaaaaaa] [bar] [abcdef]
文字を格納しているコンテナクラスであれば、イテレータを指定して string を生成することもできます。
string は vector と同様に添字演算子 [] やメンバ関数 at で要素 (文字) にアクセスすることができます。メンバ関数には front や back も用意されています。
簡単な例を示します。
リスト : 要素のアクセス (sample2902.cpp) #include <iostream> using namespace std; int main() { string s = "hello, world"; cout << s[0] << endl; cout << s[s.length() - 1] << endl; s[0] = 'H'; s[s.length() - 1] = 'D'; for (auto x : s) cout << x; cout << endl; }
$ clang++ sample2902.cpp $ ./a.out h d Hello, worlD
length は文字列の長さを求めるメンバ関数です。コンテナクラスのメンバ関数 size と同じです。string は範囲 for 文で要素にアクセスすることもできます。
string はランダムイテレータをサポートしています。どの要素でも定数時間 O(1) でアクセスすることが可能です。イテレータを生成するメンバ関数を下表に示します。
メンバ関数 | 機能 |
---|---|
begin() | 先頭要素を指し示すイテレータを返す |
end() | 終端を指し示すイテレータを返す |
cbegin() | 先頭要素を指し示す const イテレータを返す |
cend() | 終端を指し示す const イテレータを返す |
rbegin() | 先頭要素を指し示すリバースイテレータを返す |
rend() | 終端を指し示すリバースイテレータを返す |
crbegin() | 先頭要素を指し示す const リバースイテレータを返す |
crend() | 終端を指し示す const リバースイテレータを返す |
const イテレータは要素を更新することができません。リバースイテレータは末尾要素が先頭で、先頭要素が末尾になります。要素が n 個ある場合、n - 1 番目の要素が先頭で、0 番目の要素が末尾になります。
イテレータのデータ型は次のようになります。
string::iterator // 通常のイテレータ string::const_iterator // const イテレータ string::reverse_iterator // リバースイテレータ string::const_reverse_iterator // const リバースイテレータ
最近の規格 (C++11) を利用できるコンパイラでは auto を使ったほうが簡単でしょう。
簡単な使用例を示します。
リスト : イテレータの使用例 (sample2903.cpp) #include <iostream> #include <algorithm> using namespace std; int main() { string s = "hello, world"; for (string::const_iterator iter = s.cbegin(); iter != s.cend(); ++iter) cout << *iter; cout << endl; for (auto iter = s.rbegin(); iter != s.rend(); ++iter) cout << *iter; cout << endl; auto p = find(s.begin(), s.end(), 'w'); if (p != s.end()) cout << *p << endl; }
$ clang++ sample2903.cpp $ ./a.out hello, world dlrow ,olleh w
string はイテレータをサポートしているので、STL の algorithm に用意されている関数、たとえば find や for_each などを利用することができます。ただし、文字列の操作は汎用の関数を使うよりも、string のメンバ関数を使ったほうが便利です。
string は演算子 + で連結することができます。s1 + s2 は文字列 s1 と s2 を連結した新しい文字列を生成します。
簡単な使用例を示します。
リスト : 文字列の連結 (sample2904.cpp) #include <iostream> using namespace std; int main() { string s1 = "hello"; string s2 = "world"; string s3 = s1 + ", " + s2; cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; }
$ clang++ sample2904.cpp $ ./a.out hello world hello, world
string は vector と同様にメンバ関数 insert で文字列の途中にデータを挿入することができますが、データを末尾に追加する演算子 += やメンバ関数 append も用意されています。もちろん、push_back もあります。
メンバ関数 | 機能 |
---|---|
push_back(c) | 末尾に文字 c を追加する |
operator+=(str) | 末尾に文字列 str を追加する |
operator+=(c) | 末尾に文字 c を追加する |
append(str) | 末尾に文字列 str を追加する |
append(n, c) | 末尾に文字 c を n 個追加する |
insert(pos, str) | 位置 pos に文字列 str を挿入する |
insert(pos, n, c) | 位置 pos に文字 c を n 個挿入する |
insert(it, c) | イテレータ it の位置に文字 c を挿入する |
insert(it, n, c) | イテレータ it の位置に文字 c を n 個挿入する |
insert(it, first, last) | イテレータ it の位置にイテレータ [first, last) が示す区間の文字を挿入する |
簡単な例を示します。
リスト : 文字列の末尾にデータを追加する (sample2905.cpp) #include <iostream> #include <vector> using namespace std; int main() { string s1; s1.push_back('f'); s1.push_back('o'); s1.push_back('o'); cout << s1 << endl; string s2 = "bar"; s1 += " "; s1 += s2; cout << s1 << endl; s1.append(" baz"); cout << s1 << endl; s1.append(5, '!'); cout << s1 << endl; s1.insert(0, "abc "); cout << s1 << endl; string s3 = "oops "; s1.insert(4, s3); cout << s1 << endl; vector<char> s4 = {'A', 'B', 'C'}; s1.insert(s1.begin(), s4.begin(), s4.end()); cout << s1 << endl; }
$ clang++ sample2905.cpp $ ./a.out foo foo bar foo bar baz foo bar baz!!!!! abc foo bar baz!!!!! abc oops foo bar baz!!!!! ABCabc oops foo bar baz!!!!!
末尾の文字は pop_back で取り除くことができます。途中の文字や文字列はメンバ関数 erase で取り除くことができます。
メンバ関数 | 機能 |
---|---|
pop_back() | 末尾から 1 文字削除する |
erase() | 文字をすべて削除する (空文字列) |
erase(pos, n) | 位置 pos から n 文字を削除する |
erase(it) | イテレータ it の位置の文字を削除する |
erase(first, last) | イテレータ [first, last) の区間にある文字を削除する |
簡単な使用例を示します。
リスト : 文字列の削除 (sample2906.cpp) #include <iostream> using namespace std; int main() { string s1 = "foo bar baz oops!"; cout << s1.back() << endl; s1.pop_back(); cout << s1 << endl; cout << s1.front() << endl; s1.erase(s1.begin()); // イテレータの位置の文字を削除 cout << s1 << endl; s1.erase(0, 3); // 0 番目から 3 文字を削除 cout << s1 << endl; s1.erase(s1.begin()); cout << s1 << endl; s1.erase(s1.begin(), s1.begin() + 3); cout << s1 << endl; }
$ clang++ sample2906.cpp $ ./a.out ! foo bar baz oops f oo bar baz oops bar baz oops ar baz oops baz oops
文字列の探索はメンバ関数 find を使うと簡単です。
size_type find(文字列, 検索開始位置 = 0);
find は引数の文字列と等しい部分文字列を探索し、見つけたらその位置を返します。見つからない場合は string::npos を返します。
簡単な例を示しましょう。
リスト : find の使用例 (sample2907.cpp) #include <iostream> using namespace std; int main() { string s1 = "foo bar baz foo bar baz oops!"; string s2 = "baz"; int n = 0; while (true) { n = s1.find(s2, n); if (n == string::npos) break; cout << n << endl; n += s2.length(); } }
$ clang++ sample2907.cpp $ ./a.out 8 20
このほかにも、末尾から部分文字列を探索するメンバ関数 rfind や、引数の文字列に含まれる文字を探索するメンバ関数 find_first_of や find_last_of、引数の文字列に含まれていない文字を探索するメンバ関数 find_first_not_of や find_last_not_of などがあります。詳細はC++のリファレンスマニュアルをお読みください。
メンバ関数 replace を使うと、文字列を置換することができます。
string& replace(開始インデックス, 文字数, 置換文字列); string& replace(開始イテレータ, 終了イテレータ, 置換文字列);
replace は置換開始位置から文字数の部分文字列を置換文字列に置き換えます。イテレータで置換する部分を指定することもできます。
簡単な使用例を示します。
リスト : replace の使用例 (sample2908.cpp) #include <iostream> using namespace std; int main() { string s1 = "foo bar baz oops!"; s1.replace(4, 3, "ABCD"); cout << s1 << endl; }
$ clang++ sample2908.cpp $ ./a.out foo ABCD baz oops!
replace にはいろいろな使い方があります。詳細はリファレンスマニュアルをお読みください。
メンバ関数 substr を使うと、文字列から部分文字列を求めることができます。
string substr(開始位置, 文字数);
substr は開始位置から文字数で指定した部分文字列を格納した新しい string を生成して返します。
簡単な使用例を示します。
リスト : substr の使用例 (sample2909.cpp) #include <iostream> using namespace std; int main() { string s = "abcdefghijklmn"; for (int i = 0; i <= s.length(); i++) cout << s.substr(i, s.length() - i) << endl; }
$ clang++ sample2909.cpp $ ./a.out abcdefghijklmn bcdefghijklmn cdefghijklmn defghijklmn efghijklmn fghijklmn ghijklmn hijklmn ijklmn jklmn klmn lmn mn n
string はメンバ関数 c_str や data でCスタイル文字列に変換することができます。
const char* c_str(); const char* data();
簡単な使用例を示します。
リスト : c_str と data の使用例 (sample2910.cpp) #include <iostream> #include <cstring> using namespace std; int main() { string s1 = "hello, world"; cout << s1.c_str() << endl; cout << strlen(s1.data()) << endl; }
$ clang++ sample2910.cpp $ ./a.out hello, world 12