2.

C++で文字列を結合する方法|+・append・to_string・ostringstream

編集

C++で文字列を結合(連結)するには、std::string同士を+演算子でつなぐのが基本で、追記には+=、効率を重視するならappend()メソッドを使う。数値を混ぜる場合はstd::to_string()で文字列へ変換し、項目数が多い・型がさまざまなときはstd::ostringstreamを使うと書きやすい。以下、それぞれの方法と注意点を順に解説する。

この記事の要点
  • std::stringなら+演算子で直感的に結合でき、+=で末尾に追記できる。
  • 同じ変数へ繰り返し追記するなら、append()+=のほうが一時オブジェクトを作らず効率的。
  • 数値を連結するにはstd::to_string()などで明示的に文字列へ変換する必要がある(暗黙には連結されない)。
  • 多くの値や異なる型をまとめて組み立てるならstd::ostringstreamが便利。
  • C++20以降はstd::format()で書式付きの連結を簡潔に書ける。
  • C言語形式の文字列(char*)は+で連結できず、strcatはバッファあふれに注意が必要。

std::stringの「+」演算子で結合する

C++標準ライブラリのstd::stringは、+演算子による文字列結合に対応している。文字列リテラルや別のstd::stringchar1文字などをつなげられる。

#include <iostream>
#include <string>

int main() {
    std::string a = "Hello";
    std::string b = "World";
    std::string c = a + " " + b; // "Hello World"
    std::cout << c << std::endl;
}

このように複数の+を並べて一度につなぐこともできる。なお、"Hello" + "World"のように文字列リテラル同士を直接+で結ぼうとするとコンパイルエラーになる。少なくとも一方がstd::stringであれば結合できるため、上の例では先頭のastd::stringであることが効いている。

「+=」で末尾に追記する

すでにある文字列の末尾に追加していく場合は、+=演算子が使える。a + bが新しい一時的な文字列を作るのに対し、+=は左辺の文字列そのものを伸ばすため、繰り返し追記するループなどでは無駄なオブジェクト生成を抑えられる。

std::string s = "Log: ";
s += "start";    // 文字列を追記
s += ' ';         // 1文字を追記
s += "ok";       // "Log: start ok"

append()メソッドで連結する

std::string::append()は、+=と同様に末尾へ文字列を追加するメンバ関数である。追加する範囲(部分文字列の開始位置と長さ)や、同じ文字を指定個数繰り返すといった細かい指定ができる点が特徴で、用途に応じて+=と使い分ける。

std::string s = "abc";
s.append("def");       // "abcdef"
s.append(3, '-');     // "abcdef---"('-'を3個)
std::string t = "XYZ123";
s.append(t, 0, 3);    // tの先頭3文字 "XYZ" を追記

機能面では、s += "def";s.append("def");は実質的に同じ結果になる。単純な追記なら+=のほうが読みやすく、部分指定や繰り返しが必要なときにappend()を選ぶとよい。

数値を連結する場合はstd::to_string()が必要

整数や浮動小数点数を文字列に連結したいとき、std::stringに対して+で数値をそのまま足すことはできない。数値をstd::to_string()で文字列へ変換してから結合する。

#include <string>

int count = 42;
double rate = 0.5;
std::string msg = "count=" + std::to_string(count)
              + ", rate=" + std::to_string(rate);
// "count=42, rate=0.500000"

注意したいのは、std::string("x") + 42のようにintを直接足すと、意図どおりに「42」が連結されるのではなく、コンパイルエラーになるか、文字コードとして解釈されるなど思わぬ結果になり得る点である。数値は必ず明示的に変換する。なおstd::to_string(0.5)の結果は"0.500000"のように既定の桁数になるため、桁数を制御したい場合は次のstd::ostringstreamstd::format()を使う。

std::ostringstreamで連結する

連結する要素が多い、または数値・文字列が入り混じる場合は、<sstream>std::ostringstreamを使うと書きやすい。<<演算子で次々と値を流し込み、最後にstr()std::stringとして取り出す。型変換を意識せずに済み、書式の指定(桁数・基数など)も柔軟に行える。

#include <sstream>
#include <string>

int id = 7;
double price = 1980.0;
std::ostringstream oss;
oss << "id=" << id << ", price=" << price;
std::string result = oss.str();
// "id=7, price=1980"

多数の値を組み立てる処理では、+で何度も一時文字列を作るよりstd::ostringstreamのほうが見通しがよくなることが多い。

C言語形式の文字列(char*)との違いと注意

C言語由来の文字列、すなわちcharの配列やポインタ(const char*)は、+演算子では連結できない。char*同士の+はポインタの加算とみなされ、文字列の結合にはならない。Cスタイルで連結するには<cstring>strcat(またはstrncat)を使い、連結先に十分なバッファを確保しておく必要がある。

#include <cstring>

char buf[32] = "Hello";    // 終端まで含めて余裕を持つ
std::strcat(buf, " World");  // bufに連結(終端'\0'込みで収まること)
// buf は "Hello World"

strcatは連結先バッファの大きさを確認しないため、領域が足りないとバッファオーバーフローを起こし、未定義動作や重大な不具合につながる。サイズ上限を渡せるstrncatを使う、あるいは可能なら最初からstd::stringを使うほうが安全である。なおstd::stringconst char*+で混在して結合できるため、std::string("Hello") + " World"のような書き方は問題ない。

手法の比較

手法 主な用途 注意点
+演算子 少数の文字列を一度に結合 リテラル同士の+は不可。繰り返しでは一時オブジェクトが増える。
+= 同じ変数へ末尾に追記 文字列・1文字を追記可能。読みやすく効率的。
append() 部分文字列や繰り返し文字の追記 範囲・個数を細かく指定できる。単純な追記は+=でよい。
std::to_string() 数値を文字列にして連結 変換が必須。浮動小数点は既定桁数になる。
std::ostringstream 多数・多型の値の組み立て <sstream>が必要。最後にstr()で取得。
strcat(C形式) char配列の連結 バッファ確保が必須。あふれに注意。可能ならstd::stringを推奨。

std::format()で書式付きに連結する(C++20)

C++20以降では<format>std::format()が使える。プレースホルダー{}に値を埋め込む形式で、数値や文字列を型変換なしにまとめて組み立てられる。桁数や基数などの書式も指定でき、可読性が高い。

#include <format>    // C++20
#include <string>

int id = 7;
double price = 1980.0;
std::string s = std::format("id={}, price={:.2f}", id, price);
// "id=7, price=1980.00"

利用にはC++20に対応したコンパイラと標準ライブラリが必要になる。環境によってはstd::formatが未対応の場合があるため、対応状況を確認したうえで採用するとよい。

連結でよくある落とし穴

落とし穴 内容
char*+は連結にならない const char*同士の+はポインタ加算であり、文字列はつながらない。少なくとも一方をstd::stringにするか、Cスタイルならstrcat系を使う。
リテラル同士の直接結合 "a" + "b"はコンパイルエラー。なお"a" "b"のように+なしで並べると、コンパイル時に1つのリテラルへ連結される(これは演算子ではなく言語仕様)。
数値の暗黙連結はできない std::string + intは意図どおりに連結されない。必ずstd::to_string()などで変換する。
バッファオーバーフロー strcatは連結先サイズを検査しない。容量不足は未定義動作になるため、strncatstd::stringで回避する。

FAQ

Q. 「+」と「+=」「append()」はどれを使えばよいですか?
A. その場で複数の文字列を1つにまとめるなら+、既存の変数へ追記していくなら+=が読みやすく効率的です。部分文字列の指定や同じ文字の繰り返しなど細かい制御が必要なときはappend()を使います。機能的には+=append()の単純な追記は同等です。

Q. 文字列と数値を混ぜて連結するとエラーになります。なぜですか?
A. C++ではstd::stringに数値型を直接+で足せないためです。std::to_string(値)で文字列へ変換してから連結するか、std::ostringstreamや(C++20の)std::format()を使ってください。これらなら型変換を意識せずに組み立てられます。

Q. 大量の文字列を繰り返し連結すると遅くなりますか?
A. ループ内でa = a + b;を繰り返すと、その都度一時的な文字列が作られます。a += b;a.append(b);に変えると一時オブジェクトを抑えられます。あらかじめ概算サイズが分かる場合はreserve()で容量を確保しておくと、再確保の回数を減らせます。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. コメントアウト
  2. 文字列の結合/連結
  3. 変数の宣言
  4. 定数の宣言
  5. if文
  6. if文の論理演算子
  7. for文
  8. データ型(文字列以外)
  9. データ型(文字列)
  10. 配列とfor文
  11. 配列の要素数
  12. 多次元配列とfor文
  13. 多次元配列の要素数
  14. 関数の定義と呼び出し

最近更新/作成されたページ