From 1f8fa26d73fbfe1b5dac38a247015363c05b158600ede9a37c2789b0f338e508 Mon Sep 17 00:00:00 2001 From: ArchZer0 Date: Sun, 16 Aug 2026 16:04:27 +0800 Subject: [PATCH] best string --- CMakeLists.txt | 8 +- include/bstl/string.hpp | 92 ++++++++++ src/string.cpp | 362 ++++++++++++++++++++++++++++++++++++++++ tests/StringTest.cpp | 107 ++++++++++++ 4 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 include/bstl/string.hpp create mode 100644 src/string.cpp create mode 100644 tests/StringTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 057169b..fa21fe4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 定义源文件 set(BETTERSTL_SOURCES src/big_integer.cpp - src/big_decimal.cpp) + src/big_decimal.cpp + src/string.cpp) # 生成静态库 (.a 文件) add_library(BetterSTL_static STATIC ${BETTERSTL_SOURCES}) @@ -36,6 +37,11 @@ target_include_directories(BigDecimalTests PRIVATE include) target_link_libraries(BigDecimalTests PRIVATE BetterSTL_static) add_test(NAME BigDecimalTests COMMAND BigDecimalTests) +add_executable(StringTests tests/StringTest.cpp) +target_include_directories(StringTests PRIVATE include) +target_link_libraries(StringTests PRIVATE BetterSTL_static) +add_test(NAME StringTests COMMAND StringTests) + # 生成导出信息用于外部使用 install(TARGETS BetterSTL_static BetterSTL_shared LIBRARY DESTINATION lib diff --git a/include/bstl/string.hpp b/include/bstl/string.hpp new file mode 100644 index 0000000..1253201 --- /dev/null +++ b/include/bstl/string.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include + +namespace bstl { + +class String { +public: + String(); + String(const std::string& str); + String(const char* str); + String(const String& other) = default; + String& operator=(const String& other) = default; + + // 获取标准字符串 + std::string str() const; + const char* c_str() const; + size_t length() const; + size_t size() const; + bool empty() const; + + // 获取/修改字符 + char at(size_t pos) const; + char operator[](size_t pos) const; + char& operator[](size_t pos); + + // 分割 - 按分隔符分割字符串 + std::vector split(const String& delimiter = " ") const; + + // 连接 - 用分隔符连接多个字符串 + static String join(const std::vector& parts, const String& delimiter = ""); + + // 替换 - 替换第一个/所有匹配项 + String replace(const String& oldStr, const String& newStr, int count = -1) const; + + // 大小写转换 + String upper() const; + String lower() const; + String capitalize() const; // 首字母大写 + String title() const; // 每个单词首字母大写 + + // 判断函数 + bool startswith(const String& prefix) const; + bool endswith(const String& suffix) const; + bool contains(const String& substring) const; + bool isdigit() const; + bool isalpha() const; + bool isalnum() const; + bool isspace() const; + bool isupper() const; + bool islower() const; + + // 查找和计数 + int find(const String& substring, size_t start = 0) const; + int rfind(const String& substring) const; // 从后往前查找 + int count(const String& substring) const; + + // 去除空格和其他字符 + String strip() const; // 去除前后空格 + String lstrip() const; // 去除前导空格 + String rstrip() const; // 去除尾部空格 + String strip(const String& chars) const; // 去除指定字符 + + // 对齐和填充 + String ljust(size_t width, char fillchar = ' ') const; + String rjust(size_t width, char fillchar = ' ') const; + String center(size_t width, char fillchar = ' ') const; + + // 字符串转换 + int toInt() const; + long long toLongLong() const; + double toDouble() const; + + // 运算符重载 + String operator+(const String& other) const; + String operator+(const char* str) const; + String& operator+=(const String& other); + bool operator==(const String& other) const; + bool operator!=(const String& other) const; + bool operator<(const String& other) const; + bool operator>(const String& other) const; + bool operator<=(const String& other) const; + bool operator>=(const String& other) const; + +private: + std::string data_; +}; + +} // namespace bstl diff --git a/src/string.cpp b/src/string.cpp new file mode 100644 index 0000000..0c8b784 --- /dev/null +++ b/src/string.cpp @@ -0,0 +1,362 @@ +#include "bstl/string.hpp" + +#include +#include + +namespace bstl { + +String::String() : data_("") {} + +String::String(const std::string& str) : data_(str) {} + +String::String(const char* str) : data_(str == nullptr ? "" : str) {} + +std::string String::str() const { + return data_; +} + +const char* String::c_str() const { + return data_.c_str(); +} + +size_t String::length() const { + return data_.length(); +} + +size_t String::size() const { + return data_.size(); +} + +bool String::empty() const { + return data_.empty(); +} + +char String::at(size_t pos) const { + if (pos >= data_.size()) { + throw std::out_of_range("String::at: index out of range"); + } + return data_[pos]; +} + +char String::operator[](size_t pos) const { + return data_[pos]; +} + +char& String::operator[](size_t pos) { + return data_[pos]; +} + +std::vector String::split(const String& delimiter) const { + std::vector result; + + if (data_.empty()) { + return result; + } + + if (delimiter.empty()) { + result.push_back(*this); + return result; + } + + size_t start = 0; + size_t pos = 0; + + while ((pos = data_.find(delimiter.data_, start)) != std::string::npos) { + result.push_back(String(data_.substr(start, pos - start))); + start = pos + delimiter.size(); + } + + result.push_back(String(data_.substr(start))); + return result; +} + +String String::join(const std::vector& parts, const String& delimiter) { + if (parts.empty()) { + return String(""); + } + + std::string result = parts[0].str(); + for (size_t i = 1; i < parts.size(); i++) { + result += delimiter.str() + parts[i].str(); + } + return String(result); +} + +String String::replace(const String& oldStr, const String& newStr, int count) const { + if (oldStr.empty()) { + return *this; + } + + std::string result = data_; + size_t pos = 0; + int replaced = 0; + + while ((pos = result.find(oldStr.data_, pos)) != std::string::npos && (count < 0 || replaced < count)) { + result.replace(pos, oldStr.size(), newStr.str()); + pos += newStr.size(); + replaced++; + } + + return String(result); +} + +String String::upper() const { + std::string result = data_; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::toupper(c); }); + return String(result); +} + +String String::lower() const { + std::string result = data_; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::tolower(c); }); + return String(result); +} + +String String::capitalize() const { + if (data_.empty()) { + return *this; + } + std::string result = data_; + result[0] = std::toupper(static_cast(result[0])); + for (size_t i = 1; i < result.size(); i++) { + result[i] = std::tolower(static_cast(result[i])); + } + return String(result); +} + +String String::title() const { + std::string result = data_; + bool newWord = true; + for (size_t i = 0; i < result.size(); i++) { + if (std::isspace(static_cast(result[i]))) { + newWord = true; + } else { + if (newWord) { + result[i] = std::toupper(static_cast(result[i])); + newWord = false; + } else { + result[i] = std::tolower(static_cast(result[i])); + } + } + } + return String(result); +} + +bool String::startswith(const String& prefix) const { + if (prefix.size() > data_.size()) { + return false; + } + return data_.compare(0, prefix.size(), prefix.data_) == 0; +} + +bool String::endswith(const String& suffix) const { + if (suffix.size() > data_.size()) { + return false; + } + return data_.compare(data_.size() - suffix.size(), suffix.size(), suffix.data_) == 0; +} + +bool String::contains(const String& substring) const { + return data_.find(substring.data_) != std::string::npos; +} + +bool String::isdigit() const { + if (data_.empty()) return false; + for (unsigned char c : data_) { + if (!std::isdigit(c)) return false; + } + return true; +} + +bool String::isalpha() const { + if (data_.empty()) return false; + for (unsigned char c : data_) { + if (!std::isalpha(c)) return false; + } + return true; +} + +bool String::isalnum() const { + if (data_.empty()) return false; + for (unsigned char c : data_) { + if (!std::isalnum(c)) return false; + } + return true; +} + +bool String::isspace() const { + if (data_.empty()) return false; + for (unsigned char c : data_) { + if (!std::isspace(c)) return false; + } + return true; +} + +bool String::isupper() const { + if (data_.empty()) return false; + for (unsigned char c : data_) { + if (std::isalpha(c) && !std::isupper(c)) { + return false; + } + } + return true; +} + +bool String::islower() const { + if (data_.empty()) return false; + for (unsigned char c : data_) { + if (std::isalpha(c) && !std::islower(c)) { + return false; + } + } + return true; +} + +int String::find(const String& substring, size_t start) const { + size_t pos = data_.find(substring.data_, start); + return pos == std::string::npos ? -1 : static_cast(pos); +} + +int String::rfind(const String& substring) const { + size_t pos = data_.rfind(substring.data_); + return pos == std::string::npos ? -1 : static_cast(pos); +} + +int String::count(const String& substring) const { + if (substring.empty()) return 0; + int count = 0; + size_t pos = 0; + while ((pos = data_.find(substring.data_, pos)) != std::string::npos) { + count++; + pos += substring.size(); + } + return count; +} + +String String::strip() const { + size_t start = 0; + size_t end = data_.size(); + + while (start < end && std::isspace(static_cast(data_[start]))) { + start++; + } + while (end > start && std::isspace(static_cast(data_[end - 1]))) { + end--; + } + + return String(data_.substr(start, end - start)); +} + +String String::lstrip() const { + size_t start = 0; + while (start < data_.size() && std::isspace(static_cast(data_[start]))) { + start++; + } + return String(data_.substr(start)); +} + +String String::rstrip() const { + size_t end = data_.size(); + while (end > 0 && std::isspace(static_cast(data_[end - 1]))) { + end--; + } + return String(data_.substr(0, end)); +} + +String String::strip(const String& chars) const { + size_t start = 0; + size_t end = data_.size(); + + while (start < end && chars.data_.find(data_[start]) != std::string::npos) { + start++; + } + while (end > start && chars.data_.find(data_[end - 1]) != std::string::npos) { + end--; + } + + return String(data_.substr(start, end - start)); +} + +String String::ljust(size_t width, char fillchar) const { + if (data_.size() >= width) { + return *this; + } + std::string result = data_; + result.append(width - data_.size(), fillchar); + return String(result); +} + +String String::rjust(size_t width, char fillchar) const { + if (data_.size() >= width) { + return *this; + } + std::string result(width - data_.size(), fillchar); + result.append(data_); + return String(result); +} + +String String::center(size_t width, char fillchar) const { + if (data_.size() >= width) { + return *this; + } + size_t totalPad = width - data_.size(); + size_t leftPad = totalPad / 2; + size_t rightPad = totalPad - leftPad; + std::string result(leftPad, fillchar); + result.append(data_); + result.append(rightPad, fillchar); + return String(result); +} + +int String::toInt() const { + return std::stoi(data_); +} + +long long String::toLongLong() const { + return std::stoll(data_); +} + +double String::toDouble() const { + return std::stod(data_); +} + +String String::operator+(const String& other) const { + return String(data_ + other.data_); +} + +String String::operator+(const char* str) const { + return String(data_ + (str == nullptr ? "" : str)); +} + +String& String::operator+=(const String& other) { + data_ += other.data_; + return *this; +} + +bool String::operator==(const String& other) const { + return data_ == other.data_; +} + +bool String::operator!=(const String& other) const { + return data_ != other.data_; +} + +bool String::operator<(const String& other) const { + return data_ < other.data_; +} + +bool String::operator>(const String& other) const { + return data_ > other.data_; +} + +bool String::operator<=(const String& other) const { + return data_ <= other.data_; +} + +bool String::operator>=(const String& other) const { + return data_ >= other.data_; +} + +} // namespace bstl diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp new file mode 100644 index 0000000..3271714 --- /dev/null +++ b/tests/StringTest.cpp @@ -0,0 +1,107 @@ +#include + +#include +#include + +int main() { + using bstl::String; + + // 基本构造和获取 + String s1("Hello, World!"); + assert(s1.length() == 13); + assert(s1.c_str() == std::string("Hello, World!")); + + // split 分割 + String s2("apple,banana,cherry"); + auto parts = s2.split(","); + assert(parts.size() == 3); + assert(parts[0] == String("apple")); + assert(parts[1] == String("banana")); + assert(parts[2] == String("cherry")); + + // join 连接 + std::vector words = {String("Hello"), String("World")}; + String joined = String::join(words, " "); + assert(joined == String("Hello World")); + + // replace 替换 + String s3("hello hello world"); + String replaced = s3.replace("hello", "hi"); + assert(replaced == String("hi hi world")); + + String replaced1 = s3.replace("hello", "hi", 1); + assert(replaced1 == String("hi hello world")); + + // upper/lower + String s4("HeLLo WoRLd"); + assert(s4.upper() == String("HELLO WORLD")); + assert(s4.lower() == String("hello world")); + + // capitalize + String s5("hello"); + assert(s5.capitalize() == String("Hello")); + + // title + String s6("hello world"); + assert(s6.title() == String("Hello World")); + + // startswith/endswith + String s7("hello.txt"); + assert(s7.startswith("hello")); + assert(s7.endswith(".txt")); + assert(!s7.startswith(".txt")); + + // contains + assert(s7.contains("llo")); + assert(!s7.contains("xyz")); + + // isdigit/isalpha/isalnum + assert(String("12345").isdigit()); + assert(!String("123a").isdigit()); + assert(String("abc").isalpha()); + assert(!String("abc1").isalpha()); + assert(String("abc123").isalnum()); + + // isupper/islower + assert(String("HELLO").isupper()); + assert(String("hello").islower()); + assert(!String("Hello").isupper()); + + // find/rfind/count + assert(s2.find("banana") == 6); + assert(s2.find("xyz") == -1); + assert(s3.count("hello") == 2); + + // strip + String s8(" hello world "); + assert(s8.strip() == String("hello world")); + assert(s8.lstrip() == String("hello world ")); + assert(s8.rstrip() == String(" hello world")); + + // ljust/rjust/center + String s9("hi"); + assert(s9.ljust(5, '.') == String("hi...")); + assert(s9.rjust(5, '.') == String("...hi")); + assert(s9.center(5, '.') == String(".hi..")); + + // 字符串转换 + assert(String("123").toInt() == 123); + assert(String("456789").toLongLong() == 456789); + assert(String("3.14").toDouble() == 3.14); + + // 运算符 + String sa("Hello"); + String sb(" World"); + String sc = sa + sb; + assert(sc == String("Hello World")); + + sa += sb; + assert(sa == String("Hello World")); + + // 比较 + assert(String("abc") < String("def")); + assert(String("xyz") > String("abc")); + + std::cout << "All String tests passed!\n"; + return 0; +}