閉じる

【C++】stringの使い方と文字列操作まとめ

C++で文字列を扱うときに避けて通れないのがstd::stringです。

C言語の文字配列と違い、メモリ管理を意識せずに文字列を扱えるため、初心者から上級者まで幅広く使われています。

本記事では、基本的な使い方から便利な文字列操作、よくある落とし穴までを、サンプルコードとともに丁寧に解説します。

C++における文字列の基本

C文字列とstd::stringの違い

C++では、文字列を表現する方法として大きく2種類があります。

ひとつはC言語から受け継いだヌル終端文字列(C文字列)、もうひとつがC++標準ライブラリで提供されるstd::stringです。

C文字列はchar配列で表現され、末尾に'\0'(ヌル文字)を付けて終端を表します。

一方、std::stringはクラスであり、自身で長さ情報とメモリを管理します。

代表的な違いを表にまとめます。

項目C文字列(char配列)std::string
メモリ管理自分で配列サイズを決める自動で拡張・縮小
長さの取得strlenで毎回計算size()で即取得
代入・連結strcpy, strcatなど代入演算子=, +, +=
安全性バッファあふれを起こしやすいより安全で直感的
標準ライブラリ連携一部で必要C++の多くの機能と連携しやすい

通常のC++プログラムでは、まずstd::stringを使うことを基本にするとよいです。

Cスタイル文字列は、レガシーAPIとの連携時など必要なときだけ使う、というスタンスが安全です。

std::stringを使うための準備

C++でstd::stringを使うには、#includeと名前空間に注意する必要があります。

C++
#include <iostream>   // 入出力に必要
#include <string>     // std::stringを使うのに必要

int main() {
    std::string s = "Hello, world!";
    std::cout << s << std::endl;
    return 0;
}
実行結果
Hello, world!

ヘッダ<string>のインクルードを忘れるとコンパイルエラーになります。

using namespace std;は便利ですが、大規模開発では名前衝突の原因にもなるため、std::stringのようにstd::を付けて使う書き方に慣れておくと安心です。

std::stringの基本操作

文字列の宣言と初期化

std::stringには複数の宣言・初期化方法があります。

代表的な例を見てみます。

C++
#include <iostream>
#include <string>

int main() {
    // 空の文字列
    std::string s1;                  // 中身は空

    // 文字列リテラルから初期化
    std::string s2 = "Hello";
    std::string s3("World");         // コンストラクタ形式

    // コピー初期化
    std::string s4 = s2;             // s4は"Hello"

    // 同じ文字の繰り返しで初期化
    std::string s5(5, 'A');          // "AAAAA"

    std::cout << "s2: " << s2 << std::endl;
    std::cout << "s3: " << s3 << std::endl;
    std::cout << "s5: " << s5 << std::endl;
    return 0;
}
実行結果
s2: Hello
s3: World
s5: AAAAA

どの書き方でも最終的に生成されるのはstd::stringオブジェクトであり、用途や好みに合わせて選べます。

文字列の代入と連結

文字列の代入や連結は、整数などと同じ感覚で扱えます。

C++
#include <iostream>
#include <string>

int main() {
    std::string a = "Hello";
    std::string b = "World";

    // 代入
    a = "Hi";                        // aは"Hi"に変わる

    // 連結(+)演算子
    std::string c = a + " " + b;     // "Hi World"

    // 追記(+=)演算子
    c += "!";                        // "Hi World!"

    std::cout << c << std::endl;
    return 0;
}
実行結果
Hi World!

std::stringは長さを自動で調整してくれるため、代入や連結をしても自分でメモリを確保し直す必要はありません。

文字列の長さと空判定

文字列の長さはsize()またはlength()で取得できます。

意味は同じです。

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "Hello";

    std::cout << "size(): " << s.size() << std::endl;
    std::cout << "length(): " << s.length() << std::endl;

    if (s.empty()) {                 // 空ならtrue
        std::cout << "Empty" << std::endl;
    } else {
        std::cout << "Not empty" << std::endl;
    }
    return 0;
}
実行結果
size(): 5
length(): 5
Not empty

空文字かどうかはempty()で判定するのが分かりやすく、安全です。

文字単位のアクセスと変更

インデックス演算子とat関数

std::stringは配列のように[]で個々の文字にアクセスできます。

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "ABC";

    // 読み取り
    char c0 = s[0];                  // 'A'
    char c1 = s.at(1);               // 'B'

    // 書き換え
    s[2] = 'Z';                      // "ABZ"
    s.at(0) = 'X';                   // "XBZ"

    std::cout << s << std::endl;
    return 0;
}
実行結果
XBZ

[]とatの違いは境界チェックの有無です。

atは範囲外アクセス時にstd::out_of_range例外を投げるため、デバッグ時や安全性重視のコードではatの使用が推奨されることもあります。

ループで1文字ずつ処理する

文字列の各文字を順番に処理するには、インデックスループか範囲for文を使います。

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "Hello";

    // インデックスを使ったループ
    for (std::size_t i = 0; i < s.size(); ++i) {
        std::cout << s[i] << " ";
    }
    std::cout << std::endl;

    // 範囲for文(C++11以降)
    for (char ch : s) {
        std::cout << "[" << ch << "]";
    }
    std::cout << std::endl;

    return 0;
}
実行結果
H e l l o 
[H][e][l][l][o]

範囲for文は簡潔で読みやすく、バグも入りにくいため、C++11以降では積極的に利用するとよいです。

よく使う文字列操作メソッド

部分文字列の取得 substr

substrは、元の文字列から一部を切り出して新しい文字列を作成します。

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "Hello World";

    // 位置0から5文字
    std::string a = s.substr(0, 5);      // "Hello"

    // 位置6から末尾まで
    std::string b = s.substr(6);         // "World"

    std::cout << "a: " << a << std::endl;
    std::cout << "b: " << b << std::endl;
    return 0;
}
実行結果
a: Hello
b: World

substrの第1引数は開始位置、第2引数は長さです。

第2引数を省略すると末尾までが対象になります。

検索 find / rfind

文字列内から特定の文字や文字列を探すにはfindrfindを使います。

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "abcdeabc";

    // 先頭から検索
    std::size_t pos1 = s.find("abc");    // 0
    // 指定位置から検索
    std::size_t pos2 = s.find("abc", 1); // 5

    // 後ろから検索
    std::size_t pos3 = s.rfind("abc");   // 5

    if (pos1 != std::string::npos) {     // 見つかったか確認
        std::cout << "pos1: " << pos1 << std::endl;
    }
    std::cout << "pos2: " << pos2 << std::endl;
    std::cout << "pos3: " << pos3 << std::endl;

    return 0;
}
実行結果
pos1: 0
pos2: 5
pos3: 5

見つからなかった場合はstd::string::nposが返るため、必ずこの値との比較で判定します。

挿入 insert・削除 erase・置換 replace

文字列の一部に対して挿入、削除、置換を行うメソッドもよく使われます。

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "Hello World";

    // 挿入: 位置5に","を挿入
    s.insert(5, "," );                // "Hello, World"

    // 削除: 位置5から1文字削除
    s.erase(5, 1);                    // "Hello World"

    // 置換: 位置6から5文字を"CPP"に置き換え
    s.replace(6, 5, "CPP");           // "Hello CPP"

    std::cout << s << std::endl;
    return 0;
}
実行結果
Hello CPP

insert・erase・replaceはいずれも「位置と長さ」を指定するインターフェースで、配列操作のような感覚で扱えます。

入出力と数値変換

標準入力・出力との連携

std::stringはstd::cinstd::coutと直接やり取りできます。

C++
#include <iostream>
#include <string>

int main() {
    std::string name;

    std::cout << "名前を入力してください: ";
    std::cin >> name;                      // 空白で区切られる

    std::cout << "こんにちは、" << name << " さん" << std::endl;
    return 0;
}
実行結果
名前を入力してください: Taro
こんにちは、Taro さん

空白を含む行全体を読みたい場合はstd::getlineを使う必要があります。

C++
#include <iostream>
#include <string>

int main() {
    std::string line;

    std::cout << "1行入力してください: ";
    std::getline(std::cin, line);          // 改行までをすべて読み込む

    std::cout << "入力した行: " << line << std::endl;
    return 0;
}
実行結果
1行入力してください: Hello C++ string
入力した行: Hello C++ string

数値との相互変換

C++11以降では、文字列と数値の変換が非常に簡単になりました。

C++
#include <iostream>
#include <string>

int main() {
    std::string s1 = "123";
    std::string s2 = "3.14";

    // 文字列 → 数値
    int n = std::stoi(s1);               // "123" → 123
    double d = std::stod(s2);            // "3.14" → 3.14

    // 数値 → 文字列
    std::string s3 = std::to_string(n);  // 123 → "123"
    std::string s4 = std::to_string(d);  // 3.14 → "3.140000" など

    std::cout << "n: " << n << ", d: " << d << std::endl;
    std::cout << "s3: " << s3 << ", s4: " << s4 << std::endl;
    return 0;
}
実行結果
n: 123, d: 3.14
s3: 123, s4: 3.140000

入力文字列が数値として解釈できない場合はstd::invalid_argument例外が投げられるため、実用コードでは例外処理も検討してください。

C文字列との相互運用

c_strでconst char*を取得

古いC言語の関数や一部のライブラリは、const char*形式の文字列(C文字列)しか受け取れません。

そのような場合、std::stringのc_str()メソッドでC文字列へのポインタを取得します。

C++
#include <iostream>
#include <string>
#include <cstdio>     // printfなどのC標準入出力

int main() {
    std::string s = "Hello C API";

    // Cのprintfに渡す
    std::printf("message: %s\n", s.c_str());

    return 0;
}
実行結果
message: Hello C API

c_str()はstd::string内部バッファへのポインタを返すだけで、コピーは行いません

そのため、文字列オブジェクトが破棄された後にこのポインタを使うと未定義動作になります。

C文字列からstd::stringを作る

逆方向、つまりC文字列からstd::stringを作るのは簡単です。

コンストラクタや代入でそのまま渡せます。

C++
#include <iostream>
#include <string>

int main() {
    const char* cstr = "Hello from C string";

    // C文字列からstd::stringを生成
    std::string s1 = cstr;
    std::string s2(cstr);

    std::cout << s1 << std::endl;
    std::cout << s2 << std::endl;
    return 0;
}
実行結果
Hello from C string
Hello from C string

可能な限り早い段階でC文字列をstd::stringに変換してしまうことで、その後の処理を安全かつ簡潔にできます。

よくある注意点とベストプラクティス

文字コードとマルチバイト文字

std::string「文字の並び」ではなく「バイト列の並び」として実装されていることが多く、特にUTF-8環境では日本語1文字が複数バイトになることに注意が必要です。

そのため、size()で取得できるのは「文字数」ではなく「バイト数」である場合があります。

日本語を1文字単位で正しく扱いたい場合はstd::u32stringなどや、外部ライブラリ(ICUなど)の利用も検討すべきです。

パフォーマンスを意識した使い方の一例

大量の連結を行う場合、安易に+を繰り返すと一時オブジェクトが増えて非効率になることがあります。

そのような場合は+=reserve()での事前確保を検討します。

C++
#include <iostream>
#include <string>

int main() {
    std::string s;
    s.reserve(1000);           // 事前に1000バイト分を確保(目安)

    for (int i = 0; i < 10; ++i) {
        s += "abc";            // 連結を繰り返す
    }

    std::cout << s << std::endl;
    return 0;
}
実行結果
abcabcabcabcabcabcabcabcabcabc

小規模なコードではあまり気にしなくても構いませんが、大量の文字列処理を行う場合にはこのような工夫が効いてきます

まとめ

std::stringは、C++での文字列処理を安全かつ直感的に行うための中核的なクラスです。

本記事では、宣言と初期化、代入・連結、長さや空判定、文字単位アクセス、substr・find・insert・erase・replaceといった基本操作、標準入出力との連携や数値変換、c_strによるC文字列との橋渡しなどを一通り紹介しました。

まずは日常的な文字列処理をすべてstd::stringで書いてみることで、その便利さと表現力を実感できるはずです。

基本的な文字列操作

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!