【C++】std::basic_string::end をマスターしよう! 文字列操作を極めるための詳細解説


範囲指定に使用

std::basic_string::end は、std::basic_string::begin と組み合わせて、文字列全体をイテレートする範囲を指定するために使用されます。例えば、以下のコードは、文字列内のすべての文字をループで処理します。

#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";
  for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
    std::cout << *it;
  }
  std::cout << std::endl;
  return 0;
}

このコードは "Hello, World!" を出力します。

空文字列の場合

空文字列の場合、std::basic_string::endstd::basic_string::begin と同じイテレータを返します。つまり、空の範囲を指します。

注意事項

  • std::basic_string::end は、const 修飾されたバージョンとそうでないバージョンがあります。const バージョンは、読み取り専用であり、文字列を変更することはできません。
  • std::basic_string::end で返されるイテレータは、決して逆参照しないでください。これは、未定義の動作を引き起こす可能性があります。


以下のコードは、std::basic_string::end を使って、文字列の最後の文字を取得する方法を示しています。

#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";
  std::string::iterator it = str.end();
  --it;
  std::cout << *it << std::endl;
  return 0;
}


文字列の範囲を反復処理する

#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";

  // 文字列の範囲を反復処理し、各文字を出力
  for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
    std::cout << *it;
  }
  std::cout << std::endl;

  return 0;
}

このコードは以下の出力を生成します。

Hello, World!

最後の文字を取得する

#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";

  // 最後の文字を取得
  std::string::iterator it = str.end();
  --it;
  std::cout << *it << std::endl;

  return 0;
}
d

部分文字列を反復処理する

#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";

  // 部分文字列 "World" を反復処理し、各文字を出力
  std::string::iterator begin = str.begin() + 7; // "World" の先頭位置
  std::string::iterator end = str.end();
  for (std::string::iterator it = begin; it != end; ++it) {
    std::cout << *it;
  }
  std::cout << std::endl;

  return 0;
}
World
#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";
  std::string subStr = "World";

  // サブ文字列 "World" が str に存在するかどうかを確認
  std::string::iterator begin = str.begin();
  std::string::iterator end = str.end();
  std::string::iterator found = std::search(begin, end, subStr.begin(), subStr.end());

  if (found != end) {
    std::cout << "サブ文字列が見つかりました: " << *found << std::endl;
  } else {
    std::cout << "サブ文字列は見つかりませんでした" << std::endl;
  }

  return 0;
}
サブ文字列が見つかりました: W


std::string::size() を使用する

  • 欠点: 文字列の終端位置を取得するだけでなく、文字列の長さを取得するため、不要な計算が発生する可能性がある
  • 利点: シンプルで分かりやすい
std::string str = "Hello, World!";
std::string::size_type pos = str.size(); // 文字列の長さを取得
std::string::iterator it = str.begin() + pos; // 終端位置のイテレータを取得

std::string::c_str() を使用する

  • 欠点: C 言語との互換性が必要な場合にのみ使用すべきであり、パフォーマンス面で不利になる可能性がある
  • 利点: C 言語スタイルの文字列終端記号 (\0) を利用できる
const char* c_str = str.c_str();
while (*c_str != '\0') {
  ++c_str;
}
std::string::iterator it = str.begin() + (c_str - str.c_str()); // 終端位置のイテレータを取得

手動でインクリメントする

  • 欠点: 煩雑でエラーが発生しやすい
  • 利点: 最も軽量な方法
std::string str = "Hello, World!";
std::string::iterator it = str.begin();
for (; it != str.end(); ++it) {
  // ...
}

範囲ベース for ループを使用する (C++11 以降)

  • 欠点: C++11 以降のコンパイラが必要
  • 利点: 簡潔で読みやすい
#include <iostream>
#include <string>

int main() {
  std::string str = "Hello, World!";

  for (char c : str) {
    std::cout << c;
  }
  std::cout << std::endl;

  return 0;
}

std::ranges::begin() と std::ranges::end() を使用する (C++20 以降)

  • 欠点: C++20 以降のコンパイラが必要
  • 利点: モダンな C++ の書き方
#include <iostream>
#include <string>
#include <ranges>

int main() {
  std::string str = "Hello, World!";

  for (auto it = std::ranges::begin(str); it != std::ranges::end(str); ++it) {
    std::cout << *it;
  }
  std::cout << std::endl;

  return 0;
}
  • 簡潔で読みやすいコードが必要な場合は、範囲ベース for ループ (C++11 以降) または std::ranges::begin()std::ranges::end() (C++20 以降) を使用するのが良いでしょう。
  • パフォーマンスが重要な場合は、手動でインクリメントする方法が最速ですが、煩雑でエラーが発生しやすいことに注意が必要です。
  • C 言語との互換性が必要な場合は、std::string::c_str() を使用する必要があります。
  • シンプルで分かりやすい方法が必要な場合は、std::string::size() を使用するのが良いでしょう。