#include <cstring> #include <regex> #include <kcpp_string.hpp> // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; namespace kcpp { /** * String を構築します。 * * @param 文字列 */ String::String(const char *str) noexcept { setValue(str); } /** * String のコピーコンストラクタ。 * * @param str コピー元 String */ String::String(const String &str) noexcept { setValue(&(str.value[0])); } /** * String のムーブコンストラクタ。 * * @param str ムーブ元 String */ String::String(String &&str) noexcept : value(std::move(str.value)), len(str.len) { str.value = nullptr; str.len = 0; } /** * デストラクタ。 */ String::~String() noexcept { // NOP } /** * コピー代入演算子。 * コピーして代入します。 * * @param str コピー元 String * @return 本オブジェクトへの参照 */ String &String::operator=(const String &str) noexcept { if (this != &str) { setValue(&str.value[0]); } return *this; } /** * ムーブ代入演算子。 * * @param obj ムーブ元オブジェクト * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept { // 特に移すものはない。 if (this != &str) { value = std::move(str.value); len = str.len; str.value = nullptr; str.len = 0; } return *this; } /** * 文字列の長さを返します。 * * @return 文字列の長さ */ int String::length() const noexcept { return len; } /** * 指定された位置の文字を返します。 * * @param index 位置 * @return 文字 */ char String::charAt(int index) const { if ((index < 0) || (index >= len)) { // TODO: IndexOutOfBoundsException } return value[index]; } /** * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ String String::substring(int beginIndex, int endIndex) const { if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) { int subLen = endIndex - beginIndex; std::unique_ptr<char[]> subStr = std::make_unique<char[]>(subLen + 1); std::strncpy(&subStr[0], &value[beginIndex], subLen); subStr[subLen] = '\0'; String result(&subStr[0]); return result; } else { // TODO: IndexOutOfBoundsException return nullptr; } } /** * 指定された文字列が含まれるか否かを返します。 * * @param str 文字列 * @return true/false (含まれる/含まれない) */ bool String::contains(const String &str) const noexcept { return (std::strstr(&value[0], &str.value[0]) != nullptr); } /** * 指定された文字を置換します。 * * @param oldChar 置換前文字 * @param newChar 置換後文字 * @return 置換された文字列 */ String String::replace(char oldChar, char newChar) const noexcept { String str(*this); for (int idx = 0; idx < len; idx++) { if (str.value[idx] == oldChar) { str.value[idx] = newChar; } } return str; } // 文字列置換 String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); String str(res.c_str()); return str; } // 文字列置換 String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( &value[0], re, &replacement.value[0], std::regex_constants::match_any); String str(res.c_str()); return str; } // 分割 std::unique_ptr<String[]> String::split(const String &) const noexcept { return nullptr; } // 先頭の文字列が一致するか bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) { return false; } for (int idx = 0; idx < prefix.len; idx++) { if (value[idx] != prefix.value[idx]) { return false; } } return true; } // 末尾の文字列が一致するか bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) { return false; } int value_idx = (len - suffix.len); for (int idx = 0; idx < suffix.len; idx++) { if (value[value_idx] != suffix.value[idx]) { return false; } value_idx++; } return true; } // 小文字変換 String String::toLowerCase() const noexcept { String str(*this); for (int idx = 0; idx < len; idx++) { str.value[idx] = std::tolower(str.value[idx]); } return str; } // 大文字変換 String String::toUpperCase() const noexcept { String str(*this); for (int idx = 0; idx < len; idx++) { str.value[idx] = std::toupper(str.value[idx]); } return str; } // trim String String::trim() const noexcept { int beginIndex = 0; for (; beginIndex < len; beginIndex++) { if (value[beginIndex] > 0x20) { break; } } int endIndex = len; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) { break; } } int trimedLen = endIndex - beginIndex; std::unique_ptr<char[]> trimedStr = std::make_unique<char[]>(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; String result(&trimedStr[0]); return result; } /** * 本オブジェクトの文字列表現を返します。 * * @return 本オブジェクトの文字列表現 */ String String::toString() const noexcept { String str(*this); return str; } /** * 指定されたオブジェクトと合致するか否かを返します。 * * @param obj 比較するオブジェクト * @return true/false (合致する/しない) */ bool String::equals(const Object &obj) const noexcept { bool isSame = isSameClass(obj); if (isSame) { const String &str = dynamic_cast<const String &>(obj); if (len == str.len) { return (std::strcmp(&value[0], &str.value[0]) == 0); } } return false; } /** * ハッシュコードを取得します。 * * @return ハッシュコード */ int String::hashCode() const noexcept { int hash = 0; for (int idx = 0; idx < len; idx++) { hash = 31 * hash + value[idx]; } return hash; } /** * 出力用 * * @param os output stream * @param str 出力文字列 * @return output stream */ std::ostream &operator<<(std::ostream &os, const String &str) { if (str.value != nullptr) { os << &str.value[0]; } return os; } /** * 入力用 * * @param is input stream * @param str 入力先 String * @return input stream */ std::istream &operator>>(std::istream &is, String &str) { char buff[MAX_ISTREAM_BUFFER_SIZE]; is >> buff; str = String(buff); return is; } //////////////////////////////////////////////////////////////////////////// // // protected // /** * 文字列データを設定します。 * * @param str 設定する文字列 */ void String::setValue(const char *str) { if (str) { len = std::strlen(str); value = std::make_unique<char[]>(len + 1); std::strcpy(&value[0], str); } else { len = 0; value = std::make_unique<char[]>(1); value[0] = '\0'; } } /** * 本オブジェクトの複製を取得します。 * * [備考] * 派生クラスが、ポリモーフィズムをサポートするために、 * unique_ptr<Object> を返すようにしています。 * * @return 本オブジェクトの複製 */ std::unique_ptr<Object> String::clone() const noexcept { return std::make_unique<String>(*this); } }