C++の文字列2

stringクラスの続き

文字列の挿入

文字列の特定の場所に別の文字列を挿入するにはinsert関数を使用します。


#include <iostream>
#include <string>

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

    //2文字目にstr1を挿入
    //abABCDEFGc
    str2.insert(2, str1);
    std::cout << str2 << std::endl;

    str2 = "abc";
    //2文字目にstr1の先頭から2文字以降を挿入
    //abCDEFGc
    str2.insert(2, str1, 2);
    std::cout << str2 << std::endl;

    str2 = "abc";
    //2文字目にstr1の2文字目から3文字を挿入
    //abCDEc
    str2.insert(2, str1, 2, 3);
    std::cout << str2 << std::endl;

    str2 = "abc";
    //2文字目にstr1の2文字目から4文字手前までを挿入(範囲指定)
    //abCDc
    str2.insert(str2.begin() + 2, &str1[2], &str1[4]);
    std::cout << str2 << std::endl;

    str2 = "abc";
    //2文字目に5個のxを挿入
    //abxxxxxc
    str2.insert(2, 5, 'x');
    std::cout << str2 << std::endl;

    std::cin.get();
}

挿入はC言語では面倒な処理ですが、stringクラスならば簡単に実現できます。

基本的にassign関数などと同じですが、範囲指定挿入だけは第一引数が少し特殊な形式になっています。
これはイテレータというものですが、これについては別項で詳しく説明します。
とりあえず今は「こういうもの」と考えておいてください。

文字列の置き換え

文字列の置き換えにはreplace関数を使用します。


#include <iostream>
#include <string>

int main()
{
    std::string str1 = "ABCDEFG";
    std::string str2 = "abcdefg";

    //2文字目から3文字をstr1で置き換え
    //abABCDEFGfg
    str2.replace(2, 3, str1);
    std::cout << str2 << std::endl;

    str2 = "abcdefg";
    //2文字目から3文字をstr1の1文字目以降で置き換え
    //abBCDEFGfg
    str2.replace(2, 3, str1, 1);
    std::cout << str2 << std::endl;

    str2 = "abcdefg";
    //2文字目から3文字をstr1の1文字目から4文字分で置き換え
    //abBCDEfg
    str2.replace(2, 3, str1, 1, 4);
    std::cout << str2 << std::endl;

    str2 = "abcdefg";
    //2文字目~4文字目直前までをstr1で置き換え
    //abABCDEFGefg
    str2.replace(str2.begin() + 2, str2.begin() + 4, str1);
    std::cout << str2 << std::endl;

    str2 = "abcdefg";
    //2文字目~4文字目直前までをstr1の1文字目~4文字目直前までで置き換え
    //abBCDefg
    str2.replace(str2.begin() + 2, str2.begin() + 4, &str1[1], &str1[4]);
    std::cout << str2 << std::endl;

    str2 = "abcdefg";
    //2文字目から3文字を5個のxで置き換え
    //abxxxxxfg
    str2.replace(2, 3, 5, 'x');
    std::cout << str2 << std::endl;

    str2 = "abcdefg";
    //2文字~4文字目直前までを5個のxで置き換え
    //abxxxxxefg
    str2.replace(str2.begin() + 2, str2.begin() + 4, 5, 'x');
    std::cout << str2 << std::endl;

    std::cin.get();
}

かなり数が多いですが、覚える必要はありません。
柔軟な置き換えが可能ですので、やりたいことがあればその都度調べると良いでしょう。

文字列の削除

部分的に文字列を削除したい場合は、replace関数の置き換え対象文字列に空文字を渡します。


#include <iostream>
#include <string>

int main()
{
    std::string str = "ABCDEFG";

    //2文字目から3文字を削除
    //ABFG
    str.replace(2, 3, "");
    std::cout << str << std::endl;

    std::cin.get();
}

全ての文字列を削除したい場合はclear関数またはerase関数を使用します。


#include <iostream>
#include <string>

int main()
{
    std::string str1 = "ABC";
    std::string str2 = "DEF";

    str1.clear();
    str2.erase();

    //どちらも空
    std::cout << str1 << std::endl;
    std::cout << str2 << std::endl;

    std::cin.get();
}

文字列の検索

文字列の検索にはいくつかの関数が存在します。


#include <iostream>
#include <string>

int main()
{
    std::string str = "ABCDEFABCDEF";
    
    //BCが初めて出現する位置を検索
    //2
    std::cout << str.find("CD") << std::endl;

    //先頭から3文字目以降でBCが出現する位置を検索
    //8
    std::cout << str.find("CD", 3) << std::endl;

    //最後から数えて初めて出現する位置を検索
    //8
    std::cout << str.rfind("CD") << std::endl;

    //先頭から3文字目以前でBCが出現する位置を検索
    //2
    std::cout << str.rfind("CD", 3) << std::endl;

    //CまたはDが最初に出現する位置を検索
    //2
    std::cout << str.find_first_of("CD") << std::endl;

    //CまたはDが最後に出現する位置を検索
    //9
    std::cout << str.find_last_of("CD") << std::endl;

    //CまたはD以外が最初に出現する位置を検索
    //0
    std::cout << str.find_first_not_of("CD") << std::endl;

    //CまたはD以外が最後に出現する位置を検索
    //11
    std::cout << str.find_last_not_of("CD") << std::endl;

    std::cin.get();
}

通常の検索にはfind関数を使用します。
先頭を0文字目として、最初に見つかった位置を返します。

第二引数は検索開始位置を指定します。
検索開始位置より前に該当文字列があっても無視されます。
(こういうのをオフセットといいます)

rfind関数は、文字列の末尾から検索します。
第二引数は検索開始位置ですが、検索開始位置は文字列の先頭から数えることに注意してください。
第二引数が「3」の場合は、先頭から0~3文字目が検索対象です。

find_first_of関数は、文字列ではなく文字を検索します。
引数に指定した文字列をバラバラの文字と考え、いずれかの文字が最初に出現する位置を返します。
find_last_of関数は末尾から数える版です。

find_first_not_of関数は、引数に指定した文字以外が最初に出現する位置を返します。
find_last_not_of関数は末尾から数える版です。

find_first_of関数等も第二引数にオフセットを指定することができますが、find関数と同じなのでここでは省略しています。

また、これらの関数で得られるのはすべて文字列を先頭から数えた位置です。
rfind関数で後方から検索したら後ろから数えた位置が返ってくるとか、そういうことはないので注意しましょう。

戻り値

これらの関数の戻り値はstd::string::size_typeというデータ型です。
これは内部では符号なし整数です。

これらの検索関数は見つからなかったときにstd::string::nposという値を返します。
これは-1と定義されていますが、符号なし整数で-1というのは最大値を意味します。
(全てのビットが1であるため)

要するに、戻り値を「std::string::size_type型」で受け取って、検索の成否を0未満か否かで判定するのは間違いです。
正しくは以下のように「std::string::npos」と比較します。


#include <iostream>
#include <string>

int main()
{
    std::string str = "ABCDEFABCDEF";
    std::string::size_type index = str.find("XXX");

    if (index == std::string::npos) 
        std::cout << "文字列XXXは含まれない" << std::endl;
    else 
        std::cout << "文字列XXXの位置: " << index << std::endl;

    std::cin.get();
}

C言語形式文字列に変換

stringクラスは非常に便利ですが、C言語のライブラリやOSのAPIなどはstringクラスによる文字列を受け付けないため、char型を利用した文字列に変換する必要があります。
stringクラス文字列をchar型文字列に変換するにはc_str関数を使用します。


#include <iostream>
#include <string>

int main()
{
    std::string str = "ABCDE";
    const char *cstr = str.c_str();

    std::cout << cstr << std::endl;

    //printfができる
    printf("%s\n", cstr);

    std::cin.get();
}

c_str関数は、string文字列の末尾にNULL文字(\0)を付加し、文字列へのポインタを返します。
(const char*型)

通常はこれで問題ありませんが、変換後に元のstring文字列を操作した場合、変換後のデータも改変されます。
文字列の追加などを行った場合、終端のNULL文字が消えてしまい、char型の文字列配列として正しくない形式になってしまいます。
そのため、c_str関数は必要になる直前で呼び出し、変換文字列は再利用しないほうがいいでしょう。
(問題がないことが明確な場合は除く)


#include <iostream>
#include <string>

int main()
{
    std::string str = "ABCDE";
    const char *cstr = str.c_str();

    str += "FGHIJ";

    //予期せぬエラーが起こる可能性がある
    printf("%s\n", cstr);

    std::cin.get();
}

copy関数

どうしても変換後文字列を元のstring文字列に影響されない形で受け取りたい場合はcopy関数を使用します。
(Visual Studioでは_Copy_s関数を使用します)
これは文字通り、元の文字列のコピーを作りますので、元の文字列が変更されても影響はありません。


#include <iostream>
#include <string>

#define BUFFER 32

int main()
{
    std::string str = "ABCDE";
    char cstr[BUFFER];
    size_t len = str.length();

	str.copy(cstr, BUFFER, 0);
	
	//Visual Studioでエラーが出る場合は以下を使用
    //str._Copy_s(cstr, BUFFER, len, 0);

    cstr[len] = '\0';

    str += "FGHIJ";

    //元のstring文字列に影響されない
    printf("%s\n", cstr);

    std::cin.get();
}

第一引数にコピー先のchar型配列を指定します。
第二引数はコピー先配列のサイズを指定します。
第三引数は文字列のコピーを開始する位置を指定します。
第三引数は省略可能で、その場合は先頭からコピーを開始します。

_Copy_s関数は引数がひとつ追加されています。
第三引数は元の文字列を何文字コピーするかを指定します。
全てコピーするならば元の文字列の長さをそのまま指定します。
第四引数はcopy関数の第三引数と同じです。
(省略可能なのも同じです)

さて、これらの関数は元の文字列のコピーを作成してくれますが、末尾にNULL文字は付加されません。
そのため、手動でNULL文字を挿入してやる必要があります。
(17行目)
コピーした文字数をそのまま配列の添字に指定すればそこが文字列の終端ですから、そこにNULL文字に書き換えます。
当然ながら、コピー先の配列のサイズに余裕がなければエラーになりますので気を付けてください。

char型配列からstringクラス文字列に

stringクラスの初期化や各関数のほとんどは、char型の文字列をそのまま受け取れますので特に意識して変換する必要はありません。