【C++】`std::basic_string::substr`を使いこなそう!文字列操作の達人になるための詳細解説
- 抽出位置や抽出長が不正な場合は、例外が発生します。
- 抽出する部分文字列は、開始位置と抽出長を指定することで自由に選択できます。
- 既存の文字列オブジェクトの一部を抽出し、新しい文字列オブジェクトとして生成します。
構文
std::basic_string::substr(size_type pos, size_type len = npos);
パラメータ
len
: 部分文字列の長さを指定します。npos
を指定すると、開始位置から末尾まで抽出されます。デフォルト値はnpos
です。pos
: 部分文字列の開始位置を指定します。文字列の先頭は0、1文字目は1、...とインデックスが付けられます。デフォルト値は0です。
戻り値
- 抽出した部分文字列を含む新しい
std::basic_string
オブジェクトを返します。
例
#include <iostream>
#include <string>
int main() {
std::string str = "Hello, World!";
// 文字列 "World!" を抽出
std::string sub1 = str.substr(7, 5);
std::cout << "sub1: " << sub1 << std::endl; // 出力: sub1: World!
// 文字列の先頭から3文字を抽出
std::string sub2 = str.substr(0, 3);
std::cout << "sub2: " << sub2 << std::endl; // 出力: sub2: Hel
// 文字列 "Hello" を抽出 (デフォルトの長さ)
std::string sub3 = str.substr(0);
std::cout << "sub3: " << sub3 << std::endl; // 出力: sub3: Hello
return 0;
}
注意点
- C++23から、右辺値参照修飾オーバーロードが追加されました。従来のオーバーロードはconst左辺値参照オーバーロードに変更されています。
- 抽出された部分文字列は、元の文字列とは別のオブジェクトとして扱われます。元の文字列を変更しても、抽出された部分文字列は影響を受けません。
- 抽出位置や抽出長が不正な場合、プログラムが異常終了する可能性があります。
- 文字列を加工して新しい文字列を生成したい場合
- 文字列の一部を切り取って別の文字列に挿入したい場合
- 長い文字列から特定の部分だけを処理したい場合
特定の部分文字列を抽出
#include <iostream>
#include <string>
int main() {
std::string str = "The quick brown fox jumps over the lazy dog.";
// "fox" という部分文字列を抽出
std::string sub1 = str.substr(16, 3);
std::cout << "sub1: " << sub1 << std::endl; // 出力: sub1: fox
// "jumps over the" という部分文字列を抽出
std::string sub2 = str.substr(19, 14);
std::cout << "sub2: " << sub2 << std::endl; // 出力: sub2: jumps over the
return 0;
}
文字列の一部を切り取って別の文字列に挿入
#include <iostream>
#include <string>
int main() {
std::string str1 = "Hello";
std::string str2 = "World";
// "Hello" の後ろに "World" を挿入
str1.insert(5, str2);
std::cout << "str1: " << str1 << std::endl; // 出力: str1: HelloWorld
return 0;
}
#include <iostream>
#include <string>
int main() {
std::string str = "This is an example string.";
// 小文字に変換して新しい文字列を生成
std::string lower = str.substr(0).substr(0, str.size() - 1);
lower.transform(lower.begin(), lower.end(), ::tolower);
std::cout << "lower: " << lower << std::endl; // 出力: lower: this is an example string
// 先頭と末尾の空白を除去して新しい文字列を生成
std::string trimmed = str.substr(1, str.size() - 2);
trimmed.erase(trimmed.begin(), std::find_if(trimmed.begin(), trimmed.end(), [](char c){ return std::isspace(c); }));
trimmed.erase(std::find_if(trimmed.rbegin(), trimmed.rend(), [](char c){ return std::isspace(c); }));
std::cout << "trimmed: " << trimmed << std::endl; // 出力: trimmed: This is an example string
return 0;
}
文字列リテラル
- 例:
- 比較的単純な部分文字列の抽出であれば、文字列リテラルを使用する方が簡潔で分かりやすいコードになります。
std::string str = "Hello, World!";
std::string sub = "World!"; // 文字列リテラルによる部分文字列
// ...
std::cout << sub << std::endl; // 出力: World!
利点
- コンパイル時に部分文字列が確定するため、パフォーマンスが向上する場合がある
- 簡潔で分かりやすいコード
欠点
- コードが冗長になる可能性がある
- 複雑な抽出条件には対応できない
範囲ベース for ループ
- 例:
- 文字列全体を反復処理し、必要な部分文字列を抽出する場合に有効です。
std::string str = "Hello, World!";
std::string sub;
// "World!" 部分文字列を抽出
for (size_t i = 7; i < str.size(); ++i) {
sub += str[i];
}
// ...
std::cout << sub << std::endl; // 出力: World!
利点
- 可読性の高いコードになる
- 柔軟な抽出条件に対応できる
欠点
std::basic_string::substr
よりも処理速度が遅い場合がある
C 言語ライブラリ関数
- 例:
strncpy
やstrtok
などの C 言語ライブラリ関数を使用することもできますが、C++ 標準ライブラリよりも安全性や可読性が劣る場合があるため、注意が必要です。
#include <cstring>
std::string str = "Hello, World!";
char sub[6];
// "World!" 部分文字列を抽出
strncpy(sub, str.c_str() + 7, 5);
sub[5] = '\0';
// ...
std::cout << sub << std::endl; // 出力: World!
利点
- 処理速度が速い場合がある
欠点
- C++ 標準ライブラリとの互換性に注意が必要
- 安全性や可読性が低い
正規表現
- 例:
- 正規表現ライブラリを使用すれば、より複雑な抽出条件にも対応できますが、コードが煩雑になる可能性があります。
#include <regex>
std::string str = "Hello, World!";
std::string sub;
// "World!" 部分文字列を抽出
std::regex re("(\\w+)$");
std::smatch match;
if (std::regex_search(str, match, re)) {
sub = match[1].str();
}
// ...
std::cout << sub << std::endl; // 出力: World!
利点
- 非常に複雑な抽出条件にも対応できる
欠点
- 正規表現ライブラリの習得が必要
- コードが煩雑になる可能性がある
- Boost.String や C++ Essentials などのライブラリには、
std::basic_string::substr
の代替となる関数や、より高度な文字列操作機能が提供されている場合があります。
最適な代替方法の選択
- ライブラリの習得コスト
- コードの可読性
- 処理速度
- 抽出条件の複雑さ
などを考慮して、最適な代替方法を選択する必要があります。