C++ 字符串操作知识点详解 | Kai@Codehubble 技术笔记

1. 字符串初始化和基本属性

1.1 C++ 字符串操作介绍

C++ 标准库的 std::string 提供了灵活的字符串初始化方式,包括默认初始化(空字符串)和带初始值的初始化。字符串的基本属性包括是否为空、长度(字符数量)和容量(已分配的存储空间)。size()length() 方法返回字符串的字符数,empty() 方法检查字符串是否为空,c_str() 方法可将 std::string 转换为 C 风格字符串(以 null 结尾的字符数组)。

1.2 函数原型介绍

// 默认构造函数(空字符串)
std::string();
// 从 C 风格字符串初始化
std::string(const char* s);
// 检查字符串是否为空
bool empty() const noexcept;
// 返回字符串长度(字符数)
size_t size() const noexcept;
size_t length() const noexcept;
// 返回指向 C 风格字符串的指针
const char* c_str() const noexcept;
  • empty():返回 bool 值,true 表示字符串为空,false 表示非空。

  • size()/length():返回 size_t 类型的字符串长度,两者功能完全相同。

  • c_str():返回 const char* 指针,指向与字符串内容相同的 C 风格字符串。

1.3 GTEST 示例

TEST(StringInitializationTest, BasicProperties) {
    // 空字符串测试
    std::string empty_str;
    EXPECT_TRUE(empty_str.empty());
    EXPECT_EQ(empty_str.size(), 0u);
    EXPECT_EQ(empty_str.length(), 0u);
    // 有初始值的字符串测试
    std::string hello_str = "Hello";
    EXPECT_FALSE(hello_str.empty());
    EXPECT_EQ(hello_str.size(), 5u);
    EXPECT_EQ(hello_str.length(), 5u);
    // C字符串转换测试
    EXPECT_STREQ(hello_str.c_str(), "Hello");
}

2. 字符串拼接操作

2.1 C++ 字符串操作介绍

字符串拼接是将多个字符串合并为一个的操作。std::string 支持多种拼接方式:使用 + 运算符拼接两个字符串,使用 += 运算符在当前字符串后追加内容,还可以直接拼接 C 风格字符串或单个字符。

2.2 函数原型介绍

// 拼接两个字符串,返回新字符串
std::string operator+(const std::string& lhs, const std::string& rhs);
std::string operator+(const std::string& lhs, const char* rhs);
std::string operator+(const char* lhs, const std::string& rhs);
std::string operator+(const std::string& lhs, char rhs);
std::string operator+(char lhs, const std::string& rhs);
// 在当前字符串后追加内容,修改当前字符串
std::string& operator+=(const std::string& str);
std::string& operator+=(const char* s);
std::string& operator+=(char c);
  • + 运算符:返回拼接后的新字符串,不修改原字符串。

  • += 运算符:直接在当前字符串末尾追加内容,修改原字符串并返回引用。

2.3 GTEST 示例

TEST(StringConcatenationTest, VariousMethods) {
    std::string str1 = "Hello";
    std::string str2 = " World";
    // 使用+运算符拼接
    std::string result1 = str1 + str2;
    EXPECT_EQ(result1, "Hello World");
    // 使用+=运算符拼接
    std::string result2 = str1;
    result2 += str2;
    EXPECT_EQ(result2, "Hello World");
    // 拼接C风格字符串
    std::string result3 = str1 + " there";
    EXPECT_EQ(result3, "Hello there");
    // 拼接单个字符
    std::string result4 = str1;
    result4 += '!';
    EXPECT_EQ(result4, "Hello!");
}

3. 字符串比较操作

3.1 C++ 字符串操作介绍

字符串比较用于判断两个字符串的相等性或顺序关系。std::string 重载了比较运算符(==!=<><=>=),比较规则遵循字典序(逐个字符比较 ASCII 值),且区分大小写。

3.2 函数原型介绍

// 相等性比较
bool operator==(const std::string& lhs, const std::string& rhs) noexcept;
bool operator==(const std::string& lhs, const char* rhs);
bool operator==(const char* lhs, const std::string& rhs);
// 不等性比较
bool operator!=(const std::string& lhs, const std::string& rhs) noexcept;
bool operator!=(const std::string& lhs, const char* rhs);
bool operator!=(const char* lhs, const std::string& rhs);
// 顺序比较
bool operator<(const std::string& lhs, const std::string& rhs) noexcept;
bool operator>(const std::string& lhs, const std::string& rhs) noexcept;
bool operator<=(const std::string& lhs, const std::string& rhs) noexcept;
bool operator>=(const std::string& lhs, const std::string& rhs) noexcept;
  • 所有比较运算符均返回 bool 值,true 表示满足比较条件,false 表示不满足。
  • 比较基于字符的 ASCII 码值,例如 'A'(65)小于 'a'(97)。

3.3 GTEST 示例

TEST(StringComparisonTest, EqualityAndOrder) {
    std::string a = "apple";
    std::string b = "banana";
    std::string a2 = "apple";
    // 相等性测试
    EXPECT_EQ(a, a2);
    EXPECT_NE(a, b);
    // 顺序比较测试
    EXPECT_LT(a, b);  // "apple" < "banana"
    EXPECT_GT(b, a);  // "banana" > "apple"
    // 区分大小写测试
    std::string A = "Apple";
    EXPECT_NE(a, A);  // 大小写不同,字符串不等
    EXPECT_LT(A, a);  // 'A' (65) < 'a' (97)
}

4. 字符串查找和替换

4.1 C++ 字符串操作介绍

字符串查找用于定位子字符串或字符在目标字符串中的位置,支持正向查找、反向查找、从指定位置开始查找等多种模式。替换操作可在指定位置替换指定长度的字符为新内容。

4.2 函数原型介绍

// 正向查找子字符串,返回首次出现的位置
size_t find(const std::string& str, size_t pos = 0) const noexcept;
size_t find(const char* s, size_t pos = 0) const;
size_t find(char c, size_t pos = 0) const noexcept;
// 反向查找子字符串,返回最后出现的位置
size_t rfind(const std::string& str, size_t pos = npos) const noexcept;
size_t rfind(const char* s, size_t pos = npos) const;
size_t rfind(char c, size_t pos = npos) const noexcept;
// 查找首个匹配字符集中任意字符的位置
size_t find_first_of(const std::string& str, size_t pos = 0) const noexcept;
// 查找最后一个匹配字符集中任意字符的位置
size_t find_last_of(const std::string& str, size_t pos = npos) const noexcept;
// 查找首个不匹配字符集中任意字符的位置
size_t find_first_not_of(const std::string& str, size_t pos = 0) const noexcept;
// 替换指定位置和长度的字符
std::string& replace(size_t pos, size_t count, const std::string& str);
  • 返回值:均为 size_t 类型,表示找到的位置索引;若未找到,返回 std::string::npos

  • 参数

    • pos:起始查找位置(默认从 0 开始)。

    • count:替换的字符长度(用于 replace 方法)。

    • str/s/c:要查找或替换的内容(字符串、C 风格字符串或字符)。

4.3 GTEST 示例

TEST(StringSearchTest, FindAndReplace) {
    std::string str = "Hello, world! Hello, C++!";
    // 查找子字符串
    size_t pos1 = str.find("world");
    EXPECT_EQ(pos1, 7u);
    // 查找单个字符
    size_t pos2 = str.find('!');
    EXPECT_EQ(pos2, 12u);
    // 从指定位置开始查找
    size_t pos3 = str.find("Hello", 1);
    EXPECT_EQ(pos3, 14u);
    // 查找不存在的子字符串
    size_t pos4 = str.find("Python");
    EXPECT_EQ(pos4, std::string::npos);
    // 替换子字符串
    std::string replaced = str;
    replaced.replace(pos1, 5, "there");  // 从pos1开始替换5个字符
    EXPECT_EQ(replaced, "Hello, there! Hello, C++!");
}
TEST(StringManipulationTest, FindEnhance) {
    // 倒序查找
    std::string str = "Hello, world! Hello, C++!";
    size_t pos1 = str.rfind("world");
    EXPECT_EQ(pos1, 7u);
    // 查找首个字符串任何一个字符出现的首个位置
    size_t pos2 = str.find_first_of("Hello");
    EXPECT_EQ(pos2, 0u);
    size_t pos3 = str.find_first_of("Hello", 3);
    EXPECT_EQ(pos3, 3u);
    // 查找最后一个匹配字串中首个字符出现的位置
    size_t pos4 = str.find_last_of("Hello");
    EXPECT_EQ(pos4, 18u);
    // 查找首个非匹配字串任意字符的位置
    size_t pos5 = str.find_first_not_of("Hello");
    EXPECT_EQ(pos5, 5u);
}

5. 字符串大小写转换

5.1 C++ 字符串操作介绍

字符串大小写转换是将字符串中的字母全部转换为大写或小写的操作。C++ 标准库虽未直接提供std::string的大小写转换方法,但可借助<cctype>头文件中的std::toupperstd::tolower函数,通过遍历字符串实现转换功能。

5.2 函数原型介绍

首先介绍用于单个字符转换的std::toupperstd::tolower

// 将字符转换为大写
int toupper(int c);
// 将字符转换为小写
int tolower(int c);
  • 参数int c表示待转换的字符,需以unsigned char的形式传入,避免负数导致未定义行为。

  • 返回值:返回转换后的字符的 ASCII 码值,若无法转换则返回原字符。

基于上述函数,可实现字符串的大小写转换:

// 自定义字符串转大写函数
std::string toUpperCase(const std::string& str) {
    std::string result;
    for (char c : str) {
        // 将char转换为unsigned char后再调用toupper,避免异常
        result += static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    }
    return result;
}
// 自定义字符串转小写函数
std::string toLowerCase(const std::string& str) {
    std::string result;
    for (char c : str) {
        result += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
    }
    return result;
}
  • 参数const std::string& str为待转换的字符串。

  • 返回值:返回转换后的新字符串,原字符串不会被修改。

5.3 GTEST 示例

TEST(StringManipulationTest, ToUpperCase) {
    // 空字符串
    EXPECT_EQ(toUpperCase(""), "");
    // 全小写字符串
    EXPECT_EQ(toUpperCase("hello"), "HELLO");
    // 全大写字符串
    EXPECT_EQ(toUpperCase("WORLD"), "WORLD");
    // 混合大小写字符串
    EXPECT_EQ(toUpperCase("Hello World"), "HELLO WORLD");
    // 包含数字和特殊字符的字符串
    EXPECT_EQ(toUpperCase("abc123!@#"), "ABC123!@#");
}
TEST(StringManipulationTest, ToLowerCase) {
    // 空字符串
    EXPECT_EQ(toLowerCase(""), "");
    // 全小写字符串
    EXPECT_EQ(toLowerCase("hello"), "hello");
    // 全大写字符串
    EXPECT_EQ(toLowerCase("WORLD"), "world");
    // 混合大小写字符串
    EXPECT_EQ(toLowerCase("Hello World"), "hello world");
    // 包含数字和特殊字符的字符串
    EXPECT_EQ(toLowerCase("ABC123!@#"), "abc123!@#");
}

6. 字符串截取和修改

6.1 C++ 字符串操作介绍

字符串的截取与修改是处理部分字符串的重要功能。截取操作能提取字符串中指定范围的子串,修改操作则涵盖清空内容、插入新内容和删除部分字符等,这些操作是对字符串进行编辑和处理的基础手段。

6.2 函数原型介绍

// 截取子字符串(从pos开始,长度为count)
std::string substr(size_t pos = 0, size_t count = npos) const;
// 清空字符串内容
void clear() noexcept;
// 在指定位置插入字符串
std::string& insert(size_t pos, const std::string& str);
// 从指定位置删除字符
std::string& erase(size_t pos = 0, size_t count = npos);
  • substr:若count超过字符串剩余字符数,会自动截取从pos到字符串末尾的所有字符。

  • clear:清空字符串后,其size()变为 0,但可能保留原有的内存容量。

  • insert:在pos位置插入str,原位置及之后的字符会向后移动。

  • erase:从pos开始删除count个字符,若未指定count,则默认删除到字符串末尾。

6.3 GTEST 示例

TEST(StringManipulationTest, SubstrAndModify) {
    std::string str = "abcdefghij";
    // 截取指定长度子串
    std::string sub1 = str.substr(2, 3);  // 从索引2开始取3个字符
    EXPECT_EQ(sub1, "cde");
    // 截取到字符串末尾
    std::string sub2 = str.substr(5);  // 从索引5开始到结尾
    EXPECT_EQ(sub2, "fghij");
    // 清空字符串
    std::string empty_me = "not empty";
    empty_me.clear();
    EXPECT_TRUE(empty_me.empty());
    // 插入操作
    std::string inserted = "Hello world";
    inserted.insert(5, ",");  // 在位置5插入逗号
    EXPECT_EQ(inserted, "Hello, world");
    // 删除操作
    std::string deleted = "Hello, world";
    deleted.erase(5, 2);  // 从位置5删除2个字符(逗号和空格)
    EXPECT_EQ(deleted, "Helloworld");
}

7. 字符串与数字的转换

7.1 C++ 字符串操作介绍

在数据处理中,字符串与数字的双向转换十分常见。C++ 标准库提供了stoi()stod()等便捷函数直接实现转换,同时也可通过<sstream>头文件中的std::istringstream类实现,后者能更灵活地进行错误检查。stoi()系列函数虽使用简便,但转换失败时会抛出异常,需进行异常处理;istringstream则通过流状态判断转换是否成功,适合不同场景需求。

7.2 函数原型介绍

首先介绍标准库的stoi()系列转换函数:

// 字符串转int
int stoi(const std::string& str, size_t* pos = nullptr, int base = 10);
// 字符串转long
long stol(const std::string& str, size_t* pos = nullptr, int base = 10);
// 字符串转long long
long long stoll(const std::string& str, size_t* pos = nullptr, int base = 10);
// 字符串转float
float stof(const std::string& str, size_t* pos = nullptr);
// 字符串转double
double stod(const std::string& str, size_t* pos = nullptr);

参数

  • str:待转换的字符串。
  • pos:输出参数,用于存储转换终止的位置(可选,默认 nullptr)。
  • base:转换基数(仅整数转换使用,默认 10 进制)。

返回值:转换后的对应类型数值。
异常

  • std::invalid_argument:当字符串内容无法转换为对应类型时抛出。
  • std::out_of_range:当转换结果超出目标类型的表示范围时抛出。

接着介绍基础的字符串流类std::istringstream

// 字符串输入流类,用于从字符串读取数据
template<class CharT, class Traits = std::char_traits<CharT>, 
         class Allocator = std::allocator<CharT>>
class basic_istringstream;
// 常用的字符串输入流类型(char版本)
using istringstream = basic_istringstream<char>;
// 从字符串流中提取数据(重载运算符)
template<class CharT, class Traits, class Allocator, class T>
basic_istringstream<CharT, Traits, Allocator>& 
operator>>(basic_istringstream<CharT, Traits, Allocator>& is, T& value);
// 检查流状态是否正常
bool good() const noexcept;
// 检查是否到达流的末尾
bool eof() const noexcept;
  • istringstream:通过构造函数接收字符串,可像操作输入流一样从中提取数据。
  • operator>>:从字符串流中提取数据并转换为指定类型,失败时会设置流的错误状态。
  • good():返回true表示流状态正常,无错误且未到达末尾。
  • eof():返回true表示已到达流的末尾。

基于istringstream实现带错误检查的转换函数:

// 带错误检查的字符串转整数
bool stringToInt(const std::string& str, int& result) {
    std::istringstream iss(str);
    // 提取数据并检查是否完全转换(无多余字符)
    return (iss >> result) && (iss.eof());
}
// 带错误检查的字符串转双精度浮点数
bool stringToDouble(const std::string& str, double& result) {
    std::istringstream iss(str);
    return (iss >> result) && (iss.eof());
}
// 标准库数字转字符串函数
std::string to_string(int val);
std::string to_string(double val);
  • stringToInt/stringToDouble:返回bool值表示转换是否成功,结果通过引用参数传出。
  • to_string:标准库函数,直接将数字转换为std::string

7.3 GTEST 示例

TEST(StringConversionTest, StlFunctionUsage) {
    // 正常转换(整数)
    EXPECT_EQ(std::stoi("123"), 123);
    EXPECT_EQ(std::stol("-456"), -456L);
    EXPECT_EQ(std::stoll("7890123456789"), 7890123456789LL);
    // 正常转换(浮点数)
    EXPECT_NEAR(std::stof("3.14"), 3.14f, 0.001f);
    EXPECT_NEAR(std::stod("2.71828"), 2.71828, 0.00001);
    // 转换终止位置测试
    size_t pos;
    std::stoi("123abc", &pos);
    EXPECT_EQ(pos, 3u);  // 转换到第3个字符后终止
    // 异常处理测试(无效格式)
    try {
        std::stoi("not a number");
        FAIL() << "Expected std::invalid_argument";
    } catch (const std::invalid_argument& e) {
        EXPECT_STREQ(e.what(), "stoi: no conversion");
    } catch (...) {
        FAIL() << "Expected std::invalid_argument";
    }
    // 异常处理测试(范围溢出)
    try {
        // 超出int表示范围(假设int为32位)
        std::stoi("2147483648");
        FAIL() << "Expected std::out_of_range";
    } catch (const std::out_of_range& e) {
        EXPECT_STREQ(e.what(), "stoi: out of range");
    } catch (...) {
        FAIL() << "Expected std::out_of_range";
    }
}
TEST(StringConversionTest, IstringstreamUsage) {
    // 整数转换测试
    std::istringstream intIss("12345");
    int intVal;
    intIss >> intVal;
    EXPECT_TRUE(intIss.eof());  // 确保完全读取
    EXPECT_EQ(intVal, 12345);
    // 浮点数转换测试
    std::istringstream doubleIss("3.14159");
    double doubleVal;
    doubleIss >> doubleVal;
    EXPECT_TRUE(doubleIss.eof());
    EXPECT_NEAR(doubleVal, 3.14159, 0.00001);
    // 转换失败测试(非数字内容)
    std::istringstream badIss("12a34");
    int badVal;
    badIss >> badVal;
    EXPECT_TRUE(badIss.good());  // 可以正确获取12 截断
}
TEST(StringToNumberConversionTest, StringToInt) {
    int result;
    // 正常转换
    EXPECT_TRUE(stringToInt("123", result));
    EXPECT_EQ(result, 123);
    // 负数转换
    EXPECT_TRUE(stringToInt("-456", result));
    EXPECT_EQ(result, -456);
    // 错误情况(含非数字字符)
    EXPECT_FALSE(stringToInt("12a3", result));
    // 错误情况(含小数点)
    EXPECT_FALSE(stringToInt("12.3", result));
}
TEST(NumberToStringConversionTest, IntToString) {
    EXPECT_EQ(std::to_string(123), "123");
    EXPECT_EQ(std::to_string(-456), "-456");
    EXPECT_EQ(std::to_string(0), "0");
}
TEST(NumberToStringConversionTest, DoubleToString) {
    EXPECT_EQ(std::to_string(3.14), "3.140000");  // 注意默认精度
    EXPECT_EQ(std::to_string(-2.718), "-2.718000");
}

8. 字符串与数字的拼接

8.1 C++ 字符串操作介绍

字符串与数字的拼接是将数字转换为字符串后与其他字符串合并的操作。基于std::to_string可实现简单拼接,而使用std::stringstream能实现更灵活的拼接,例如指定浮点数的小数位数、控制数值的格式化输出等。

8.2 函数原型介绍

首先介绍用于格式化输出的std::stringstream

// 字符串流类,支持输入和输出操作
template<class CharT, class Traits = std::char_traits<CharT>, 
         class Allocator = std::allocator<CharT>>
class basic_stringstream;
// 常用的字符串流类型(char版本)
using stringstream = basic_stringstream<char>;
// 向字符串流中插入数据
template<class CharT, class Traits, class Allocator, class T>
basic_stringstream<CharT, Traits, Allocator>& 
operator<<(basic_stringstream<CharT, Traits, Allocator>& os, const T& value);
// 设置浮点数精度
std::ios_base& precision(std::streamsize n);
// 设置浮点数固定小数位格式
std::ios_base& fixed(std::ios_base& str);
// 获取流中的字符串
std::basic_string<CharT, Traits, Allocator> str() const;
  • stringstream:可同时进行输入和输出操作,常用于字符串的格式化处理。
  • operator<<:向流中插入数据,自动进行类型转换。
  • precision:设置浮点数的有效数字位数或小数位数(结合fixed使用)。
  • fixed:设置浮点数为固定小数位格式,此时precision指定小数位数。
  • str():返回流中累积的字符串。

基于上述工具实现拼接函数:

// 字符串与整数拼接(字符串在前)
std::string concatStrInt(const std::string& str, int num) {
    return str + std::to_string(num);
}
// 整数与字符串拼接(数字在前)
std::string concatIntStr(int num, const std::string& str) {
    return std::to_string(num) + str;
}
// 字符串与浮点数拼接(支持指定小数位数)
std::string concatStrDouble(const std::string& str, double num, int precision = 2) {
    std::stringstream ss;
    ss.precision(precision);       // 设置精度
    ss << std::fixed << str << num; // 固定小数位格式
    return ss.str();
}
  • 参数

  • str:待拼接的字符串。

  • num:待拼接的数字(整数或浮点数)。

  • precision:浮点数的小数位数(默认 2 位)。

  • 返回值:拼接后的新字符串。

8.3 GTEST 示例

TEST(StringNumberConcatTest, StringIntConcat) {
    // 基础拼接
    EXPECT_EQ(concatStrInt("age:", 25), "age:25");
    EXPECT_EQ(concatIntStr(100, "points"), "100points");
    // 负数测试
    EXPECT_EQ(concatStrInt("temp:", -5), "temp:-5");
    EXPECT_EQ(concatIntStr(-3, " floors"), "-3 floors");
    // 零值测试
    EXPECT_EQ(concatStrInt("count:", 0), "count:0");
}
TEST(StringNumberConcatTest, StringDoubleConcat) {
    // 正数浮点数
    EXPECT_EQ(concatStrDouble("price:", 99.99), "price:99.99");
    EXPECT_EQ(concatStrDouble("pi:", 3.14159, 3), "pi:3.142"); // 测试精度
    EXPECT_EQ(concatStrDouble("ratio:", 0.5), "ratio:0.50");   // 默认精度
    // 负数浮点数
    EXPECT_EQ(concatStrDouble("debt:", -150.5), "debt:-150.50");
    EXPECT_EQ(concatStrDouble("change:", -0.1234, 4), "change:-0.1234");
}
TEST(StringStreamTest, CustomFormatting) {
    std::stringstream ss;
    ss.precision(2);
    ss << std::fixed << "Value: " << 123.456;
    EXPECT_EQ(ss.str(), "Value: 123.46"); // 自动四舍五入
}

9. 字符串迭代器操作

9.1 C++ 字符串操作介绍

std::string支持迭代器操作,通过迭代器可以便捷地遍历字符串中的字符,结合 STL 算法能实现更灵活的字符串处理,如字符替换、过滤等。迭代器提供了一种统一的方式访问容器元素,适用于各种遍历和修改场景。

9.2 函数原型介绍

// 正向迭代器
iterator begin() noexcept;
const_iterator begin() const noexcept;
const_iterator cbegin() const noexcept;
// 正向迭代器(指向末尾后一个位置)
iterator end() noexcept;
const_iterator end() const noexcept;
const_iterator cend() const noexcept;
// 反向迭代器
reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator crbegin() const noexcept;
// 反向迭代器(指向开头前一个位置)
reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crend() const noexcept;
  • 正向迭代器begin()返回指向第一个字符的迭代器,end()返回指向最后一个字符后位置的迭代器。

  • const 迭代器cbegin()/cend()返回常量迭代器,不能通过其修改元素。

  • 反向迭代器rbegin()返回指向最后一个字符的反向迭代器,rend()返回指向第一个字符前位置的反向迭代器,用于反向遍历。

9.3 GTEST 示例

// 测试字符串正向迭代器遍历
TEST(StringIteratorTest, ForwardTraversal) {
    std::string str = "hello";
    std::string result;

    // 使用正向迭代器遍历
    for (std::string::const_iterator it = str.cbegin(); it != str.cend(); ++it) {
        result += *it;
    }
    EXPECT_EQ(result, "hello");
}

// 测试字符串反向迭代器遍历
TEST(StringIteratorTest, ReverseTraversal) {
    std::string str = "hello";
    std::string reversed;

    // 使用反向迭代器遍历(逆序)
    for (std::string::const_reverse_iterator it = str.crbegin();
         it != str.crend(); ++it) {
        reversed += *it;
    }
    EXPECT_EQ(reversed, "olleh");
}

// 测试使用迭代器修改字符串
TEST(StringIteratorTest, ModifyWithIterator) {
    std::string str = "hello";

    // 使用迭代器修改字符
    for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
        *it = std::toupper(static_cast<unsigned char>(*it));
    }
    EXPECT_EQ(str, "HELLO");
}

// 测试结合STL算法使用字符串迭代器
TEST(StringIteratorTest, StlAlgorithmUsage) {
    std::string str = "a1b2c3d4";
    std::string digits;

    // 使用STL算法提取数字字符
    std::copy_if(str.begin(), str.end(), std::back_inserter(digits),
                 [](char c) { return std::isdigit(static_cast<unsigned char>(c)); });
    EXPECT_EQ(digits, "1234");
}

9.4 使用下标遍历

这种方式是最直观的方式,简单的示例函数如下:

void traverseByIndex(const std::string& str) {
    // 遍历所有字符(索引范围:0 ~ str.size()-1)
    for (size_t i = 0; i < str.size(); ++i) {
        std::cout << "索引 " << i << ": " << str[i] << " ";
    }
    std::cout << std::endl;
}

注意

  • 索引从 0 开始,最大索引为 str.size() - 1,越界访问会导致未定义行为。
  • 若需边界检查,可使用 at(i) 替代 [i](越界时抛出 std::out_of_range 异常)。

10. 字符串正则表达式操作

10.1 C++ 字符串操作介绍

正则表达式是处理字符串模式匹配的强大工具,可用于验证、搜索、替换等场景。C++11 引入了<regex>库,提供了对正则表达式的支持,能方便地结合std::string进行各种模式匹配操作。掌握正则表达式的基础规则是进行复杂字符串处理的关键。

10.2 正则表达式基础规则表

以下是正则表达式的基础使用规则,通过表格形式呈现,便于快速掌握:

元字符 / 规则 说明 示例 匹配结果
. 匹配任意单个字符(除换行符) a.b 匹配 "aab"、"acb"、"a1b" 等
* 匹配前面的元素 0 次或多次 ab*c 匹配 "ac"、"abc"、"abbc"、"abbbc" 等
+ 匹配前面的元素 1 次或多次 ab+c 匹配 "abc"、"abbc"、"abbbc" 等(不匹配 "ac")
? 匹配前面的元素 0 次或 1 次 ab?c 匹配 "ac"、"abc"(不匹配 "abbc")
^ 匹配字符串的开始位置 ^abc 匹配以 "abc" 开头的字符串,如 "abcdef"
$ 匹配字符串的结束位置 abc$ 匹配以 "abc" 结尾的字符串,如 "xyzabc"
[] 字符集,匹配其中任意一个字符 [abc] 匹配 "a"、"b" 或 "c"
[^] 否定字符集,匹配不在其中的任意字符 [^abc] 匹配除 "a"、"b"、"c" 外的任意字符
[a-z] 范围字符集,匹配指定范围内的字符 [0-9] 匹配任意数字字符
() 分组,将多个元素视为一个整体 (ab)+ 匹配 "ab"、"abab"、"ababab" 等
\| 或运算,匹配左边或右边的表达式 a\|b 匹配 "a" 或 "b"
\d 匹配数字字符,等价于[0-9] \d+ 匹配 "123"、"45" 等数字序列
\D 匹配非数字字符,等价于[^0-9] \D+ 匹配 "abc"、"!@#" 等非数字序列
\w 匹配单词字符(字母、数字、下划线),等价于[a-zA-Z0-9_] \w+ 匹配 "hello"、"user123"、"var_name" 等
\W 匹配非单词字符,等价于[^a-zA-Z0-9_] \W+ 匹配 "!@#"、"\$%^" 等非单词字符序列
\s 匹配空白字符(空格、制表符、换行符等) \s+ 匹配一个或多个空白字符
\S 匹配非空白字符 \S+ 匹配一个或多个非空白字符
\{n\} 匹配前面的元素恰好 n 次 a\{3\} 匹配 "aaa"
\{n,\} 匹配前面的元素至少 n 次 a\{2,\} 匹配 "aa"、"aaa"、"aaaa" 等
\{n,m\} 匹配前面的元素至少 n 次,至多 m 次 a\{1,3\} 匹配 "a"、"aa"、"aaa"

10.3 函数原型介绍

C++ 中与正则表达式相关的主要类和函数如下:

// 正则表达式类
template<class CharT, class Traits = std::regex_traits<CharT>>
class basic_regex;
// 常用的正则表达式类型(char版本)
using regex = basic_regex<char>;
// 全字符串匹配,整个字符串与正则表达式匹配时返回true
template<class CharT, class Traits, class Allocator>
bool regex_match(const std::basic_string<CharT, Traits, Allocator>& s,
                const std::basic_regex<CharT, Traits>& e);
// 部分匹配,字符串中存在与正则表达式匹配的子串时返回true
template<class CharT, class Traits, class Allocator>
bool regex_search(const std::basic_string<CharT, Traits, Allocator>& s,
                 const std::basic_regex<CharT, Traits>& e);
// 字符串替换,将与正则表达式匹配的部分替换为指定字符串
template<class CharT, class Traits, class Allocator, class STraits, class SAllocator>
std::basic_string<CharT, STraits, SAllocator>
regex_replace(const std::basic_string<CharT, Traits, Allocator>& s,
             const std::basic_regex<CharT, Traits>& e,
             const std::basic_string<CharT, STraits, SAllocator>& fmt);
  • regex:表示一个正则表达式对象,可通过字符串构造。

  • regex_match:检查整个字符串是否与正则表达式完全匹配。

  • regex_search:在字符串中搜索与正则表达式匹配的子串,只要存在就返回 true。

  • regex_replace:替换字符串中与正则表达式匹配的部分,返回替换后的新字符串。

10.4 GTEST 示例

TEST(RegexOperationTest, RegexMatch) {
    // 匹配邮箱格式(简单版)
    const std::regex emailRegex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)");
    EXPECT_TRUE(std::regex_match("test@example.com", emailRegex));
    EXPECT_FALSE(std::regex_match("test@example", emailRegex)); // 缺少顶级域名

    // 匹配整数
    const std::regex intRegex(R"(^[-+]?\d+$)");
    EXPECT_TRUE(std::regex_match("123", intRegex));
    EXPECT_TRUE(std::regex_match("-456", intRegex));
    EXPECT_FALSE(std::regex_match("12.3", intRegex)); // 包含小数点,不是整数
}

TEST(RegexOperationTest, RegexSearch) {
    std::string text = "Contact us at support@example.com or 123-456-7890";

    // 搜索邮箱
    const std::regex emailRegex(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
    std::smatch match;
    EXPECT_TRUE(std::regex_search(text, match, emailRegex));
    EXPECT_EQ(match.str(), "support@example.com");

    // 搜索电话号码
    const std::regex phoneRegex(R"(\d{3}-\d{3}-\d{4})");
    EXPECT_TRUE(std::regex_search(text, match, phoneRegex));
    EXPECT_EQ(match.str(), "123-456-7890");
}

TEST(RegexOperationTest, RegexReplace) {
    std::string text = "Hello [Name], your order #12345 is ready.";

    // 替换占位符
    const std::regex nameRegex(R"(\[Name\])");
    std::string replaced1 = std::regex_replace(text, nameRegex, "John");
    EXPECT_EQ(replaced1, "Hello John, your order #12345 is ready.");

    // 替换数字
    const std::regex numRegex(R"(\d+)");
    std::string replaced2 = std::regex_replace(text, numRegex, "99999");
    EXPECT_EQ(replaced2, "Hello [Name], your order #99999 is ready.");
}

11. 综合案例:字符串处理工具类

11.1 C++ 字符串操作介绍

在实际开发中,通常会将常用的字符串操作封装成工具类,方便复用。这个综合案例将前面介绍的各种字符串操作整合起来,实现一个功能完善的字符串处理工具类,包括字符串验证、转换、修改等功能。

11.2 工具类实现

#include <string>
#include <regex>
#include <sstream>
#include <cctype>
#include <algorithm>
class StringUtils {
public:
    // 检查字符串是否为空或只包含空白字符
    static bool isEmptyOrWhitespace(const std::string& str) {
        return str.empty() || std::all_of(str.begin(), str.end(), ::isspace);
    }
    // 去除字符串首尾空白字符
    static std::string trim(const std::string& str) {
        size_t start = str.find_first_not_of(" \t\n\r");
        if (start == std::string::npos) return "";

        size_t end = str.find_last_not_of(" \t\n\r");
        return str.substr(start, end - start + 1);
    }
    // 验证邮箱格式
    static bool isValidEmail(const std::string& email) {
        const std::regex pattern(R"(^\[a-zA-Z0-9._%+-]+@\[a-zA-Z0-9.-]+\\.\[a-zA-Z]{2,}\$)");
        return std::regex_match(email, pattern);
    }
    // 字符串转整数(带错误检查)
    static bool toInt(const std::string& str, int& result) {
        std::istringstream iss(trim(str));
        return (iss >> result) && (iss.eof());
    }
    // 整数转字符串
    static std::string fromInt(int num) {
        return std::to_string(num);
    }
    // 字符串替换(全部替换)
    static std::string replaceAll(const std::string& str, const std::string& oldSub, const std::string& newSub) {
        std::string result = str;
        size_t pos = 0;
        while ((pos = result.find(oldSub, pos)) != std::string::npos) {
            result.replace(pos, oldSub.length(), newSub);
            pos += newSub.length();
        }
        return result;
    }
    // 字符串转大写
    static std::string toUpperCase(const std::string& str) {
        std::string result;
        for (char c : str) {
            result += static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
        }
        return result;
    }
    // 字符串转小写
    static std::string toLowerCase(const std::string& str) {
        std::string result;
        for (char c : str) {
            result += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
        }
        return result;
    }
};

11.3 GTEST 示例

TEST(StringUtilsTest, BasicFunctions) {
    // 空字符串检查
    EXPECT_TRUE(StringUtils::isEmptyOrWhitespace(""));
    EXPECT_TRUE(StringUtils::isEmptyOrWhitespace("   \t\n"));
    EXPECT_FALSE(StringUtils::isEmptyOrWhitespace(" hello "));
    // 修剪空白字符
    EXPECT_EQ(StringUtils::trim("  hello world  "), "hello world");
    EXPECT_EQ(StringUtils::trim("\t\n test \r"), "test");
    // 大小写转换
    EXPECT_EQ(StringUtils::toUpperCase("Hello World"), "HELLO WORLD");
    EXPECT_EQ(StringUtils::toLowerCase("Hello World"), "hello world");
}
TEST(StringUtilsTest, ValidationAndConversion) {
    // 邮箱验证
    EXPECT_TRUE(StringUtils::isValidEmail("user@example.com"));
    EXPECT_FALSE(StringUtils::isValidEmail("invalid-email"));
    // 整数转换
    int num;
    EXPECT_TRUE(StringUtils::toInt("123", num));
    EXPECT_EQ(num, 123);
    EXPECT_FALSE(StringUtils::toInt("12a3", num));
    // 字符串替换
    EXPECT_EQ(StringUtils::replaceAll("ababa", "a", "x"), "xbxbx");
    EXPECT_EQ(StringUtils::replaceAll("hello world", "world", "there"), "hello there");
}

12. 文章总结

本文详细介绍了 C++ 中std::string的各种操作,从基础到进阶,涵盖了多个重要方面:

  1. 基础操作:包括字符串的初始化、基本属性获取(长度、是否为空等)、拼接(++=运算符)、比较(各种比较运算符)等,这些是字符串处理的基石。

  2. 中级操作:涉及字符串的查找与替换(findrfindreplace等方法)、大小写转换(基于std::toupperstd::tolower实现)、截取与修改(substrinserterase等),能满足大多数日常字符串处理需求。

  3. 转换与拼接:讲解了字符串与数字的双向转换(stoi系列函数、to_stringistringstream)以及字符串与数字的拼接(基于to_stringstringstream),在数据处理中经常用到。

  4. 迭代器操作:介绍了std::string的迭代器使用,包括正向迭代器、反向迭代器等,结合 STL 算法可实现灵活的字符串遍历和修改。

  5. 正则表达式:通过表格形式呈现了正则表达式的基础规则,便于快速掌握,同时介绍了 C++ 中regexregex_matchregex_searchregex_replace等的使用,用于复杂的模式匹配和字符串处理。

  6. 综合案例:提供了一个字符串处理工具类,整合了前面介绍的各种操作,展示了实际开发中字符串处理功能的封装与复用。

掌握这些字符串操作知识,能帮助开发者高效地处理各种字符串相关的任务,提升 C++ 编程能力。在实际应用中,应根据具体场景选择合适的操作方法,兼顾效率和可读性。

13. 附录

附上C++完整gtest测试例

#include <gtest/gtest.h>
#include <string>
#include <algorithm>
#include <regex>

// 测试字符串初始化和基本属性
TEST(StringInitializationTest, BasicProperties) {
    // 空字符串测试
    std::string empty_str;
    EXPECT_TRUE(empty_str.empty());
    EXPECT_EQ(empty_str.size(), 0u);
    EXPECT_EQ(empty_str.length(), 0u);

    // 有初始值的字符串测试
    std::string hello_str = "Hello";
    EXPECT_FALSE(hello_str.empty());
    EXPECT_EQ(hello_str.size(), 5u);
    EXPECT_EQ(hello_str.length(), 5u);
    // EXPECT_EQ(hello_str.capacity(), 15u); // 取决于实现,可能不同

    // C字符串转换测试
    EXPECT_STREQ(hello_str.c_str(), "Hello");
}

// 测试字符串拼接操作
TEST(StringConcatenationTest, VariousMethods) {
    std::string str1 = "Hello";
    std::string str2 = " World";

    // 使用+运算符拼接
    std::string result1 = str1 + str2;
    EXPECT_EQ(result1, "Hello World");

    // 使用+=运算符拼接
    std::string result2 = str1;
    result2 += str2;
    EXPECT_EQ(result2, "Hello World");

    // 拼接C风格字符串
    std::string result3 = str1 + " there";
    EXPECT_EQ(result3, "Hello there");

    // 拼接单个字符
    std::string result4 = str1;
    result4 += '!';
    EXPECT_EQ(result4, "Hello!");
}

// 测试字符串比较操作
TEST(StringComparisonTest, EqualityAndOrder) {
    std::string a = "apple";
    std::string b = "banana";
    std::string a2 = "apple";

    // 相等性测试
    EXPECT_EQ(a, a2);
    EXPECT_NE(a, b);

    // 顺序比较测试
    EXPECT_LT(a, b);  // "apple" < "banana"
    EXPECT_GT(b, a);  // "banana" > "apple"

    // 区分大小写测试
    std::string A = "Apple";
    EXPECT_NE(a, A);  // 大小写不同,字符串不等
    EXPECT_LT(A, a);  // 'A' (65) < 'a' (97)
}

// 测试字符串查找和替换
TEST(StringSearchTest, FindAndReplace) {
    std::string str = "Hello, world! Hello, C++!";

    // 查找子字符串
    size_t pos1 = str.find("world");
    EXPECT_EQ(pos1, 7u);

    // 查找单个字符
    size_t pos2 = str.find('!');
    EXPECT_EQ(pos2, 12u);

    // 从指定位置开始查找
    size_t pos3 = str.find("Hello", 1);
    EXPECT_EQ(pos3, 14u);

    // 查找不存在的子字符串
    size_t pos4 = str.find("Python");
    EXPECT_EQ(pos4, std::string::npos);

    // 替换子字符串
    std::string replaced = str;
    replaced.replace(pos1, 5, "there");  // 从pos1开始替换5个字符
    EXPECT_EQ(replaced, "Hello, there! Hello, C++!");
}

// 测试查找增强
TEST(StringManipulationTest, FindEnhance) {
    // 倒序查找
    std::string str = "Hello, world! Hello, C++!";
    size_t pos1 = str.rfind("world");
    EXPECT_EQ(pos1, 7u);

    // 查找首个字符串任何一个字符出现的首个位置
    size_t pos2 = str.find_first_of("Hello");
    EXPECT_EQ(pos2, 0u);

    size_t pos3 = str.find_first_of("Hello", 3);
    EXPECT_EQ(pos3, 3u);

    // 查找最后一个匹配字串中首个字符出现的位置
    size_t pos4 = str.find_last_of("Hello");
    EXPECT_EQ(pos4, 18u);

    // 查找首个非匹配字串任意字符的位置
    size_t pos5 = str.find_first_not_of("Hello");
    EXPECT_EQ(pos5, 5u);

    // 循环查找多次
    std::string repeatStr = "HelloHelloHelloHello";
    size_t loc = repeatStr.find("Hello");
    size_t startPos = 0;
    EXPECT_EQ(loc, startPos);
    while (loc != std::string::npos) {
        EXPECT_EQ(loc, startPos);
        loc = repeatStr.find("Hello", loc + strlen("Hello"));
        startPos += strlen("Hello");
    }
}

// 大小写转换
// 字符串转大写
std::string toUpperCase(const std::string& str) {
    std::string result;
    for (char c : str) {
        result += static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    }
    return result;
}

// 字符串转小写
std::string toLowerCase(const std::string& str) {
    std::string result;
    for (char c : str) {
        result += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
    }
    return result;
}

TEST(StringManipulationTest, ToUpperCase) {
    // 空字符串
    EXPECT_EQ(toUpperCase(""), "");

    // 全小写字符串
    EXPECT_EQ(toUpperCase("hello"), "HELLO");

    // 全大写字符串
    EXPECT_EQ(toUpperCase("WORLD"), "WORLD");

    // 混合大小写字符串
    EXPECT_EQ(toUpperCase("Hello World"), "HELLO WORLD");

    // 包含数字和特殊字符的字符串
    EXPECT_EQ(toUpperCase("abc123!@#"), "ABC123!@#");

    // 包含非ASCII字符(视本地化设置可能有不同结果)
    // EXPECT_EQ(toUpperCase("café"), "CAFÉ"); // 本地测试不通过
}

TEST(StringManipulationTest, ToLowerCase) {
    // 空字符串
    EXPECT_EQ(toLowerCase(""), "");

    // 全小写字符串
    EXPECT_EQ(toLowerCase("hello"), "hello");

    // 全大写字符串
    EXPECT_EQ(toLowerCase("WORLD"), "world");

    // 混合大小写字符串
    EXPECT_EQ(toLowerCase("Hello World"), "hello world");

    // 包含数字和特殊字符的字符串
    EXPECT_EQ(toLowerCase("ABC123!@#"), "abc123!@#");

    // 包含非ASCII字符
    // EXPECT_EQ(toLowerCase("CAFÉ"), "café"); // 本地测试不通过
}

// 测试字符串截取和修改
TEST(StringManipulationTest, SubstrAndModify) {
    std::string str = "abcdefghij";

    // 截取子字符串
    std::string sub1 = str.substr(2, 3);  // 从索引2开始,长度3
    EXPECT_EQ(sub1, "cde");

    // 截取从指定位置到结尾
    std::string sub2 = str.substr(5);
    EXPECT_EQ(sub2, "fghij");

    // 字符串清空
    std::string empty_me = "not empty";
    empty_me.clear();
    EXPECT_TRUE(empty_me.empty());

    // 字符串插入
    std::string inserted = "Hello world";
    inserted.insert(5, ",");  // 在位置5插入逗号
    EXPECT_EQ(inserted, "Hello, world");

    // 字符串删除
    std::string deleted = "Hello, world";
    deleted.erase(5, 2);  // 从位置5删除2个字符
    EXPECT_EQ(deleted, "Helloworld");
}

// 测试字符串转换
TEST(StringConversionTest, ToNumbers) {
    // 字符串转整数
    EXPECT_EQ(std::stoi("123"), 123);
    EXPECT_EQ(std::stol("-456"), -456L);
    EXPECT_EQ(std::stoll("7890123456789"), 7890123456789LL);

    // 字符串转浮点数
    EXPECT_NEAR(std::stof("3.14"), 3.14f, 0.001f);
    EXPECT_NEAR(std::stod("2.71828"), 2.71828, 0.00001);

    // 注意:异常测试需要使用ASSERT_THROW或EXPECT_THROW
    EXPECT_THROW(std::stoi("not a number"), std::invalid_argument);
}

// 测试字符串迭代器和算法
TEST(StringAlgorithmTest, IteratorsAndSTL) {
    std::string str = "hello";

    // 使用迭代器遍历
    std::string reversed;
    for (auto it = str.rbegin(); it != str.rend(); ++it) {
        reversed += *it;
    }
    EXPECT_EQ(reversed, "olleh");

    // 使用STL算法
    std::string upper_str = str;
    std::transform(upper_str.begin(), upper_str.end(),
                  upper_str.begin(), ::toupper);
    EXPECT_EQ(upper_str, "HELLO");
}

// 字符串转数字
// 字符串转整数(返回是否成功,结果通过引用传出)
bool stringToInt(const std::string& str, int& result) {
    std::istringstream iss(str);
    // 严格匹配:整个字符串必须是整数,不允许有额外字符
    return (iss >> result) && (iss.eof());
}

// 字符串转浮点数(返回是否成功,结果通过引用传出)
bool stringToDouble(const std::string& str, double& result) {
    // 手动处理特殊浮点值(不区分大小写) 此部分处理可能和编译器相关 改成手动处理
    if (str == "inf" || str == "INF") {
        result = std::numeric_limits<double>::infinity();
        return true;
    }
    if (str == "-inf" || str == "-INF") {
        result = -std::numeric_limits<double>::infinity();
        return true;
    }
    if (str == "nan" || str == "NAN") {
        result = std::numeric_limits<double>::quiet_NaN();
        return true;
    }

    std::istringstream iss(str);
    // 严格匹配:整个字符串必须是浮点数,不允许有额外字符
    return (iss >> result) && (iss.eof());
}

// 带异常的字符串转整数(转换失败时抛出异常)
int stringToIntWithException(const std::string& str) {
    std::istringstream iss(str);
    int result;
    if (!(iss >> result) || !iss.eof()) {
        throw std::invalid_argument("Invalid integer format: " + str);
    }
    return result;
}

TEST(StringToNumberConversionTest, StringToInt) {
    int result;

    // 正常转换
    EXPECT_TRUE(stringToInt("123", result));
    EXPECT_EQ(result, 123);

    // 负数转换
    EXPECT_TRUE(stringToInt("-456", result));
    EXPECT_EQ(result, -456);

    // 零值转换
    EXPECT_TRUE(stringToInt("0", result));
    EXPECT_EQ(result, 0);

    // 带前导零(允许,因为整数可以有前导零)
    EXPECT_TRUE(stringToInt("007", result));
    EXPECT_EQ(result, 7);

    // 错误情况:非数字字符
    EXPECT_FALSE(stringToInt("12a3", result));
    EXPECT_FALSE(stringToInt("abc", result));

    // 错误情况:包含空格
    // 前导空格场景 默认是带前导空格的
    // 如果需要跳过前导空格校验可以使用 iss >> std::noskipws >> result
    EXPECT_TRUE(stringToInt(" 123", result));
    EXPECT_FALSE(stringToInt("123 ", result)); // 尾随空格
    EXPECT_FALSE(stringToInt("12 3", result)); // 中间空格

    // 错误情况:空字符串
    EXPECT_FALSE(stringToInt("", result));

    // 错误情况:浮点数格式
    EXPECT_FALSE(stringToInt("12.3", result));
}

TEST(StringToNumberConversionTest, StringToDouble) {
    double result;

    // 整数形式浮点数
    EXPECT_TRUE(stringToDouble("123", result));
    EXPECT_EQ(result, 123.0);

    // 标准小数
    EXPECT_TRUE(stringToDouble("3.14", result));
    EXPECT_EQ(result, 3.14);

    // 负数小数
    EXPECT_TRUE(stringToDouble("-0.5", result));
    EXPECT_EQ(result, -0.5);

    // 科学计数法
    EXPECT_TRUE(stringToDouble("1e3", result));
    EXPECT_EQ(result, 1000.0);

    EXPECT_TRUE(stringToDouble("2.5e-2", result));
    EXPECT_EQ(result, 0.025);

    // 错误情况:非数字字符
    EXPECT_FALSE(stringToDouble("3.14a", result));
    EXPECT_FALSE(stringToDouble("pi", result));

    // 错误情况:格式错误
    EXPECT_FALSE(stringToDouble("12.3.4", result)); // 多个小数点
    // 会正常转换 0.5
    EXPECT_TRUE(stringToDouble(".5", result));     // 缺少整数部分
    // 会正常转换 5.0
    EXPECT_TRUE(stringToDouble("5.", result));     // 缺少小数部分
}

TEST(StringToNumberConversionTest, ExceptionHandling) {
    // 测试异常抛出情况
    EXPECT_THROW(stringToIntWithException("12a3"), std::invalid_argument);
    EXPECT_THROW(stringToIntWithException(""), std::invalid_argument);
    EXPECT_THROW(stringToIntWithException("12.3"), std::invalid_argument);

    // 测试正常转换不抛出异常
    EXPECT_NO_THROW(stringToIntWithException("789"));
    EXPECT_NO_THROW(stringToIntWithException("-0"));
}

TEST(StringToNumberConversionTest, EdgeCases) {
    int int_result;
    double double_result;

    // 整数边界值(依赖于系统的int范围)
    EXPECT_TRUE(stringToInt("2147483647", int_result));       // 典型的int最大值
    EXPECT_TRUE(stringToInt("-2147483648", int_result));      // 典型的int最小值

    // 浮点数边界值
    EXPECT_TRUE(stringToDouble("1.7976931348623157e+308", double_result)); // 接近double最大值
    EXPECT_TRUE(stringToDouble("2.2250738585072014e-308", double_result)); // 接近double最小值

    // 特殊值(注意:这些是double的特殊值,字符串转换需要精确匹配)
    EXPECT_TRUE(stringToDouble("inf", double_result));        // 无穷大
    EXPECT_TRUE(stringToDouble("-inf", double_result));       // 负无穷大
}

// 字符串与整数拼接(字符串在前)
std::string concatStrInt(const std::string& str, int num) {
    return str + std::to_string(num);
}

// 整数与字符串拼接(数字在前)
std::string concatIntStr(int num, const std::string& str) {
    return std::to_string(num) + str;
}

// 字符串与浮点数拼接(支持指定小数位数)
std::string concatStrDouble(const std::string& str, double num, int precision = 2) {
    std::stringstream ss;
    ss.precision(precision);
    ss << std::fixed << str << num;
    return ss.str();
}

// 测试套件:StringNumberConcatTest
TEST(StringNumberConcatTest, StringIntConcat) {
    // 基础拼接
    EXPECT_EQ(concatStrInt("age:", 25), "age:25");
    EXPECT_EQ(concatIntStr(100, "points"), "100points");

    // 负数测试
    EXPECT_EQ(concatStrInt("temp:", -5), "temp:-5");
    EXPECT_EQ(concatIntStr(-3, " floors"), "-3 floors");

    // 零值测试
    EXPECT_EQ(concatStrInt("count:", 0), "count:0");
    EXPECT_EQ(concatIntStr(0, " error"), "0 error");

    // 空字符串测试
    EXPECT_EQ(concatStrInt("", 123), "123");
    EXPECT_EQ(concatIntStr(456, ""), "456");
}

TEST(StringNumberConcatTest, StringDoubleConcat) {
    // 正数浮点数
    EXPECT_EQ(concatStrDouble("price:", 99.99), "price:99.99");
    EXPECT_EQ(concatStrDouble("pi:", 3.14159, 3), "pi:3.142"); // 测试精度

    // 负数浮点数
    EXPECT_EQ(concatStrDouble("debt:", -150.5), "debt:-150.50");

    // 整数形式的浮点数
    EXPECT_EQ(concatStrDouble("height:", 180.0), "height:180.00");

    // 科学计数法场景(此处用高精度避免自动转换)
    EXPECT_EQ(concatStrDouble("value:", 0.0001234, 6), "value:0.000123");
}

TEST(StringNumberConcatTest, ComplexConcat) {
    // 多次拼接场景
    std::string result = concatStrInt("user", 10) + concatStrDouble("_score:", 95.5);
    EXPECT_EQ(result, "user10_score:95.50");

    // 特殊字符与数字拼接
    EXPECT_EQ(concatIntStr(2023, "-08-10"), "2023-08-10");
    EXPECT_EQ(concatStrInt("ratio 1:", 3) + ":" + concatStrInt("", 4), "ratio 1:3:4");
}

// 通用字符串正则验证函数
bool matchesRegex(const std::string& str, const std::regex& pattern) {
    return std::regex_match(str, pattern);
}

// 测试套件:StringRegexValidationTest
TEST(StringRegexValidationTest, EmailValidation) {
    // 邮箱正则表达式:支持字母、数字、常见符号及域名格式
    const std::regex emailRegex(
        R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"
    );

    // 有效邮箱测试
    EXPECT_TRUE(matchesRegex("test@example.com", emailRegex));
    EXPECT_TRUE(matchesRegex("user.name+tag@domain.co", emailRegex));
    EXPECT_TRUE(matchesRegex("123_abc@sub.domain.io", emailRegex));

    // 无效邮箱测试
    EXPECT_FALSE(matchesRegex("missing@dot", emailRegex)); // 缺少顶级域名
    EXPECT_FALSE(matchesRegex("no@domain", emailRegex));   // 顶级域名太短
    EXPECT_FALSE(matchesRegex("invalid@.com", emailRegex)); // 域名开头有圆点
    EXPECT_FALSE(matchesRegex(" spaces@example.com", emailRegex)); // 前导空格
    EXPECT_FALSE(matchesRegex("no@symbol@com", emailRegex)); // 多个@符号
}

TEST(StringRegexValidationTest, UrlValidation) {
    // URL正则表达式:支持http/https协议,域名和路径
    // 要求域名至少包含一个顶级域名(如 .com, .co.uk 等)
    const std::regex urlRegex(
        R"(^https?://[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+(:\d+)?(/.*)?$)"
    );

    // 有效URL测试
    EXPECT_TRUE(matchesRegex("http://example.com", urlRegex));
    EXPECT_TRUE(matchesRegex("https://sub.domain.co:8080/path?query=1", urlRegex));
    EXPECT_TRUE(matchesRegex("https://simple.io", urlRegex));

    // 无效URL测试
    EXPECT_FALSE(matchesRegex("ftp://invalid.com", urlRegex)); // 不支持的协议
    EXPECT_FALSE(matchesRegex("https://.com", urlRegex));      // 缺少域名主体
    EXPECT_FALSE(matchesRegex("http://domain", urlRegex));     // 缺少顶级域名
    EXPECT_FALSE(matchesRegex("https:// domain.com", urlRegex)); // 包含空格
}

TEST(StringRegexValidationTest, PhoneNumberValidation) {
    // 手机号正则表达式:中国手机号格式(11位数字,以1开头)
    const std::regex phoneRegex(
        R"(^1[3-9]\d{9}$)"
    );

    // 有效手机号测试
    EXPECT_TRUE(matchesRegex("13800138000", phoneRegex));
    EXPECT_TRUE(matchesRegex("19912345678", phoneRegex));

    // 无效手机号测试
    EXPECT_FALSE(matchesRegex("12345678901", phoneRegex)); // 开头数字不合法
    EXPECT_FALSE(matchesRegex("13800001", phoneRegex));    // 长度不足
    EXPECT_FALSE(matchesRegex("138001380000", phoneRegex));// 长度过长
    EXPECT_FALSE(matchesRegex("138-0013-8000", phoneRegex));// 包含分隔符
    EXPECT_FALSE(matchesRegex(" 13800138000", phoneRegex)); // 前导空格
}

// 判断是否为闰年
bool isLeapYear(int year) {
    if (year % 4 != 0) return false;
    if (year % 100 != 0) return true;
    return (year % 400 == 0);
}

// 检查日期逻辑有效性(年、月、日范围)
bool isValidDate(int year, int month, int day) {
    // 月份范围校验
    if (month < 1 || month > 12) return false;

    // 每月最大天数
    int maxDay;
    switch (month) {
        case 2:  // 2月特殊处理
            maxDay = isLeapYear(year) ? 29 : 28;
            break;
        case 4: case 6: case 9: case 11:  // 30天的月份
            maxDay = 30;
            break;
        default:  // 31天的月份
            maxDay = 31;
            break;
    }

    return (day >= 1 && day <= maxDay);
}

// 综合验证:先正则校验格式,再逻辑校验日期有效性
bool isValidDateString(const std::string& date) {
    // 正则校验格式(YYYY-MM-DD)
    const std::regex dateRegex(
        R"(^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$)"
    );
    if (!std::regex_match(date, dateRegex)) {
        return false;
    }

    // 解析年、月、日
    int year, month, day;
    char dash1, dash2;
    std::istringstream iss(date);
    if (!(iss >> year >> dash1 >> month >> dash2 >> day)) {
        return false;
    }

    // 逻辑校验日期有效性
    return isValidDate(year, month, day);
}

TEST(DateValidationTest, LogicalValidity) {
    // 有效日期
    EXPECT_TRUE(isValidDateString("2023-10-05"));
    EXPECT_TRUE(isValidDateString("2024-02-29"));  // 2024是闰年
    EXPECT_TRUE(isValidDateString("1999-12-31"));
    EXPECT_TRUE(isValidDateString("2000-02-29"));  // 2000是闰年(能被400整除)

    // 无效日期(格式正确但逻辑错误)
    EXPECT_FALSE(isValidDateString("2023-02-30"));  // 2月没有30天(修复点)
    EXPECT_FALSE(isValidDateString("2023-02-29"));  // 2023不是闰年
    EXPECT_FALSE(isValidDateString("2019-04-31"));  // 4月只有30天
    EXPECT_FALSE(isValidDateString("2021-06-31"));  // 6月只有30天
    EXPECT_FALSE(isValidDateString("1900-02-29"));  // 1900不是闰年(能被100整除但不能被400整除)

    // 格式错误的日期(正则直接过滤)
    EXPECT_FALSE(isValidDateString("2023/10/05"));  // 分隔符错误
    EXPECT_FALSE(isValidDateString("23-10-05"));    // 年份格式错误
    EXPECT_FALSE(isValidDateString("2023-13-05"));  // 月份超出范围
    EXPECT_FALSE(isValidDateString("2023-06-0"));   // 日期格式不完整
}

TEST(StringRegexValidationTest, PasswordStrength) {
    // 密码强度正则:至少8位,包含大小写字母、数字和特殊符号
    const std::regex strongPasswordRegex(
        R"(^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$)"
    );

    // 有效强密码测试
    EXPECT_TRUE(matchesRegex("Passw0rd!", strongPasswordRegex));
    EXPECT_TRUE(matchesRegex("S3cr3t$123", strongPasswordRegex));

    // 无效密码测试
    EXPECT_FALSE(matchesRegex("weakpass", strongPasswordRegex)); // 无大写、数字和特殊符号
    EXPECT_FALSE(matchesRegex("Short1!", strongPasswordRegex));  // 长度不足
    EXPECT_FALSE(matchesRegex("nopunct123A", strongPasswordRegex)); // 无特殊符号
    EXPECT_FALSE(matchesRegex("NOLOWER1!", strongPasswordRegex)); // 无小写字母
}

// 测试字符串正向迭代器遍历
TEST(StringIteratorTest, ForwardTraversal) {
    std::string str = "hello";
    std::string result;

    // 使用正向迭代器遍历
    for (std::string::const_iterator it = str.cbegin(); it != str.cend(); ++it) {
        result += *it;
    }
    EXPECT_EQ(result, "hello");
}

// 测试字符串反向迭代器遍历
TEST(StringIteratorTest, ReverseTraversal) {
    std::string str = "hello";
    std::string reversed;

    // 使用反向迭代器遍历(逆序)
    for (std::string::const_reverse_iterator it = str.crbegin(); it != str.crend(); ++it) {
        reversed += *it;
    }
    EXPECT_EQ(reversed, "olleh");
}

// 测试使用迭代器修改字符串
TEST(StringIteratorTest, ModifyWithIterator) {
    std::string str = "hello";

    // 使用迭代器修改字符
    for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
        *it = std::toupper(static_cast<unsigned char>(*it));
    }
    EXPECT_EQ(str, "HELLO");
}

// 测试结合STL算法使用字符串迭代器
TEST(StringIteratorTest, StlAlgorithmUsage) {
    std::string str = "a1b2c3d4";
    std::string digits;

    // 使用STL算法提取数字字符
    std::copy_if(str.begin(), str.end(), std::back_inserter(digits),
                 [](char c) { return std::isdigit(static_cast<unsigned char>(c)); });
    EXPECT_EQ(digits, "1234");
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇