C++で文字列をある文字で分割した文字列ビューのベクターを作成するコードです。コンセプトを使用したC++20以降用のコード、std::basic_string_view
を使用したC++17以降用のコードを記載しています。
C++20以降用のコード
あまり使う機会はありませんがbasic_string
やbasic_string_view
はテンプレート引数で文字列特性を指定できるため、その対応のためにコンセプトを使用しています。
#include <string_view> #include <vector> template <typename STRING_TYPE, typename CHAR_TRAITS = std::char_traits<wchar_t>> concept wstring_viewable = std::constructible_from<std::basic_string_view<wchar_t, CHAR_TRAITS>, STRING_TYPE>; // 文字列ビューを文字で分割した文字列ビューのベクターを作成します。 // C++20以降で使用可能です。 template <typename CHAR_TRAITS = std::char_traits<wchar_t>, wstring_viewable<CHAR_TRAITS> T> auto split_wstring_view_by_delimiter_as_vector(const T& string, wchar_t delimiter) { using STRING_VIEW_TYPE = std::basic_string_view<wchar_t, CHAR_TRAITS>; STRING_VIEW_TYPE sv{ string }; std::vector<STRING_VIEW_TYPE> result; const auto len = sv.length(); size_t i = 0; for (;;) { const auto j = sv.find(delimiter, i); result.emplace_back(sv.substr(i, j - i)); if (j == STRING_VIEW_TYPE::npos) { break; } i = j + 1; } return std::move(result); } int main() { // 文字列リテラルは静的領域に確保されるため、文字列ビューは常に有効です。 auto svv1 = split_wstring_view_by_delimiter_as_vector(L"ABC.DEF", L'.'); auto svv2 = split_wstring_view_by_delimiter_as_vector(L".ABC.DEF.", L'.'); auto svv3 = split_wstring_view_by_delimiter_as_vector(L"..ABC.DEF..", L'.'); // svv1: {"ABC", "DEF"} // svv2: {"", "ABC", "DEF", ""} // svv3: {"", "", "ABC", "DEF", "", ""} // std::wstringは動的に確保されるため、破壊後の文字列ビューは無効です。 std::wstring ws1{ L"ABC.DEF" }; auto svv4 = split_wstring_view_by_delimiter_as_vector(ws1, L'.'); // svv4: {"ABC", "DEF"} ws1.clear(); // svv4: {<無効な値>, <無効な値>} // 上記は新しい文字列のベクトル等に複製すれば回避できます(メモリは消費します)。 std::wstring ws2{ L"ABC.DEF" }; auto svv5 = split_wstring_view_by_delimiter_as_vector(ws2, L'.'); std::vector<std::wstring> sv1(svv5.cbegin(), svv5.cend()); ws2.clear(); // sv1: {"ABC", "DEF"} return 0; }
C++17以降のコード
#include <string_view> #include <vector> // 文字列ビューを文字で分割した文字列ビューのベクターを作成します。 // C++17以降で使用可能です。 template <typename CHAR_TRAITS = std::char_traits<wchar_t>> auto split_wstring_view_by_delimiter_as_vector( std::basic_string_view<wchar_t, CHAR_TRAITS> sv, wchar_t delimiter) { using STRINGVIEW_TYPE = std::basic_string_view<wchar_t, CHAR_TRAITS>; std::vector<STRINGVIEW_TYPE> result; const auto len = sv.length(); size_t i = 0; for (;;) { const auto j = sv.find(delimiter, i); result.emplace_back(sv.substr(i, j - i)); if (j == STRINGVIEW_TYPE::npos) { break; } i = j + 1; } return std::move(result); } int main() { using namespace std::string_view_literals; // 文字列リテラルは静的領域に確保されるため、文字列ビューは常に有効です。 auto svv1 = split_wstring_view_by_delimiter_as_vector(L"ABC.DEF"sv, L'.'); auto svv2 = split_wstring_view_by_delimiter_as_vector(L".ABC.DEF."sv, L'.'); auto svv3 = split_wstring_view_by_delimiter_as_vector(L"..ABC.DEF.."sv, L'.'); // svv1: {"ABC", "DEF"} // svv2: {"", "ABC", "DEF", ""} // svv3: {"", "", "ABC", "DEF", "", ""} // std::wstringは動的に確保されるため、破壊後の文字列ビューは無効です。 std::wstring ws1{ L"ABC.DEF" }; auto svv4 = split_wstring_view_by_delimiter_as_vector(std::wstring_view{ ws1 }, L'.'); // svv4: {"ABC", "DEF"} ws1.clear(); // svv4: {<無効な値>, <無効な値>} // 上記は新しい文字列のベクトル等に複製すれば回避できます(メモリは消費します)。 std::wstring ws2{ L"ABC.DEF" }; auto svv5 = split_wstring_view_by_delimiter_as_vector(std::wstring_view{ ws2 }, L'.'); std::vector<std::wstring> sv1(svv5.cbegin(), svv5.cend()); ws2.clear(); // sv1: {"ABC", "DEF"} return 0; }
注意
上記の関数は戻り値にstd::wstring_view
を使うので多様なクラスに適用できてメモリ消費も少ないですが、元の文字列が動的に確保された場合(std::wstring
、winrt::hstring
、wil::unique_something_string
等)、その文字列の解放後に取得した文字列ビューが無効となることに注意してください。これは文字列ビューベクトルのイテレーターでstd::vector<std::wstring>
等を作成することで回避できます。