string | Programming Place Plus C++編【標準ライブラリ】 第2章

トップページC++編

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

この章の概要

この章の概要です。


string

C++標準ライブラリには、文字列を安全かつ便利に扱える std::string という機能があります。std::string を使うには、<string> という名前の標準ヘッダをインクルードする必要があります

std::string の正体を明かすと、std::basic_string というクラステンプレート【言語解説】第12章)に対する typedef です。クラステンプレートについての知識がないと読めませんが、具体的には、以下のように定義されています。

namespace std {
    template <typename CharT,
              typename Traits = std::char_traits<CharT>,
              typename Allocator = std::allocator<CharT> >
    class basic_string;

    typedef basic_string<char> string;
}

また、ワイド文字列用に、wstring も定義されています。こちらは、次のように定義されています。

namespace std {
    typedef basic_string<wchar_t> wstring;
}

つまり、std::string も std::wstring も、そこに含まれている機能はどちらも std::basic_string が提供しているものです。違いは、文字を表現するために char型を使うのか、wchar_t型を使うのかという点だけです。

そこで、この章では std::basic_string の機能を順番に取り上げていきます。いずれも、実際に使うときには、std::string や std::wstring に読み替えてください。

std::basic_string は、その内部に char型や wchar_t型のような標準的な文字型の配列で文字列を保持します。その配列は、文字列の長さに応じて、動的にメモリ割り当てを行うようになっています。そのため、確保しておいた領域が足りずに、バッファオーバーフローを起こす心配がありません。また、この後順次取り上げるように、std::basic_string は、文字列操作に関する豊富な機能を持っています。

std::basic_string は安全かつ便利なものです。C++ で文字列を扱う際には、原則として std::basic_string を使うようにすべきでしょう。

欠点は、動的メモリ割り当てと解放に伴うコストです。しかし現実的には、このコストが問題になることは稀なはずです。


サイズと容量

std::basic_string の理解のためにまず、「サイズ」と「容量」という概念を理解しておきましょう。

std::basic_string はその内部に、動的に割り当てられる生の文字配列を保持しています。この動的な文字配列は、必要に応じて自動的に拡張されます。

このとき、どのような方法でメモリを確保するのかを、アロケータ(3つ目のテンプレート仮引数)で指定できます。これを指定しないことは、標準の方法で構わないという意味です。特別に事情がない限り、これで問題ありません。

【上級】C++11 以降では、内部に小さな固定長の char型配列を置き、保持する文字列が短いときにはこの領域を使うようにして、動的メモリ割り当て(と解放)を避ける実装になっていることがあります。これは、SSO (Small String Optimization) と呼ばれる最適化手法です。

実際に確保されている動的な文字配列の要素数のことを「容量(キャパシティ)」と呼びます。容量は、capacity関数で取得できます。

size_type capacity() const;

詳細は後程取り上げます。

容量は、バイト数のような単位ではなく、「要素数(文字数)」であることに注意してください。

一方「サイズ」とは、動的な文字配列内に実際に存在している(使用されている)要素数のことです。サイズは、size関数で取得できます。こちらもバイト数ではなく、要素数であることに注意してください。

size_type size() const;

詳細は後程取り上げます。

容量とサイズの違いが分かっても、あまりピンと来ない人が多いかもしれません。理解しづらく感じる人は、「確保した領域のすべてが使われている状態」をイメージしてしまっているのかもしれません。こういうイメージだと、「容量」も「サイズ」も同じなのではないかと考えるでしょう。

実際にはたとえば、32文字分のメモリ領域が確保されているが(つまり「容量」が 32)、まだ文字が1つも存在していない(つまり「サイズ」が 0)という状態があります。文字を「追加(挿入)」して初めて「サイズ」が増加していきます。いわば「枠は作ったけれど、中身はまだ埋まっていない」という状態がある(現実的には、ほとんどいつもそういう状態です)ということです。

初期化

std::basic_string型の変数を定義するときに、いくつかの値を指定でき、その内容によって初期化されます。たとえば、

std::string s("xyz");
std::wstring ws(L"xyz");

このように定義すれば、変数s は “xyz”、変数ws は L“xyz” という文字列を保持した状態で初期化されます。これらは、関数を呼び出して、実引数を与えるように見えると思いますが、実際、そのようなことが行われています。

変数を定義するときに呼び出される特別な関数があり、コンストラクタと呼ばれます(【言語解説】第13章

初期値として与えられる値のパターンはかなり多くあります。以下に、関数宣言の形でリストアップしました。

この先もずっとそうですが、標準ライブラリに関する章は、リファレンスとしての役割も兼ねているので、できるだけ多くの機能を取り上げるようにしています。言語解説編からのリンクで来ている場合、まだ未解説の機能が使われていることがあるのでご了承ください。

// 1. 空の文字列で初期化
basic_string();

// 2. 空の文字列で初期化。アロケータを指定できる
explicit basic_string(const Allocator& alloc);

// 3. ch という文字を、count個並べた文字列で初期化。アロケータを指定してもいい
basic_string(size_type count,
             CharT ch,
             const Allocator& alloc = Allocator());

// 4. ほかの文字列(other) の一部 (pos文字目から count個) で初期化。アロケータを指定してもいい
basic_string(const basic_string& other,
             size_type pos,
             size_type count = std::basic_string::npos,
             const Allocator& alloc = Allocator());

// 5. s が指す文字列の最初の count個の文字で初期化。アロケータを指定してもいい
basic_string(const CharT* s,
             size_type count,
             const Allocator& alloc = Allocator());

// 6. s が指す文字列で初期化。アロケータを指定してもいい
basic_string(const CharT* s,
             const Allocator& alloc = Allocator());

// 7. 範囲[first~last) にある文字列で初期化。アロケータを指定してもいい
template <typename InputIt>
basic_string(InputIt first,
             InputIt last,
             const Allocator& alloc = Allocator());

// 8. ほかの std::basic_string をコピーして初期化
basic_string(const basic_string& other);

アロケータは第32章、explicit は【言語解説】第19章、仮引数の型に付く & は【言語解説】第16章、仮引数名の後ろの「= ~」の部分は【言語解説】第8章、「範囲」を表現する方法についてはこの章で解説しています。

4番目のタイプについて、count を std::basic_string::npos とした場合の意味は、末尾の文字まで使うということです。

5番目のタイプについて、s がヌルポインタであったり、count個の文字を含んでいなかったりする場合の動作は未定義です。

6番目のタイプについて、s がヌルポインタの場合の動作は未定義です。

以下は、それぞれを実際に使ったサンプルプログラムです。

#include <iostream>
#include <memory>
#include <string>

int main()
{
    const char* const CSTR = "abcdefg";

    std::allocator<char> alloc;

    std::string str1;                     // 空文字列
    std::string str2(alloc);              // 空文字列。アロケータ指定
    std::string str3(5, 'a');             // "aaaaa"
    std::string str4(str3, 1, 3);         // "aaa"
    std::string str5(CSTR, 4);            // "abcd"
    std::string str6(CSTR);               // "abcdefg"
    std::string str7(&CSTR[3], &CSTR[6]); // "def"
    std::string str8(str7);               // "def"

    std::cout << str1 << "\n"
              << str2 << "\n"
              << str3 << "\n"
              << str4 << "\n"
              << str5 << "\n"
              << str6 << "\n"
              << str7 << "\n"
              << str8 << std::endl;
}

実行結果



aaaaa
aaa
abcd
abcdefg
def
def

std::basic_string の文字列は、std::cout で直接出力できます(入出力については後程あらためて取り上げます)。

メンバ型

std::basic_string には、いくつかの型名が「公開」されたメンバとして定義されています。

「公開」されたとは、std::basic_string が自分の実装内でだけ使うのではなく、std::basic_string を使う側も自由に使えるということです。詳細は【言語解説】第12章を参照してください。

以下、表にまとめて紹介します。具体的にどう定義されているかは、実装によって異なります。

まず、要素の型に関する定義があります。

メンバ型名 意味
value_type 要素の型。第1テンプレート仮引数と同じ。
reference 要素の参照型。T&
const_reference 要素の const参照型。const T&
pointer 要素のポインタ型。T*
const_pointer 要素の constポインタ型。const T*

大きさや距離に関する型があります。

メンバ型名 意味
size_type 主に要素数を意味する型。符号無し整数。
多くの実装で std::size_t と同じ。
difference_type 主に距離を意味する型。符号付き整数。
多くの実装で std::ptrdiff_t と同じ。

イテレータに関する型があります。イテレータについては、「イテレータ」の項で説明しています。

メンバ型名 意味
iterator イテレータ型
const_iterator constイテレータ型
reverse_iterator 逆イテレータ型
const_reverse_iterator const逆イテレータ型

文字型の特性を表す型があります。

メンバ型名 意味
traits_type 文字の特性を表す型。第2テンプレート仮引数と同じ

アロケータに関する型があります。アロケータについては、第32章で説明します。

メンバ型名 意味
allocator_type アロケータ型。第3テンプレート仮引数と同じ

定数

std::basic_string には、「公開」された定数があります。

「公開」されたとは、std::basic_string が自分の実装内でだけ使うのではなく、std::basic_string を使う側も自由に使えるということです。詳細は【言語解説】第12章を参照してください。

static const size_type npos = -1;

npos は、std::basic_string::size_type型で、値はその最大値です。std::basic_string::size_type型は符号無し整数型です。

npos はいくつかの関数の実引数として使ったり、戻り値として返されたりします。その意味するところは、場面によって異なるので、それぞれの関数の仕様を確認してください。


代入

代入演算子

std::basic_string型への代入は、単に代入演算子を使うだけです。これは、代入元が (char[] などの)生の文字列でも、‘x’ のような1つの文字でも問題ありません。もはや、strcpy関数の出番はありません。

代入演算子の宣言としては以下のようになっています(演算子を関数のように表現することについては、【言語解説】第17章を参照してください)。

basic_string& operator=(const basic_string& str);
basic_string& operator=(const CharT* s);
basic_string& operator=(CharT ch);

以下のサンプルプログラムで、実際に代入を行っています。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    std::string str2;

    str2 = str1;
    std::cout << str2 << std::endl;

    str2 = "xyz";
    std::cout << str2 << std::endl;

    str2 = 'a';
    std::cout << str2 << std::endl;
}

実行結果

abcde
xyz
a

assign

また、assign関数を使うと、もう少し複雑な方法で代入を行えます。

// 1. str を代入する
basic_string& assign(const basic_string& str);

// 2. str の pos文字目から count個の文字を代入する
basic_string& assign(const basic_string& str,
                     size_type pos,
                     size_type count);

// 3. count個の ch を代入する
basic_string& assign(size_type count, CharT ch);

// 4. s が指し示す文字列を代入する
basic_string& assign(const CharT* s);

// 5. s が指し示す文字列から count個の文字を代入する
basic_string& assign(const CharT* s,
                     size_type count);

// 6. 範囲[first~last) にある文字列を代入する
template <typename InputIt>
basic_string& assign(InputIt first, InputIt last);

以下は使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    std::string str2;

    str2.assign(str1);                // 1. 他の std::string から代入
    std::cout << str2 << std::endl;

    str2.assign(str1, 1, 3);          // 2. str1[1] から 3文字を代入
    std::cout << str2 << std::endl;

    str2.assign(5, 'a');              // 3. 5個の 'a' を代入
    std::cout << str2 << std::endl;

    str2.assign("xyz");               // 4. const char*型から代入
    std::cout << str2 << std::endl;

    str2.assign("xyzxyz", 4);         // 5. "xyzxyz" の先頭から 4文字を代入
    std::cout << str2 << std::endl;

    str2.assign(&str1[0], &str1[3]);  // 6. 指定範囲を代入
    std::cout << str2 << std::endl;
}

実行結果

abcde
bcd
aaaaa
xyz
xyzx
abc

最後の範囲指定タイプについては、「イテレータ」の項で、あらためて考え方を取り上げます。


ところで、ここまでのサンプルプログラムでは、代入する文字列の長さを特に気にしていません。char型配列を使う場合、バッファオーバーフローの恐れがある訳ですが、std::basic_string ではこの心配はありません。なぜなら、std::basic_string は必要に応じて、自動的にメモリを割り当ててくれるからです。

便利で安全である一方、単なる代入のように見える箇所でも、動的メモリ割り当てが行われていることがあり得るので、処理負荷を気にしないといけないこともあります。

要素のアクセス

std::basic_string が保持している文字列中の1文字にアクセスする方法を取り上げます。

[]演算子

配列と同じく、[]演算子を使えます。

[]演算子の宣言としては以下のようになっています(演算子を関数のように表現することについては、【言語解説】第19章を参照してください)。

reference operator[](size_type pos);
const_reference operator[](size_type pos) const;

pos に指定した値が、サイズを超える場合の動作は未定義です。サイズと一致する場合は、非const版(上記宣言の1つ目の方)は未定義の動作、const版(2つ目の方)ではヌル文字への参照が返されます。

pos の型、std::basic_string::size_type は符号無しなので、負数を指定する可能性はありません。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    char c;

    c = str1[2];
    std::cout << c << std::endl;
}

実行結果

c

at

もう1つの方法として at関数があります。

reference at(size_type pos);
const_reference at(size_type pos) const;

関数宣言の後ろに付く const については、【言語解説】第12章を参照してください。

引数pos に指定する値は添字です。

誤ったインデックスの指定を検出可能である点が、[]演算子と異なります。at関数は、pos に指定した値が、サイズ以上であったとき、std::out_of_range例外を送出します。

例外については、【言語解説】第32章を参照してください。

pos の型、std::basic_string::size_type は符号無しなので、負数を指定する可能性はありません。

#include <iostream>
#include <string>
#include <stdexcept>

int main()
{
    std::string str1("abcde");
    char c;

    try {
        c = str1.at(5);
    }
    catch (std::out_of_range) {
        std::cerr << "範囲外アクセス" << std::endl;
        return 1;
    }
    std::cout << c << std::endl;
}

実行結果

範囲外アクセス

連結

strcat関数で行うような文字列の連結も簡単に行えます。方法がいくつかあるので、順番に取り上げます。

+=演算子、+演算子

+=演算子を使って、文字列や文字を末尾へ連結できます。

+=演算子の宣言としては以下のようになっています(演算子を関数のように表現することについては、【言語解説】第19章を参照してください)。

basic_string& operator+=(const basic_string& str);
basic_string& operator+=(CharT ch);
basic_string& operator+=(const CharT* s);

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    std::string str2("aaa");

    str2 += str1;  // "abcdeaaa"
    str2 += '?';   // "abcdeaaa?"
    str2 += "xyz"; // "abcdeaaa?xyz"

    std::cout << str2 << std::endl;
}

実行結果

abcdeaaa?xyz

また、+演算子も使えます。

【上級】こちらはメンバ関数ではなく、非メンバとして定義されています(【言語解説】第35章

template <typename CharT, typename Traits, typename Alloc>
basic_string<CharT,Traits,Alloc> operator+(
    const basic_string<CharT,Traits,Alloc>& lhs,
    const basic_string<CharT,Traits,Alloc>& rhs);

template <typename CharT, typename Traits, typename Alloc>
basic_string<CharT,Traits,Alloc> operator+(
    const CharT* lhs,
    const basic_string<CharT,Traits,Alloc>& rhs);

template <typename CharT, typename Traits, typename Alloc>
basic_string<CharT,Traits,Alloc> operator+(
    const basic_string<CharT,Traits,Alloc>& lhs,
    const CharT* rhs);

template <typename CharT, typename Traits, typename Alloc>
basic_string<CharT,Traits,Alloc> operator+(
    CharT lhs,
    const basic_string<CharT,Traits,Alloc>& rhs);

template <typename CharT, typename Traits, typename Alloc>
basic_string<CharT,Traits,Alloc> operator+(
    const basic_string<CharT,Traits,Alloc>& lhs,
    CharT rhs);

非常に複雑なように見えますが、要は、「std::basic_string 同士」「std::basic_string 対、生の文字列」「std::basic_string 対、文字」の組み合わせがあるだけです。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    std::string str2("aaa");

    str2 = str1 + str2;
    std::cout << str2 << std::endl;

    str2 = "xyz" + str1;
    std::cout << str2 << std::endl;

    str2 = str1 + "xyz";
    std::cout << str2 << std::endl;

    str2 = '?' + str1;
    std::cout << str2 << std::endl;

    str2 = str1 + '?';
    std::cout << str2 << std::endl;
}

実行結果

abcdeaaa
xyzabcde
abcdexyz
?abcde
abcde?

効率面では +=演算子の方が良いので、使いどころに注意してください。

append

append関数を使う方法もあります。以下のように宣言されています。

// 1. str を連結する
basic_string& append(const basic_string& str);

// 2. str の pos文字目から count個の文字を連結する
basic_string& append(const basic_string& str,
                     size_type pos,
                     size_type count);

// 3. count個の ch を連結する
basic_string& append(size_type count, CharT ch);

// 4. s が指し示す文字列を連結する
basic_string& append(const CharT* s);

// 5. s が指し示す文字列から count個の文字を連結する
basic_string& append(const CharT* s,
                     size_type count);

// 6. 範囲[first~last) にある文字列を連結する
template <typename InputIt>
basic_string& append(InputIt first, InputIt last);

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("aaa");
    std::string str2("abc");

    str1.append(str2);                // 他の std::basic_string を連結
    std::cout << str1 << std::endl;

    str1.append(str2, 1, 3);          // str2[1] から 3文字を連結
    std::cout << str1 << std::endl;

    str1.append(5, 'a');              // 5個の 'a' を連結
    std::cout << str1 << std::endl;

    str1.append("xyz");               // const char*型を連結
    std::cout << str1 << std::endl;

    str1.append("xyzxyz", 4);         // "xyzxyz" の先頭から 4文字を連結
    std::cout << str1 << std::endl;

    str1.append(&str2[0], &str2[3]);  // 指定範囲を連結
    std::cout << str1 << std::endl;
}

実行結果

aaaabc
aaaabcbc
aaaabcbcaaaaa
aaaabcbcaaaaaxyz
aaaabcbcaaaaaxyzxyzx
aaaabcbcaaaaaxyzxyzxabc

push_back

push_back関数を使って、1文字だけ連結できます。

void push_back(CharT ch);

他の方法と比べて随分と限定的な機能しかないようですが、STL に含まれるほかの機能群にも同名の関数が用意されており、まったく同じ使い方ができるように設計されています。つまり、統一性を持たせるために用意されている関数と言えます。

#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");

    str.push_back('x');         // 1文字連結
    std::cout << str << std::endl;
}

実行結果

abcdex


挿入

文字列の途中に、他の文字列や文字を挿入できます。これは、生の配列では少々面倒な処理ですが、std::basic_string では insert関数を使って簡単に実現できます。

//1. str を index文字目へ挿入する
basic_string& insert(size_type index, const basic_string& str);

//2. str の index_str文字目から count文字分だけ、index文字目のところへ挿入する
basic_string& insert(
    size_type index,
    const basic_string& str,
    size_type index_str,
    size_type count);

//3. s を index文字目へ挿入する
basic_string& insert(size_type index, const CharT* s);

//4. s を count文字分だけ、index文字目へ挿入する
basic_string& insert(size_type index, const CharT* s, size_type count);

//5. ch を count個だけ、index文字目へ挿入する
basic_string& insert(size_type index, size_type count, CharT ch);

//6. ch を pos が指し示す位置へ挿入する
iterator insert(iterator pos, CharT ch);

//7. ch を count個だけ、pos が指し示す位置へ挿入する
void insert(iterator pos, size_type count, CharT ch);

//8. 範囲[first~last) にある文字列を、pos が指し示す位置へ挿入する
template <typename InputIt>
void insert(iterator pos, InputIt first, InputIt last);

挿入先の位置を、size_type型か iterator型のいずれかで指定しています(いずれも第1引数)。指定した位置が、新しい文字列の先頭になるように挿入されます。

戻り値は、basic_string&型の場合は *this です(this の意味は【言語解説】第11章参照)。iterator型の場合は、挿入した文字を指すイテレータか、挿入した文字列の先頭の文字を指すイテレータです。イテレータについては後述しています。

引数index に「サイズ」以上の値を指定した場合には、std::out_of_range例外(第17章)を送出します。

以下は使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1;
    std::string str2("ABCDEF");

    str1 = "abc";
    str1.insert(1, str2);               // str[1] の直後に str2 を挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(1, str2, 2, 3);         // str[1] の直後に str2[2] から 3文字分を挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(1, "x");                // str[1] の直後に "x" を挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(1, "xyz", 2);           // str[1] の直後に "xyz" の最初の 2文字を挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(1, 3, 'a');             // str[1] の直後に 'a' を 3個挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(str1.begin(), 'x');     // str1 の先頭に 'x' を挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(str1.begin(), 3, 'x');  // str1 の先頭に 'x' を 3個挿入
    std::cout << str1 << std::endl;

    str1 = "abc";
    str1.insert(str1.begin(), str2.begin(), str2.end());  // str1 の先頭に str2 の全体を挿入
    std::cout << str1 << std::endl;
}

実行結果

aABCDEFbc
aCDEbc
axbc
axybc
aaaabc
xabc
xxxabc
ABCDEFabc

削除

ここでは、保持している文字列の全体や一部を削除する方法を取り上げます。

削除を行うと当然「サイズ」は減少します。しかし、「容量」の方は減少しないことに注意してください。「容量」を減らす方法については、「容量に関する処理」を参照してください。

また、削除を行うと、保持している文字の位置がずれるため、文字を指していたポインタや参照、イテレータは無効になります

clear

clear関数は、空文字列を保持した状態にします。つまり、「サイズ」が 0 になります。

void clear();

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");

    str.clear();
    std::cout << str << std::endl;
}

実行結果

erase

erase関数は、指定した位置、あるいは指定した範囲内の文字を削除します。

//1. index番目から count文字分だけ削除
basic_string& erase(size_type index = 0, size_type count = npos);

//2. position が指し示す文字を削除
iterator erase(iterator position);

//3. 範囲[first~last) を削除
iterator erase(iterator first, iterator last);

1つ目のタイプでは、index と count はそれぞれデフォルト実引数を持っています。count の方のデフォルト実引数 (std::basic_string::npos) は、index番目以降のすべての文字を対象にすることを意味します。そのため、2つの引数をともにデフォルトに任せた場合の効果は、clear関数と同じです。

デフォルト実引数については、【言語解説】第8章を参照してください。

また、1つ目のタイプにおいて、引数index に「サイズ」以上の値を指定した場合には、std::out_of_range例外(第17章)を送出します。

戻り値は、basic_string&型の場合は *this です(this の意味は【言語解説】第11章参照)。iterator型の場合は、削除された文字の次を指すイテレータです。次の文字がなければ end関数が返すイテレータと同等のものを返します。イテレータについては後述しています。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str;

    str = "abcde";
    str.erase();        // 0番目以降のすべての文字を削除(=クリア)
    std::cout << str << std::endl;

    str = "abcde";
    str.erase(2);       // 2番目以降のすべての文字を削除
    std::cout << str << std::endl;

    str = "abcde";
    str.erase(1, 2);    // 1番目の文字から 2文字分削除
    std::cout << str << std::endl;

    str = "abcde";
    str.erase(str.begin() + 2);  // 2番目の文字を削除
    std::cout << str << std::endl;

    str = "abcde";
    str.erase(str.begin(), str.begin() + 3);  // 0番目~3番目の文字を削除
    std::cout << str << std::endl;
}

実行結果


ab
ade
abde
de


部分文字列

substr関数を使うと、文字列を部分的に切り出せます。

basic_string substr(size_type pos = 0, size_type count = npos) const;

先頭から pos文字目を先頭に、そこから count文字分だけ切り出した文字列を返します。

引数はいずれもデフォルト実引数(【言語解説】第8章)があります。count のデフォルト実引数(std::basic_string::npos) は “末尾まで” を意味しています。pos のデフォルト実引数は 0 なので、“先頭から” を意味しています。

count に指定した値が大きすぎて、文字列の末尾を超えてしまった場合は、末尾までで止められます。一方、pos に指定した値が大きすぎて、開始位置が文字列の末尾を超えている場合は、std::out_of_range例外が送出されます。

例外については、【言語解説】第32章を参照してください。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abcabc");

    std::cout << str.substr() << std::endl;     // そのまま返される
    std::cout << str.substr(2) << std::endl;    // 2文字目以降が返される
    std::cout << str.substr(2, 3) << std::endl; // 2文字目から 最大 3文字分が返される
}

実行結果

abcabc
cabc
cab

容量に関する処理

capacity

現在の容量は、capacity関数で取得できます。

size_type capacity() const;

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abc");

    std::string::size_type capa = str.capacity();
    std::cout << capa << std::endl;
}

実行結果

15

reserve

reserve関数を使うと、容量を変化させられます。サイズが変化することはありません。

void reserve(size_type new_cap = 0);

引数は、新しい容量の指定です。呼び出し後の容量は、実引数の値 “以上” になります。つまり、指定した値よりも大きくなる可能性があります。

なお、この関数で容量を減らすことができるかどうかは、実装次第であり、保証されていません

実引数の値が、max_size関数で返される値を超えていた場合は、std::length_error例外(第17章)が送出されます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abc");

    std::cout << "BEFORE" << "\n"
              << "size: " << str.size() << "\n"
              << "capa: " << str.capacity() << std::endl;

    str.reserve(100);

    std::cout << "AFTER" << "\n"
              << "size: " << str.size() << "\n"
              << "capa: " << str.capacity() << std::endl;
}

実行結果

BEFORE
size: 3
capa: 15
AFTER
size: 3
capa: 111

文字を挿入したり連結したりするときに、容量が足りなくなって起こるメモリの再割り当て処理は、それなりに負荷が大きくなります。これが無視できない場合に、reserve関数を使って、都合の良いタイミングであらかじめメモリの割り当てを行っておくことができます。

サイズに関する処理

size、length

これは size関数、または length関数を使って取得できます。

size_type size() const;
size_type length() const;

2つの関数の意味はまったく同じです。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abc");

    std::string::size_type size = str.size();
    std::cout << size << std::endl;

    std::string::size_type len = str.length();
    std::cout << len << std::endl;
}

実行結果

3
3

empty

empty関数は、空かどうかの判定を行います。std::basic_string において「空」というのは、空文字列を保持している状態のことで、サイズが 0 の状態であるともいえます。

bool empty() const;

空であるときには true、空でないときには false が返されます。

bool型、true や false の意味は、【言語解説】第7章で解説しています。

list(第10章)の場合、size関数よりも empty関数の方が効率が優れている可能性があります (C++11以降では必ず同等です)。そのため、空かどうかの判定にはつねに empty関数を使うと覚えておいた方が良いという訳です。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1;
    std::string str2("abc");

    if (str1.empty()) {
        std::cout << "str1 is empty" << std::endl;
    }
    if (str2.empty()) {
        std::cout << "str2 is empty" << std::endl;
    }
}

実行結果

str1 is empty

max_size

「サイズ」の最大値というものがあり、max_size関数で取得できます。この値が意味するのは、std::basic_string に格納できる文字数の限界です。

size_type max_size() const;

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str;
    std::cout << str.max_size() << std::endl;
}

実行結果

4294967294

resize

resize関数を使うと、「サイズ」を変更できます。

void resize(size_type count);
void resize(size_type count, CharT ch);

引数count が新しいサイズです。現在のサイズよりも大きい数を指定した場合は、指定した数になるように、要素を追加します。現在のサイズよりも小さい数を指定した場合は、末尾側に近い文字を取り除くことで、サイズが削減されます。

要素を追加する際には、1つ目のタイプではヌル文字(「CharT()」で得られる値)を追加し、2つ目のタイプでは、引数ch に指定した文字を追加します。いずれにしても、結果として、容量が増えるかもしれません。

要素が削除された場合には、容量は変化しません。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abc");

    str.resize(5);
    std::cout << str << std::endl;

    str.resize(10, 'x');
    std::cout << str << std::endl;

    str.resize(1);
    std::cout << str << std::endl;
}

実行結果

abc
abc  xxxxx
a


イテレータ

イテレータ(反復子)は、データ構造に含まれる各要素に対する操作を抽象化する仕組みです。つまり、データ構造の種類を問わず、同じ方法で要素を操作できます。

イテレータという仕組みは、標準ライブラリ全体の中でも、比較的大きなテーマなので、詳細は、(第14章)であらためて取り上げることにして、ここでは std::basic_string を使うために最低限必要なことにだけ触れます。

イテレータは、C言語のポインタに近い感覚の機能になっています。つまり、イテレータは、データ構造に含まれる要素(文字)の1つを指し示すものです。そして、ポインタと同じように、*演算子や ->演算子などを使うことができるように作られています。

ただし、「ポインタ」=「イテレータ」ではないということは理解しておいてください。std::basic_string のためのイテレータは、ポインタを使って実装されていることがありますが、必ずそうだということではありません。

std::basic_string の先頭要素を指すイテレータを begin関数で、末尾要素の次を指すイテレータを end関数で取得できます。

iterator begin();
const_iterator begin() const;

iterator end();
const_iterator end() const;

begin関数は、先頭の文字を指すイテレータを返します。end関数の方は、末尾の文字の後ろを指すイテレータを返します。

begin関数や end関数で取得できるイテレータの型は、std::basic_string::iterator型あるいは、その const版(constイテレータ)である std::basic_string::const_iterator型です。要素の操作を抽象化できるといっても、型自体は std::basic_string に依存しています。

constイテレータは、ポインタにおける constポインタと同じことです。つまり、constイテレータが指し示す先にある要素は、そのイテレータを経由して変更できません。

次のサンプルプログラムは、std::basic_string に含まれる各文字を1文字ずつ、改行を挟みながら出力しています。

#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");

    const std::string::const_iterator itEnd = str.end();
    for (std::string::const_iterator it = str.begin(); it != itEnd; ++it) {
        std::cout << *it << std::endl;
    }
}

実行結果

a
b
c
d
e

end関数は、末尾の文字の “後ろ” を指すイテレータを返すため、現在の処理対象のイテレータと、end関数が返すイテレータが一致したときにループを終えるようにします。

逆イテレータ

要素を、末尾側から先頭へ向かって逆方向に走査するためのイテレータもあります。これを逆イテレータと呼びます。

逆イテレータは、rbegin関数rend関数で取得できます。

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

reverse_iterator rend();
const_reverse_iterator rend() const;

rbegin関数は、末尾の文字を指す逆イテレータを返します。rend関数の方は、先頭の文字の手前を指す逆イテレータを返します。

逆イテレータの型は、std::basic_string::reverse_iterator型あるいは、その const版(const逆イテレータ)である std::basic_string::const_reverse_iterator型です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");

    const std::string::const_reverse_iterator ritEnd = str.rend();
    for (std::string::const_reverse_iterator rit = str.rbegin(); rit != ritEnd; ++rit) {
        std::cout << *rit << std::endl;
    }
}

実行結果

e
d
c
b
a

比較

演算子

文字列同士の比較は、等価演算子や関係演算子によって行えます。比較の対象は、std::basic_string だけでなく、C言語形式の文字列でも構いません。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abc");
    std::string str2("abcde");

    std::cout << (str1 == str2) << "\n"
              << (str1 != str2) << "\n"
              << (str1 <  str2) << "\n"
              << (str1 <= str2) << "\n"
              << (str1 >  str2) << "\n"
              << (str1 >= str2) << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << (str1 == "abc") << "\n"
              << (str1 != "abc") << "\n"
              << (str1 <  "abc") << "\n"
              << (str1 <= "abc") << "\n"
              << (str1 >  "abc") << "\n"
              << (str1 >= "abc") << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << ("abc" == str1) << "\n"
              << ("abc" != str1) << "\n"
              << ("abc" <  str1) << "\n"
              << ("abc" <= str1) << "\n"
              << ("abc" >  str1) << "\n"
              << ("abc" >= str1) << std::endl;
}

実行結果

0
1
1
1
0
0
-----
1
0
0
1
0
1
-----
1
0
0
1
0
1

compare

compare関数を使って比較を行う方法もあります。

//1. str と比較
int compare(const basic_string& str) const;

//2. 自身の pos1文字目から count1文字分を、str と比較
int compare(size_type pos1,
            size_type count1,
            const basic_string& str) const;

//3. 自身の pos1文字目から count1文字分と、str の pos2文字目から count2文字分とを比較
int compare(size_type pos1,
            size_type count1,
            const basic_string& str,
            size_type pos2,
            size_type count2) const;

//4. s と比較
int compare(const CharT* s) const;

//5. 自身の pos1文字目から count1文字分を、s と比較
int compare(size_type pos1,
            size_type count1,
            const CharT* s) const;

//6. 自身の pos1文字目から count1文字分と、s の先頭から count2文字分とを比較
int compare(size_type pos1,
            size_type count1,
            const CharT* s,
            size_type count2) const;

compare関数は、自身が保持している文字列(あるいはその部分文字列)と、比較対象の文字列を辞書順で比較し、自身の方が小さければ 0未満の値を、自身の方が大きければ 0 より大きい値を返します

両者の文字の並びが一致している場合は、サイズの違いも比較します。サイズも同じであれば 0 が返されます。サイズが異なる場合、自身の方が小さければ 0未満の値を、自身の方が大きければ 0 より大きい値を返します。

正確には、比較は Traits::compare関数を使って行われます(Traits は std::basic_string の第2テンプレート仮引数)。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abc");
    std::string str2("abcde");
    std::string str3("bcd");

    std::cout << str1.compare(str2) << "\n"
              << str1.compare(str3) << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << str1.compare(1, 2, str2) << "\n"
              << str1.compare(1, 2, str3) << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << str1.compare(1, 2, str2, 1, 2) << "\n"
              << str1.compare(1, 2, str3, 0, 2) << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << str1.compare("abc") << "\n"
              << str1.compare("abcde") << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << str1.compare(1, 2, "bc") << "\n"
              << str1.compare(1, 2, "bcd") << std::endl;

    std::cout << "-----" << std::endl;

    std::cout << str1.compare(1, 2, "bc", 2) << "\n"
              << str1.compare(1, 2, "bcd", 2) << std::endl;
}

実行結果

-1
-1
-----
1
-1
-----
0
0
-----
0
-1
-----
0
-1
-----
0
0


交換

swap関数を使うと、自身の内容を他の std::basic_string の内容と交換できます。

両者のテンプレート実引数は同じでなければなりません。

void swap(basic_string& other);

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    std::string str2("xyz");

    str1.swap(str2);

    std::cout << str1 << std::endl;
    std::cout << str2 << std::endl;
}

実行結果

xyz
abcde

また、以下のように宣言された、std::swap関数もあります。

namespace std {
    template <typename CharT, typename Traits, typename Alloc>
    void swap(std::basic_string<CharT, Traits, Alloc>& lhs, std::basic_string<CharT, Traits, Alloc>& rhs);
}

std::swap関数は、第15章であらためて取り上げますが、これは要は型を問わないように用意された交換関数です。しかし、型によっては、専用の交換処理を実行する方が効率的であるため、上記のような std::basic_string 専用タイプがあります。

std::basic_string用の std::swap関数は、「a.swap(b)」のような形で、前述した方の swap関数を呼び出します。以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    std::string str2("xyz");

    std::swap(str1, str2);

    std::cout << str1 << std::endl;
    std::cout << str2 << std::endl;
}

実行結果

xyz
abcde

検索

特定の文字や文字列を検索するための関数も用意されています。これは6種類あります。

これらの検索系関数はいずれも、目的に合う文字や文字列を発見できた場合には、位置(添字)を表す値を返します。発見できなかったときは、std::basic_string::npos を返します。

find

find関数は、先頭から調べます。発見できた場合には、その最初の位置を返します。

size_type find(const basic_string& str, size_type pos = 0) const;
size_type find(const CharT* s, size_type pos, size_type count) const;
size_type find(const CharT* s, size_type pos = 0) const;
size_type find(CharT ch, size_type pos = 0) const;

str、s、ch は探したい文字や文字列の指定です。

pos は、検索を開始する位置の指定です。デフォルト実引数がある場合は 0 になっているので、文字列の先頭から検索することを意味します。

count は、検索を行う文字数の指定です。ヌル文字が現れなくても、この文字数分だけ検索を終えた時点で打ち切られます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde abcde");
    std::string str2("cde");

    std::cout << str1.find(str2) << std::endl;
    std::cout << str1.find(str2, 3) << std::endl;

    std::cout << str1.find("abcde", 1, 3) << std::endl;
    std::cout << str1.find("abcde", 1, 5) << std::endl;

    std::cout << str1.find("abcde") << std::endl;
    std::cout << str1.find("hex") << std::endl;

    std::cout << str1.find('e') << std::endl;
    std::cout << str1.find('e', 5) << std::endl;
}

実行結果

2
8
6
6
0
4294967295
4
10

rfind

rfind関数は、目的の文字や文字列が最後に現れる位置を返します。

size_type rfind(const basic_string& str, size_type pos = npos) const;
size_type rfind(const CharT* s, size_type pos, size_type count) const;
size_type rfind(const CharT* s, size_type pos = npos) const;
size_type rfind(CharT ch, size_type pos = npos) const;

str、s、ch は探したい文字や文字列の指定です。

pos は、検索を行わない位置の指定です。ここに指定した位置よりも手前側でしか、目的の文字や文字列を発見しません。デフォルト実引数がある場合は npos になっており、これは制限を加えず、文字列全体が検索対象であることを意味します。

count は、検索を行う文字数の指定です。ヌル文字が現れなくても、この文字数分だけ検索を終えた時点で打ち切られます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde abcde");
    std::string str2("cde");

    std::cout << str1.rfind(str2) << std::endl;
    std::cout << str1.rfind(str2, 3) << std::endl;

    std::cout << str1.rfind("abcde", 1, 3) << std::endl;
    std::cout << str1.rfind("abcde", 1, 5) << std::endl;

    std::cout << str1.rfind("abcde") << std::endl;
    std::cout << str1.rfind("hex") << std::endl;

    std::cout << str1.rfind('e') << std::endl;
    std::cout << str1.rfind('e', 5) << std::endl;
}

実行結果

8
2
0
0
6
4294967295
10
4

find_first_of

find_first_of関数は、文字列を先頭から調べ、指定の文字のいずれかが現れる最初の位置を返します。

size_type find_first_of(const basic_string& str, size_type pos = 0) const;
size_type find_first_of(const CharT* s, size_type pos, size_type count) const;
size_type find_first_of(const CharT* s, size_type pos = 0) const;
size_type find_first_of(CharT ch, size_type pos = 0) const;

str、s、ch は探したい文字や文字列の指定です。str や s の場合、この文字列に含まれるいずれかの文字を探します。

pos は、検索を開始する位置の指定です。デフォルト実引数がある場合は 0 になっているので、文字列の先頭から検索することを意味します。

count は、検索を行う文字数の指定です。ヌル文字が現れなくても、この文字数分だけ検索を終えた時点で打ち切られます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde abcde");
    std::string str2("cde");

    std::cout << str1.find_first_of(str2) << std::endl;
    std::cout << str1.find_first_of(str2, 3) << std::endl;

    std::cout << str1.find_first_of("abcde", 1, 3) << std::endl;
    std::cout << str1.find_first_of("abcde", 1, 5) << std::endl;

    std::cout << str1.find_first_of("abcde") << std::endl;
    std::cout << str1.find_first_of("hex") << std::endl;

    std::cout << str1.find_first_of('e') << std::endl;
    std::cout << str1.find_first_of('e', 5) << std::endl;
}

実行結果

2
3
1
1
0
4
4
10

find_last_of

find_last_of関数は、指定の文字のいずれかが最後に現れる位置を返します。

size_type find_last_of(const basic_string& str, size_type pos = npos) const;
size_type find_last_of(const CharT* s, size_type pos, size_type count) const;
size_type find_last_of(const CharT* s, size_type pos = npos) const;
size_type find_last_of(CharT ch, size_type pos = npos) const;

str、s、ch は探したい文字や文字列の指定です。str や s の場合、この文字列に含まれるいずれかの文字を探します。

pos は、検索を行わない位置の指定です。ここに指定した位置よりも手前側でしか、目的の文字や文字列を発見しません。デフォルト実引数がある場合は npos になっており、これは制限を加えず、文字列全体が検索対象であることを意味します。

count は、検索を行う文字数の指定です。ヌル文字が現れなくても、この文字数分だけ検索を終えた時点で打ち切られます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde abcde");
    std::string str2("cde");

    std::cout << str1.find_last_of(str2) << std::endl;
    std::cout << str1.find_last_of(str2, 3) << std::endl;

    std::cout << str1.find_last_of("abcde", 1, 3) << std::endl;
    std::cout << str1.find_last_of("abcde", 1, 5) << std::endl;

    std::cout << str1.find_last_of("abcde") << std::endl;
    std::cout << str1.find_last_of("hex") << std::endl;

    std::cout << str1.find_last_of('e') << std::endl;
    std::cout << str1.find_last_of('e', 5) << std::endl;
}

実行結果

10
3
1
1
10
10
10
4

find_first_not_of

find_first_not_of関数は、文字列を先頭から調べ、指定の文字のいずれでもない文字が最初に現れる位置を返します。

size_type find_first_not_of(const basic_string& str, size_type pos = 0) const;
size_type find_first_not_of(const CharT* s, size_type pos, size_type count) const;
size_type find_first_not_of(const CharT* s, size_type pos = 0) const;
size_type find_first_not_of(CharT ch, size_type pos = 0) const;

str、s、ch は探したい文字や文字列の指定です。str や s の場合、この文字列に含まれるいずれの文字でもない文字を探します。

pos は、検索を開始する位置の指定です。デフォルト実引数がある場合は 0 になっているので、文字列の先頭から検索することを意味します。

count は、検索を行う文字数の指定です。ヌル文字が現れなくても、この文字数分だけ検索を終えた時点で打ち切られます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde abcde");
    std::string str2("cde");

    std::cout << str1.find_first_not_of(str2) << std::endl;
    std::cout << str1.find_first_not_of(str2, 3) << std::endl;

    std::cout << str1.find_first_not_of("abcde", 1, 3) << std::endl;
    std::cout << str1.find_first_not_of("abcde", 1, 5) << std::endl;

    std::cout << str1.find_first_not_of("abcde") << std::endl;
    std::cout << str1.find_first_not_of("hex") << std::endl;

    std::cout << str1.find_first_not_of('e') << std::endl;
    std::cout << str1.find_first_not_of('e', 5) << std::endl;
}

実行結果

0
5
3
5
5
0
0
5

find_last_not_of

find_last_not_of関数は、文字列を先頭から調べ、指定の文字のいずれでもない文字が最後に現れる位置を返します。

size_type find_last_not_of(const basic_string& str, size_type pos = npos) const;
size_type find_last_not_of(const CharT* s, size_type pos, size_type count) const;
size_type find_last_not_of(const CharT* s, size_type pos = npos) const;
size_type find_last_not_of(CharT ch, size_type pos = npos) const;

str、s、ch は探したい文字や文字列の指定です。str や s の場合、この文字列に含まれるいずれの文字でもない文字を探します。

pos は、検索を行わない位置の指定です。ここに指定した位置よりも手前側でしか、目的の文字や文字列を発見しません。デフォルト実引数がある場合は npos になっており、これは制限を加えず、文字列全体が検索対象であることを意味します。

count は、検索を行う文字数の指定です。ヌル文字が現れなくても、この文字数分だけ検索を終えた時点で打ち切られます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde abcde");
    std::string str2("cde");

    std::cout << str1.find_last_not_of(str2) << std::endl;
    std::cout << str1.find_last_not_of(str2, 3) << std::endl;

    std::cout << str1.find_last_not_of("abcde", 1, 3) << std::endl;
    std::cout << str1.find_last_not_of("abcde", 1, 5) << std::endl;

    std::cout << str1.find_last_not_of("abcde") << std::endl;
    std::cout << str1.find_last_not_of("hex") << std::endl;

    std::cout << str1.find_last_not_of('e') << std::endl;
    std::cout << str1.find_last_not_of('e', 5) << std::endl;
}

実行結果

7
1
4294967295
4294967295
5
9
9
5


置換

文字列の置換には、replace関数を使います。

//1. pos文字目から count文字分の範囲を、str で置換
basic_string& replace(
    size_type pos, size_type count,
    const basic_string& str);

//2. 範囲[first~last) を、str で置換
basic_string& replace(
    const_iterator first, const_iterator last,
    const basic_string& str);

//3. pos文字目から count文字分の範囲を、str の pos2文字目から count2文字分の範囲で置換
basic_string& replace(
    size_type pos,
    size_type count,
    const basic_string& str,
    size_type pos2,
    size_type count2);

//4. 範囲[first~last) を、範囲[first2~last2) で置換
template <typename InputIt>
basic_string& replace(
    const_iterator first, const_iterator last,
    InputIt first2, InputIt last2);

//5. pos文字目から count文字分の範囲を、cstr の先頭から count2文字分で置換
basic_string& replace(
    size_type pos, size_type count,
    const CharT* cstr,
    size_type count2);

//6. 範囲[first~last) を、cstr の先頭から count2文字分で置換
basic_string& replace(
    const_iterator first, const_iterator last,
    const CharT* cstr,
    size_type count2);

//7. pos文字目から count文字分の範囲を、cstr で置換
basic_string& replace(
    size_type pos, size_type count,
    const CharT* cstr);

//8. 範囲[first~last) を、cstr で置換
basic_string& replace(
    const_iterator first, const_iterator last,
    const CharT* cstr);

//9. pos文字目から count文字分の範囲を、count2文字分の ch で置換
basic_string& replace(
    size_type pos, size_type count,
    size_type count2, CharT ch);

//10. 範囲[first~last) を、count2文字分の ch で置換
basic_string& replace(
    const_iterator first, const_iterator last,
    size_type count2, CharT ch);

pos、count、first、last は、自身の文字列の範囲を指定する引数です。pos2、count2、first2、last2 は、置換結果として使う文字列の範囲を指定する引数です。

cstr は生の文字列を指し示すポインタです。count2 を伴うものは、ヌル文字が現れなくても、count2 で指定した文字数分で打ち切られます。count2 を伴わない場合は、ヌル文字までが対象です。

戻り値はいずれも、自身を指す参照です。

pos の値がサイズ以上の場合と、pos2 の値が str のサイズ以上の場合には、std::out_of_range例外が送出されます。また、置換後の文字列のサイズが、max_size関数が返す値を超えてしまう場合には、std::length_error例外が送出されます。

例外については、【言語解説】第32章を参照してください。

置換される範囲の長さと、置換文字列の長さは一致する必要はありません。たとえば、3文字分の範囲を、1文字や5文字で置換できます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str;
    std::string rep("xyz");

    str = "abcabc";
    str.replace(1, 4, rep);        //1. str[1] から 4文字分を rep で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(str.begin() + 1, str.begin() + 4, rep);  //2. str[1]~str[3] を rep で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(1, 4, rep, 2, 1);  //3. str[1] から 4文字分を rep[2] からの 1文字分で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(str.begin(), str.begin() + 3, rep.begin(), rep.begin() + 2);  //4. str[0]~str[2] を rep[0]~rep[1] で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(1, 4, "xyz", 2);   //5. str[1] から 4文字分を "xyz" の先頭2文字分で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(str.begin() + 1, str.begin() + 4, "xyz", 2);  //6. str[1]~str[3] を 4文字分を "xyz" の先頭2文字分で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(1, 4, "xyz");      //7. str[1] から 4文字分を "xyz" で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(str.begin() + 1, str.begin() + 4, "xyz");  //8. str[1]~str[4] を "xyz" で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(1, 4, 3, 'a');     //9. str[1] から 4文字分を 3個の 'a' で置換
    std::cout << str << std::endl;

    str = "abcabc";
    str.replace(str.begin() + 1, str.begin() + 4, 3, 'a');  //10. str[1]~str[3] を 4文字分を 3個の 'a' で置換
    std::cout << str << std::endl;
}

実行結果

axyzc
axyzbc
azc
xyabc
axyc
axybc
axyzc
axyzbc
aaaac
aaaabc

C言語形式の文字列の相互変換

便利で安全な std::basic_string を使うべきですが、まれに、C言語由来の関数に文字列を渡さなければならないといった理由で、C言語形式の文字列(const char* など)が必要になることがあります。そのため、std::basic_string と、C言語形式の文字列を相互にやり取りするための方法があります。

C言語形式の文字列から std::basic_string を得る

C言語形式の文字列があるとき、これを std::basic_string として使うには、新たな std::basic_string を作るか、既存の std::basic_string の変数へ代入します。

たとえば、新たな std::basic_string を作るのなら、std::basic_string を初期化するときに、C言語形式の文字列を渡せばよいです。詳細は、「初期化」の項を参照してください。以下はプログラム例です。

#include <iostream>
#include <string>

int main()
{
    const char* s1 = "abcde";
    char* s2 = "abcde";
    const char s3[] = "abcde";

    std::string str1(s1);
    std::string str2(s2);
    std::string str3(s3);

    std::cout << str1 << "\n"
              << str2 << "\n"
              << str3 << std::endl;
}

実行結果

abcde
abcde
abcde

std::basic_string から C言語形式の文字列を得る

std::basic_string を生の文字列とみなすことはできません。

std::string str("abcde");
const char* s = str;  // コンパイルエラー

c_str

std::basic_string の内容を持ったC言語形式の文字列が必要であれば、c_str関数を使います。

const CharT* c_str() const;

戻り値は、std::basic_string が保持している生の文字列を指し示す constポインタです。末尾にはきちんとヌル文字が付加されているので、C言語形式の文字列を要求している場面でそのまま使えます。なお、std::basic_string のサイズが 0 のときは、空文字列が返されます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");

    const char* s = str.c_str();
    std::cout << s << std::endl;
}

実行結果

abcde

c_str関数は、文字列のコピーを取っているわけではないので、std::basic_string側の文字列に変化が生じた時点で、戻り値で返されたポインタは無効になります

#include <iostream>
#include <string>

int main()
{
    std::string str("abcde");

    const char* s = str.c_str();
    std::cout << s << std::endl;

    str += "x";  // str の保持している文字列が変化

    // c_str() で取得したポインタは無効になったので、
    // 以下は保証できない。
//  std::cout << s << std::endl;
}

コピーを取る必要があるのならば、c_str関数ではなく copy関数を使います。

data

c_str関数に似た data関数というものがあります。

const CharT* data() const;

data関数も、std::basic_string が保持している文字列を指し示す constポインタを返しますが、末尾にヌル文字が付加されません。また、std::basic_string のサイズが 0 のときにも、ヌルでないポインタを返しますが、何を指しているか分からないので、間接参照などの操作を行ってはなりません

以下、使用例です。

#include <cstdio>
#include <string>

void print(const char* s, std::size_t len)
{
    for (std::size_t i = 0; i < len; ++i) {
        std::printf("%c", s[i]);
    }
    std::printf("\n");
}

int main()
{
    std::string str("abcde");

    print(str.data(), str.length());
}

実行結果

abcde

copy

copy関数は、std::basic_string が保持している文字列の全体あるいは一部を、文字配列へコピーします。

size_type copy(CharT* dest, size_type count, size_type pos = 0) const;

pos文字目を先頭に、count文字分を、dest が指し示す配列へコピーします。戻り値は、コピーされた文字数です。

pos に指定した値が、サイズを超えている場合には、std::out_of_range例外が送出されます。

例外については、【言語解説】第32章を参照してください。

なお、copy関数では、コピー先の末尾にヌル文字が付加されないことに注意してください。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str1("abcde");
    char cstr[10];
    std::size_t len;

    len = str1.copy(cstr, sizeof(cstr) - 1);
    cstr[len] = '\0';
    std::cout << cstr << std::endl;

    len = str1.copy(cstr, sizeof(cstr) - 1, 3);
    cstr[len] = '\0';
    std::cout << cstr << std::endl;
}

実行結果

abcde
de


入出力

<<、>>

std::basic_string は、入出力ストリームに対して直接的に入出力を行うことが可能です。これは、以下の宣言があることで実現されています。

namespace std {
    template <typename CharT, typename Traits, typename Allocator>
    basic_ostream<CharT, Traits>& operator<<(
        basic_ostream<CharT, Traits>& os,
        const basic_string<CharT, Traits, Allocator>& str);

    template <typename CharT, typename Traits, typename Allocator>
    basic_istream<CharT, Traits>& operator>>(
        basic_istream<CharT, Traits>& is,
        basic_string<CharT, Traits, Allocator>& str);
}

この2つの宣言があることによって、std::cout や std::cin を使えます。

std::basic_ostream や std::basic_istream についての詳細は、第27章で解説することにして、ここでは最低限の使い方を例示します。

#include <iostream>
#include <string>

int main()
{
    std::string str;

    std::cin >> str;
    std::cout << str << std::endl;
}

実行結果

abcde  <-- 入力
abcde

より細かい制御を行う方法は、第30章を参照してください。

std::getline関数

std::getline関数を使って入力を行う方法もあります。これは、std名前空間内にある名前空間スコープの関数です。std::basic_string は引数に現れます。

namespace std {
    template <typename CharT, typename Traits, typename Allocator>
    basic_istream<CharT, Traits>& getline(
        basic_istream<CharT, Traits>& stream,
        basic_string<CharT, Traits, Allocator>& str);

    template <typename CharT, typename Traits, typename Allocator>
    basic_istream<CharT, Traits>& getline(
        basic_istream<CharT, Traits>& stream,
        basic_string<CharT, Traits, Allocator>& str,
        CharT delim);
}

第1引数に、入力ストリームを指定します。標準入力からの入力を受け取るためには std::cin を指定すれば良いです。std::basic_istream については、第27章で取り上げます。

第2引数に、std::basic_string型の変数(の参照)を指定します。入力された文字列が、この変数に格納されます。必要なメモリ領域は自動的に確保されます。

通常、改行文字が登場するまで読み取りを行いますが、第3引数に文字を指定した場合は、その文字の登場で打ち切ります。

戻り値は、第1引数と同じものが返されます。

以下、使用例です。

#include <iostream>
#include <string>

int main()
{
    std::string str;

    std::getline(std::cin, str);
    std::cout << str << std::endl;

    std::getline(std::cin, str, 'c');
    std::cout << str << std::endl;
}

実行結果

abcde  <-- 入力
abcde
abcde  <-- 入力
ab


練習問題

問題① std::basic_string型の変数を ‘X’ という1文字で初期化するには、どうすればいいですか?

問題② あなたの使っているコンパイラにおいて、空の std::basic_string はどれだけのサイズと容量を持つか調べてみてください。

問題③ 文字列の途中に ‘\0’ を挿入することによって、std::basic_string のサイズは変化するでしょうか?

問題④ std::basic_string を渡すと、その中に含まれる小文字のアルファベットを大文字化するような関数を作成してください(ちなみに、std::basic_string にそのような機能はありません)。

問題⑤ 問題④の関数を、イテレータによる範囲指定に対応させてください。


解答ページはこちら

参考リンク


更新履歴

’2018/8/1 全体的に見直し修正。
サイズと容量」「メンバ型」「定数」「入出力」「交換」の項を追加。
「C++11(UTF-8 のサポート)」の項を削除(内容的に string の解説ではないので)。
C++11以降に関する記述は、追加・変更の概要を示す程度に留めるように統一した。C++11以降の話題は、Modern C++編で行う方針。

’2018/4/5 VisualStudio 2013 の対応終了。

≪さらに古い更新履歴を展開する≫



前の章へ (第1章 C標準ライブラリの扱い)

次の章へ (第3章 pair)

C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る