#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);
}
}