M.Hiroi's Home Page

お気楽C++プログラミング超入門

応用編 : 文字列 (string)


Copyright (C) 2015-2023 Makoto Hiroi
All rights reserved.

はじめに

今回は標準ライブラリ (STL) の中から「文字列 (string)」を取り上げます。STL には basic_string というテンプレートがあり、string は basic_string<char> の別名として定義されています。string は vector とよく似ていますが、文字列を操作するのに都合がよいメンバ関数が追加されているところが異なります。C++の場合、Cスタイル文字列よりも string を使ったほうが便利です。本ページでは 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 のイテレータ

string はランダムイテレータをサポートしています。どの要素でも定数時間 O(1) でアクセスすることが可能です。イテレータを生成するメンバ関数を下表に示します。

表 : イテレータの生成 (string)
メンバ関数機能
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

●Cスタイル文字列への変換

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

初版 2015 年 11 月 7 日
改訂 2023 年 4 月 15 日