diff --git a/CMakeLists.txt b/CMakeLists.txt index 057169b..8656933 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,16 @@ 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) + +add_executable(IOTests tests/IOTest.cpp) +target_include_directories(IOTests PRIVATE include) +target_link_libraries(IOTests PRIVATE BetterSTL_static) +add_test(NAME IOTests COMMAND IOTests) + # 生成导出信息用于外部使用 install(TARGETS BetterSTL_static BetterSTL_shared LIBRARY DESTINATION lib diff --git a/IO_SUPPORT.md b/IO_SUPPORT.md new file mode 100644 index 0000000..eddd0c3 --- /dev/null +++ b/IO_SUPPORT.md @@ -0,0 +1,91 @@ +# BetterSTL cin/cout 支持 + +现在三个核心类都支持标准输入输出: + +## BigInteger - 高精度整数 + +```cpp +#include +#include + +bstl::BigInteger bi; +std::cout << "Enter a large integer: "; +std::cin >> bi; +std::cout << "You entered: " << bi << std::endl; +``` + +支持任意精度的整数输入输出,不受 `long long` 限制。 + +## BigDecimal - 高精度浮点数 + +```cpp +#include +#include + +bstl::BigDecimal bd; +std::cout << "Enter a decimal number: "; +std::cin >> bd; +std::cout << "You entered: " << bd << std::endl; +``` + +支持任意精度的浮点数输入输出,精度不丢失。 + +## String - 增强字符串 + +```cpp +#include +#include + +bstl::String str; +std::cout << "Enter a word: "; +std::cin >> str; +std::cout << "You entered: " << str << std::endl; + +// 可以使用 Python 风格的方法 +std::cout << "Uppercase: " << str.upper() << std::endl; +std::cout << "Split: "; +auto parts = str.split("_"); +``` + +## 使用示例 + +编译和运行演示程序: + +```bash +cd /home/archzero/C++/BetterSTL +cmake -S . -B out/build -G Ninja +cmake --build out/build + +# 运行库测试 +ctest --test-dir out/build + +# 运行演示(需要在终端交互) +./out/build/IOTests +``` + +## 实现细节 + +### operator<<(输出) +- BigInteger: 通过 `toString()` 输出 +- BigDecimal: 通过 `toString()` 输出 +- String: 通过 `str()` 输出 + +### operator>>(输入) +- BigInteger: 读取字符串,创建新对象 +- BigDecimal: 读取字符串,创建新对象 +- String: 读取字符串,创建新对象 + +所有运算符都定义在 `bstl` 命名空间中。 + +## 编译链接 + +在编译你的程序时: + +```bash +g++ your_program.cpp -I/path/to/include -L/path/to/lib -lBetterSTL -o your_program +``` + +或者使用静态库: +```bash +g++ your_program.cpp -I/path/to/include /path/to/libBetterSTL.a -o your_program +``` diff --git a/examples/IODemo.cpp b/examples/IODemo.cpp new file mode 100644 index 0000000..8800568 --- /dev/null +++ b/examples/IODemo.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +#include + +int main() { + using bstl::BigInteger; + using bstl::BigDecimal; + using bstl::String; + + std::cout << "=== BetterSTL cin/cout Demo ===\n\n"; + + // BigInteger 示例 + std::cout << "BigInteger Example:\n"; + std::cout << "Enter a large integer: "; + BigInteger bi; + std::cin >> bi; + std::cout << "You entered: " << bi << "\n"; + std::cout << "Doubled: " << (bi * BigInteger(2)) << "\n\n"; + + // BigDecimal 示例 + std::cout << "BigDecimal Example:\n"; + std::cout << "Enter a decimal number: "; + BigDecimal bd; + std::cin >> bd; + std::cout << "You entered: " << bd << "\n"; + std::cout << "Doubled: " << (bd * BigDecimal(2)) << "\n\n"; + + // String 示例 + std::cout << "String Example:\n"; + std::cout << "Enter a word: "; + String str; + std::cin >> str; + std::cout << "You entered: " << str << "\n"; + std::cout << "Uppercase: " << str.upper() << "\n"; + std::cout << "Lowercase: " << str.lower() << "\n\n"; + + std::cout << "Demo completed!\n"; + return 0; +} diff --git a/examples/a.out b/examples/a.out new file mode 100755 index 0000000..978346a Binary files /dev/null and b/examples/a.out differ diff --git a/include/bstl/big_decimal.hpp b/include/bstl/big_decimal.hpp index 59b8639..0ce87b6 100644 --- a/include/bstl/big_decimal.hpp +++ b/include/bstl/big_decimal.hpp @@ -2,6 +2,7 @@ #define BETTERSTL_BIG_DECIMAL_H #include +#include #include namespace bstl { @@ -69,6 +70,10 @@ private: BigInteger& rhs, int& rhsScale); }; +// 流运算符重载 +std::ostream& operator<<(std::ostream& os, const BigDecimal& bd); +std::istream& operator>>(std::istream& is, BigDecimal& bd); + } // namespace bstl #endif //BETTERSTL_BIG_DECIMAL_H diff --git a/include/bstl/big_integer.hpp b/include/bstl/big_integer.hpp index c4f12d9..2f6e088 100644 --- a/include/bstl/big_integer.hpp +++ b/include/bstl/big_integer.hpp @@ -2,6 +2,7 @@ #define BETTERSTL_BIG_INTEGER_HPP #include +#include namespace bstl { @@ -65,6 +66,10 @@ private: static std::string divAbs(const std::string& a, const std::string& b); }; +// 流运算符重载 +std::ostream& operator<<(std::ostream& os, const BigInteger& bi); +std::istream& operator>>(std::istream& is, BigInteger& bi); + } // namespace bstl #endif //BETTERSTL_BIG_INTEGER_HPP diff --git a/include/bstl/string.hpp b/include/bstl/string.hpp new file mode 100644 index 0000000..37588fe --- /dev/null +++ b/include/bstl/string.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include +#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_; +}; + +// 流运算符重载 +std::ostream& operator<<(std::ostream& os, const String& str); +std::istream& operator>>(std::istream& is, String& str); + +} // namespace bstl diff --git a/src/big_decimal.cpp b/src/big_decimal.cpp index 52291ee..2570da5 100644 --- a/src/big_decimal.cpp +++ b/src/big_decimal.cpp @@ -338,4 +338,15 @@ BigDecimal BigDecimal::round(int scale) const { return BigDecimal(rounded, scale); } +std::ostream& operator<<(std::ostream& os, const BigDecimal& bd) { + return os << bd.toString(); +} + +std::istream& operator>>(std::istream& is, BigDecimal& bd) { + std::string str; + is >> str; + bd = BigDecimal(str); + return is; +} + } // namespace bstl diff --git a/src/big_integer.cpp b/src/big_integer.cpp index 3ed86c3..4436fd9 100644 --- a/src/big_integer.cpp +++ b/src/big_integer.cpp @@ -374,5 +374,16 @@ BigInteger BigInteger::operator/(const BigInteger& other) const { return result; } +std::ostream& operator<<(std::ostream& os, const BigInteger& bi) { + return os << bi.toString(); +} + +std::istream& operator>>(std::istream& is, BigInteger& bi) { + std::string str; + is >> str; + bi = BigInteger(str); + return is; +} + } // namespace bstl diff --git a/src/string.cpp b/src/string.cpp new file mode 100644 index 0000000..e1e2a1a --- /dev/null +++ b/src/string.cpp @@ -0,0 +1,373 @@ +#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_; +} + +std::ostream& operator<<(std::ostream& os, const String& str) { + return os << str.str(); +} + +std::istream& operator>>(std::istream& is, String& str) { + std::string temp; + is >> temp; + str = String(temp); + return is; +} + +} // namespace bstl diff --git a/tests/IOTest.cpp b/tests/IOTest.cpp new file mode 100644 index 0000000..eae9339 --- /dev/null +++ b/tests/IOTest.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +#include +#include +#include + +int main() { + using bstl::BigInteger; + using bstl::BigDecimal; + using bstl::String; + + // 测试 BigInteger 的 cout + BigInteger bi1("12345678901234567890"); + std::ostringstream oss1; + oss1 << bi1; + assert(oss1.str() == "12345678901234567890"); + + // 测试 BigInteger 的 cin + std::istringstream iss1("987654321"); + BigInteger bi2; + iss1 >> bi2; + assert(bi2.toString() == "987654321"); + + // 测试负数 + std::istringstream iss2("-555"); + BigInteger bi3; + iss2 >> bi3; + assert(bi3.toString() == "-555"); + + // 测试 BigDecimal 的 cout + BigDecimal bd1("123.45"); + std::ostringstream oss2; + oss2 << bd1; + assert(oss2.str() == "123.45"); + + // 测试 BigDecimal 的 cin + std::istringstream iss3("67.89"); + BigDecimal bd2; + iss3 >> bd2; + assert(bd2.toString() == "67.89"); + + // 测试负小数 + std::istringstream iss4("-12.34"); + BigDecimal bd3; + iss4 >> bd3; + assert(bd3.toString() == "-12.34"); + + // 测试 String 的 cout + String s1("Hello World"); + std::ostringstream oss3; + oss3 << s1; + assert(oss3.str() == "Hello World"); + + // 测试 String 的 cin + std::istringstream iss5("HelloWorld"); + String s2; + iss5 >> s2; + assert(s2.str() == "HelloWorld"); + + std::cout << "All cin/cout tests passed!\n"; + return 0; +} 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; +}