C++入門|stringクラスの便利な使い方

文字数を測り、狙った文字を読み取り、必要な部分だけを切り出す。stringを使えば、技名や修行メッセージを自在に操れます。

C++で文字列を扱うときに、よく使われるのがstringクラスです。

これまでのプログラムでも、戦士名、技名、流派名、修行メッセージなどを保存するためにstringを使ってきました。

stringは、文字列を保存するだけの入れ物ではありません。

文字数を調べたり、特定の位置にある文字を取り出したり、文字列の一部分だけを切り出したりできます。

さらに、C言語で使われるchar型の配列からstringへ変換したり、stringの内容をC言語形式の文字列として取り出したりすることもできます。

ドラゴンボール風にたとえるなら、stringは神殿にある文字列解析スカウターです。

技名全体を記録するだけでなく、その技名が何文字あるのかを測定し、特定の位置にある文字を読み取り、必要な部分だけを抽出できます。

今回確認する主な機能は、次のとおりです。

機能役割
length文字列の長さを取得する
sizelengthと同じように文字列の長さを取得する
添字指定した位置の文字を取得する
substr文字列の一部分を切り出す
コンストラクタchar型の文字列からstringを作る
c_strstringをC言語形式の文字列として取り出す

stringクラスを使う準備

stringクラスを使うには、stringヘッダーをインクルードします。

#include <string>

画面に文字列を表示するため、iostreamも使用します。

#include <iostream>

今回のサンプルでは、標準名前空間を使います。

using namespace std;

string型の変数は、次のように宣言して初期化できます。

string techniqueName = "Sky Burst";

この場合、techniqueNameにはSky Burstという文字列が保存されます。

stringはクラスですが、基本的な変数とよく似た感覚で扱えます。

string message;

message = "Training Start";

文字列同士を比較したり、連結したり、さまざまなメンバ関数を呼び出したりできる点が、char型の配列よりも扱いやすいところです。

stringクラスの便利な機能を確認する

最初のサンプルでは、技名としてSky Burstを保存します。

その文字列に対して、次の処理を行います。

文字列全体を表示します。
文字列の長さを調べます。
先頭の文字を取得します。
添字4の文字を取得します。
添字4から5文字を切り出します。

プロジェクト/ファイル名: Chap7_04/technique_string.cpp

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char** argv) {
    // 解析する技名
    string techniqueName = "Sky Burst";

    // 文字列全体を表示する
    cout << "技名[" << techniqueName << "]" << endl;

    // 文字列の長さを取得する
    cout << "文字数:" << techniqueName.length() << endl;

    // 先頭の文字を取得する
    cout << "先頭の文字:" << techniqueName[0] << endl;

    // 添字4の文字を取得する
    cout << "添字4の文字:" << techniqueName[4] << endl;

    // 添字4から5文字を切り出す
    cout << "添字4から5文字:"
         << techniqueName.substr(4, 5) << endl;

    return 0;
}

実行結果

技名[Sky Burst]
文字数:9
先頭の文字:S
添字4の文字:B
添字4から5文字:Burst

Sky Burstは、空白を含めて9文字です。

文字列中の空白も1文字として数えられます。

また、文字列の位置を表す添字は0から始まります。

そのため、先頭のSは添字0です。
空白は添字3です。
Bは添字4です。

図:stringの文字数と添字

この図が示していること

この図では、Sky Burstを1文字ずつ区切り、それぞれの添字を表示しています。

文字列の長さは9です。
添字は0から始まるため、最後の文字tの添字は8になります。

先頭の文字を取得する場合は添字0を使います。
Bを取得する場合は添字4を使います。

lengthで文字列の長さを取得する

文字列の長さを調べるには、lengthを使います。

techniqueName.length()

サンプルで使用している文字列は、次の内容です。

Sky Burst

文字数を分けて確認すると、次のようになります。

S k y   B u r s t
1 2 3 4 5 6 7 8 9

S、k、yの3文字に加えて、空白が1文字あります。

そのあとに、B、u、r、s、tの5文字があります。

したがって、合計は9文字です。

lengthのイメージを図で表すと、次のようになります。

lengthは、文字列に含まれる要素数を返します。

戻り値は整数として扱えます。

cout << techniqueName.length() << endl;

sizeでも文字列の長さを取得できる

stringには、lengthと同じように文字列の長さを返すsizeもあります。

techniqueName.size()

今回の文字列では、どちらも9を返します。

cout << techniqueName.length() << endl;
cout << techniqueName.size() << endl;

実行結果は次のようになります。

9
9

stringでは、lengthとsizeは同じ結果になります。

メンバ関数得られる結果
techniqueName.length()文字列の長さ
techniqueName.size()文字列の長さ

文字列の長さを調べていることを分かりやすく表現したい場合は、lengthという名前が直感的です。

一方、vectorなどほかのSTLコンテナと書き方をそろえたい場合は、sizeが使われることもあります。

添字を使って特定の文字を取得する

stringでは、配列と同じように角括弧と添字を使って、特定の位置の文字を取得できます。

techniqueName[0]

添字は0から始まります。

Sky Burstの各文字と添字は、次のようになります。

文字    S  k  y  空白  B  u  r  s  t
添字    0  1  2   3    4  5  6  7  8

先頭の文字Sを取得する場合は、添字0を指定します。

techniqueName[0]

添字4を指定すると、Bを取得できます。

techniqueName[4]

特定の位置の文字を取得するイメージは、次のとおりです。

ここで注意したいのは、日常的な数え方と添字の違いです。

添字4のBは、文字列全体では5文字目にあります。
添字は0から始まるため、位置の数え方が1つずれます。

日常的な数え方添字文字
1文字目0S
2文字目1k
3文字目2y
4文字目3空白
5文字目4B

存在しない添字を指定しない

Sky Burstの長さは9なので、有効な添字は0から8までです。

有効な添字:0~8

添字9は文字列の範囲外です。

techniqueName[9]

範囲外の添字を角括弧で指定した場合、安全に文字を取得できる保証はありません。

そのため、添字を使う前に、lengthやsizeで文字列の長さを確認することが大切です。

if (index < techniqueName.length()) {
    cout << techniqueName[index] << endl;
}

このif文では、indexが文字列の長さより小さい場合だけ、文字を取得しています。

atで範囲を確認しながら取得する

特定の位置の文字を取得する方法には、atもあります。

techniqueName.at(4)

この処理では、添字4のBを取得できます。

角括弧とatの大きな違いは、範囲外の位置を指定した場合の動作です。

atは範囲外の位置を指定すると、std::out_of_rangeという例外を送出します。

取得方法範囲確認
techniqueName[4]範囲外を自動では検査しない
techniqueName.at(4)範囲外なら例外を送出する

今回の中心は角括弧による取得ですが、安全性を高めたい場面ではatも利用できます。

substrで文字列の一部分を切り出す

文字列の一部分だけを取得したいときは、substrを使います。

基本的な形は次のとおりです。

文字列.substr(開始位置, 文字数)

第1引数には、切り出しを開始する添字を指定します。

第2引数には、そこから何文字を切り出すかを指定します。

今回のサンプルでは、次のように使用しています。

techniqueName.substr(4, 5)

開始位置は添字4です。

Sky Burstの添字4にある文字はBです。

そこから5文字を切り出すため、結果はBurstになります。

第2引数は終了位置ではありません。

何文字取り出すかを表します。

この点は、間違えやすいので注意しましょう。

指定意味
substr(4, 5)添字4から5文字を取得する
substr(4, 3)添字4から3文字を取得する
substr(0, 3)先頭から3文字を取得する

図:substrによる部分文字列の切り出し

この図が示していること

この図では、substr(4, 5)によって、添字4から5文字を切り出す流れを表しています。

添字4にあるBから切り出しを開始し、B、u、r、s、tの5文字を取得します。

第2引数の5は、添字5までという意味ではありません。切り出す文字数が5文字という意味です。

substrの第2引数を省略する

substrでは、第2引数を省略することもできます。

techniqueName.substr(4)

この場合、添字4から文字列の最後までを切り出します。

Sky Burstでは、結果はBurstです。

Sky Burst
    ↑
  添字4から最後まで

結果:Burst

途中から末尾までを取り出したい場合は、第2引数を省略すると簡潔に書けます。

stringとchar型文字列の違い

C++では、文字列を扱うためにstringを使えます。

一方、C言語では、一般的にchar型の配列を使って文字列を表します。

char message[] = "Training Start";

C++ではC言語向けの関数やライブラリを利用することもあります。

そのような場面では、char型の文字列とstringの間でデータを受け渡す必要があります。

文字列の表現主に使われる場面
stringC++の文字列処理
char型の配列C言語形式の文字列処理
const char*C言語形式の読み取り専用文字列へのポインタ

stringは、長さの管理や連結、比較などをクラス側が行ってくれるため、C++ではとても扱いやすい文字列型です。

char型の配列からstringへ変換する

char型の文字列からstringを作るには、stringのコンストラクタへchar型の文字列を渡します。

char message[] = "Training Start";
string trainingMessage(message);

trainingMessageには、Training Startと同じ内容が保存されます。

この変換は、C言語形式の文字列をC++のstringとして扱いたい場合に便利です。

stringからC言語形式の文字列を取得する

stringの内容をC言語形式で利用したいときは、c_strを使います。

trainingMessage.c_str()

c_strの戻り値は、文字列の内容を指すconst char*です。

読み取り専用なので、取得した文字列の内容を直接書き換えることはできません。

const char* text = trainingMessage.c_str();

printfの%sへ渡すこともできます。

printf("%s\n", trainingMessage.c_str());

char型文字列との変換を確認するサンプル

次のプログラムでは、char型の配列からstringを作り、そのあとstringをc_strでC言語形式の文字列としてprintfへ渡します。

プロジェクト/ファイル名: Chap7_05/string_char_conversion.cpp

#include <iostream>
#include <string>
#include <cstdio>

using namespace std;

int main(int argc, char** argv) {
    // C言語形式のchar型文字列
    char oldMessage[] = "Training Start";

    // char型文字列からstringを生成する
    string trainingMessage(oldMessage);

    // stringとして表示する
    cout << trainingMessage << endl;

    // C++のstringを用意する
    string techniqueMessage = "Power Charge";

    // c_strでC言語形式の文字列を取得して表示する
    printf("%s\n", techniqueMessage.c_str());

    return 0;
}

実行結果

Training Start
Power Charge

char型文字列からstringが作られる流れ

次の配列には、C言語形式の文字列が保存されています。

char oldMessage[] = "Training Start";

このoldMessageをstringのコンストラクタへ渡します。

string trainingMessage(oldMessage);

コンストラクタは、新しいstringオブジェクトを作るときに呼び出される特別な関数です。

この処理によって、oldMessageと同じ内容を持つtrainingMessageが作られます。

char型の文字列
Training Start
       ↓
stringのコンストラクタ
       ↓
string型の文字列
Training Start

stringのコンストラクタには、次のような形式があります。

string(const char* text);

const char*で受け取っているため、渡されたC言語形式の文字列を変更せずに読み取り、stringの内容として保存します。

c_strの戻り値

c_strは、stringが管理している文字列をC言語形式で参照するためのメンバ関数です。

techniqueMessage.c_str()

戻り値はconst char*です。

const char* text = techniqueMessage.c_str();

constが付いているため、textを通して文字列を書き換えることはできません。

text[0] = 'X';

この処理は許可されません。

c_strで取得した文字列は、読み取り専用として扱います。

c_strはchar型の配列を新しく作る処理ではない

c_strは、独立したchar型配列を新しく作って返すわけではありません。

stringが内部で管理している文字列データへのポインタを返します。

そのため、元のstringを変更すると、以前に取得したポインタをそのまま使えない場合があります。

string message = "Power";

const char* text = message.c_str();

// stringの内容を変更する
message += " Charge";

messageの変更によって内部の保存場所が変わると、textが以前指していた場所が無効になる可能性があります。

安全に使うため、c_strで得たポインタは、必要な場所ですぐに利用するのが基本です。

printf("%s\n", message.c_str());

書き換え可能なchar型配列が必要な場合

c_strの戻り値はconst char*なので、内容を変更できません。

書き換え可能なchar型配列が必要な場合は、必要な大きさの配列を用意し、文字列をコピーする処理が必要です。

ただし、単にC言語の表示関数へ文字列を渡すだけなら、c_strで十分です。

目的適した方法
printfで表示したいc_strを使う
C言語関数へ読み取り専用で渡したいc_strを使う
独立した書き換え可能な配列が必要別の配列へコピーする
C++内で文字列を編集したいstringのまま扱う

図:char型文字列とstringの相互変換

この図が示していること

この図では、char型の文字列からstringを作る流れと、stringからC言語形式の文字列を取り出す流れを表しています。

char型の文字列からstringを作る場合は、stringのコンストラクタへ文字列を渡します。

stringをC言語形式で使う場合は、c_strを呼び出してconst char*を取得します。

c_strで得られる文字列は読み取り専用なので、内容を直接変更できません。

stringを使う利点

stringは、文字列の長さを自分で管理する必要がありません。

char型の配列では、配列の大きさや終端のヌル文字を意識する必要があります。

stringでは、文字列の保存や長さの管理をクラスが行います。

さらに、メンバ関数を使ってさまざまな文字列処理を行えます。

処理stringでの書き方
長さを取得するlengthまたはsize
特定の文字を取得する角括弧またはat
一部分を切り出すsubstr
C言語形式で取得するc_str
char型文字列から作るコンストラクタへ渡す

ドラゴンボール風に考えるなら、stringは技名や修行メッセージを管理する高性能な文字列スカウターです。

単に文字を保存するだけでなく、文字数を測り、特定の位置を読み取り、必要な部分を切り出せます。

length、添字、substrの関係

今回の最初のサンプルでは、Sky Burstという文字列を調べました。

lengthによって文字列全体の長さを取得しました。

techniqueName.length()

角括弧によって特定の位置の文字を取得しました。

techniqueName[0]
techniqueName[4]

substrによって文字列の一部分を切り出しました。

techniqueName.substr(4, 5)

これらの処理は、すべて文字列中の位置を使っています。

添字は0から始まるため、文字数と最後の添字は一致しません。

文字数が9なら、最後の添字は8です。

文字数       9
有効な添字   0~8

この関係を理解しておくと、添字やsubstrを正しく使いやすくなります。

stringクラスで身につけたい感覚

stringクラスには、文字列を扱うための便利なメンバ関数が用意されています。

文字列の長さを調べるときはlengthまたはsizeを使います。

techniqueName.length()

特定の位置にある文字を取得するときは、角括弧と添字を使います。

techniqueName[0]

文字列の一部分を切り出すときは、substrを使います。

techniqueName.substr(4, 5)

char型の文字列からstringを作るときは、stringのコンストラクタへ渡します。

string trainingMessage(oldMessage);

stringをC言語形式の文字列として利用するときは、c_strを使います。

printf("%s\n", techniqueMessage.c_str());

c_strの戻り値はconst char*なので、読み取り専用として扱います。

ドラゴンボール風にたとえるなら、lengthは技名の文字数を測るスカウターです。

角括弧は、技名の特定位置へ照準を合わせる機能です。

substrは、技名の中から必要な部分だけを切り出す解析装置です。

c_strは、C++の文字列データをC言語の通信装置でも読める形式へ変換する接続機能です。

stringの便利な機能を使い分けられるようになると、入力された文章の解析、ファイル名の処理、コマンドの判定、メッセージの切り出しなど、さまざまな文字列処理を作れるようになります。