Newer
Older
libkcpp / modules / src / kcpp_string.cpp
#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 &regex, const String &replacement) const
    {
        std::regex re(&regex.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 &regex, const String &replacement) const
    {
        std::regex re(&regex.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);
    }
}