OSDN Git Service

[Feature] 2バイト文字を考慮しつつ部分文字列を切り出す関数
authorHabu <habu1010+github@gmail.com>
Thu, 26 Jan 2023 14:41:53 +0000 (23:41 +0900)
committerHabu <habu1010+github@gmail.com>
Thu, 26 Jan 2023 15:01:14 +0000 (00:01 +0900)
2バイト文字を考慮しつつ部分文字列を切り出す関数 str_substr を実装する。
str_substr で trim_kanji 関数を置き換える。また、str_separate 関数の処理で
str_substr を使用するように変更する。

src/knowledge/knowledge-inventory.cpp
src/util/string-processor.cpp
src/util/string-processor.h

index 9c8ee7e..cb56d49 100644 (file)
@@ -155,7 +155,7 @@ static void do_cmd_knowledge_inventory_aux(PlayerType *player_ptr, FILE *fff, It
     char tmp_item_name[MAX_NLEN];
     describe_flavor(player_ptr, tmp_item_name, o_ptr, OD_NAME_ONLY);
     constexpr auto max_item_length = 26;
-    auto item_name = trim_kanji(std::string(tmp_item_name), max_item_length);
+    auto item_name = str_substr(tmp_item_name, 0, max_item_length);
     std::stringstream ss;
     ss << item_name;
     const int item_length = ss.tellp();
index 48e2f11..5f42c36 100644 (file)
@@ -714,11 +714,9 @@ std::vector<std::string> str_split(std::string_view str, char delim, bool trim,
 std::vector<std::string> str_separate(std::string_view str, size_t len)
 {
     std::vector<std::string> result;
-    std::vector<char> buf(len + 1);
 
     while (!str.empty()) {
-        angband_strcpy(buf.data(), str.data(), buf.size());
-        result.emplace_back(buf.data());
+        result.push_back(str_substr(str, 0, len));
         str.remove_prefix(result.back().size());
     }
 
@@ -753,42 +751,79 @@ std::string str_erase(std::string str, std::string_view erase_chars)
     return str;
 }
 
-/*
- * @brief 2バイト文字を考慮して指定バイト数で文字列を区切る
- * @param str 元文字列
- * @param count 文字列長 (nulloptは、「最終1バイトが2バイト文字の1バイト目だった時だけその1バイトを削る」の意味)
- * @return 上記を実施したイミュータブルな文字列
- */
-std::string trim_kanji(const std::string &str, std::optional<int> count)
+static std::pair<size_t, size_t> adjust_substr_pos(std::string_view sv, size_t pos, size_t n)
 {
-    const auto max_chars = count.has_value() ? count.value() : static_cast<int>(str.length());
-#ifdef JP
-    auto c = str.data();
-    auto should_trim = false;
-    for (auto seek = 0; seek < max_chars; seek++) {
-        if (*c == '\0') {
-            break;
-        }
+    const auto start = std::min(pos, sv.length());
+    const auto end = n == std::string_view::npos ? sv.length() : std::min(pos + n, sv.length());
 
-        if (!iskanji(*c)) {
-            c++;
-            continue;
+#ifdef JP
+    auto seek_pos = 0U;
+    while (seek_pos < start) {
+        if (iskanji(sv[seek_pos])) {
+            ++seek_pos;
         }
+        ++seek_pos;
+    }
+    const auto mb_pos = seek_pos;
 
-        should_trim = true;
-        c++;
-        if ((*c == '\0') || (seek == max_chars - 1)) {
-            break;
+    while (seek_pos < end) {
+        if (iskanji(sv[seek_pos])) {
+            if (seek_pos == end - 1) {
+                break;
+            }
+            ++seek_pos;
         }
-
-        should_trim = false;
-        c++;
-        seek++;
+        ++seek_pos;
     }
+    const auto mb_n = seek_pos - mb_pos;
 
-    const auto trimed_length = should_trim ? max_chars - 1 : max_chars;
-    return str.substr(0, trimed_length);
+    return { mb_pos, mb_n };
 #else
-    return str.substr(0, max_chars);
+    return { start, end - start };
 #endif
 }
+
+/*!
+ * @brief 2バイト文字を考慮して部分文字列を取得する
+ *
+ * 引数で与えられた文字列から pos バイト目から n バイトの部分文字列を取得する。
+ * 但し、以下の通り2バイト文字の途中で分断されないようにする。
+ * - 開始位置 pos バイト目が2バイト文字の後半バイトの場合は pos+1 バイト目を開始位置とする。
+ * - 終了位置 pos+n バイト目が2バイト文字の前半バイトの場合は pos+n-1 バイト目を終了位置とする。
+ *
+ * @param sv 文字列
+ * @param pos 部分文字列の開始位置
+ * @param n 部分文字列の長さ
+ * @return 部分文字列
+ */
+std::string str_substr(std::string_view sv, size_t pos, size_t n)
+{
+    const auto [mb_pos, mb_n] = adjust_substr_pos(sv, pos, n);
+    return std::string(sv.substr(mb_pos, mb_n));
+}
+
+/*!
+ * @brief 2バイト文字を考慮して部分文字列を取得する
+ *
+ * 引数で与えられた文字列を pos バイト目から n バイトの部分文字列にして返す。
+ * 但し、以下の通り2バイト文字の途中で分断されないようにする。
+ * - 開始位置 pos バイト目が2バイト文字の後半バイトの場合は pos+1 バイト目を開始位置とする。
+ * - 終了位置 pos+n バイト目が2バイト文字の前半バイトの場合は pos+n-1 バイト目を終了位置とする。
+ *
+ * @param str 文字列
+ * @param pos 部分文字列の開始位置
+ * @param n 部分文字列の長さ
+ * @return 部分文字列
+ */
+std::string str_substr(std::string &&str, size_t pos, size_t n)
+{
+    const auto [mb_pos, mb_n] = adjust_substr_pos(str, pos, n);
+    str.erase(mb_pos + mb_n);
+    str.erase(0, mb_pos);
+    return std::move(str);
+}
+
+std::string str_substr(const char *str, size_t pos, size_t n)
+{
+    return str_substr(std::string_view(str), pos, n);
+}
index 12289fb..97034b6 100644 (file)
@@ -1,7 +1,6 @@
 #pragma once
 
 #include "system/angband.h"
-#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -36,4 +35,6 @@ std::string str_ltrim(std::string_view str);
 std::vector<std::string> str_split(std::string_view str, char delim, bool trim = false, int num = 0);
 std::vector<std::string> str_separate(std::string_view str, size_t len);
 std::string str_erase(std::string str, std::string_view erase_chars);
-std::string trim_kanji(const std::string &str, std::optional<int> count = std::nullopt);
+std::string str_substr(std::string_view sv, size_t pos = 0, size_t n = std::string_view::npos);
+std::string str_substr(std::string &&str, size_t pos = 0, size_t n = std::string_view::npos);
+std::string str_substr(const char *str, size_t pos = 0, size_t n = std::string_view::npos);