36 C++ STL模板库5-string

C++ STL模板库5-string

文章目录

std::string是一个STL模板类,可以指定字符类型,如 std::string(默认为 char类型)和 std::wstring(宽字符类型)。使用时,需要包含头文件 <string>

一、构造与赋值

- 构造函数

  1. 默认构造函数:string()
    std::string s1; // 空字符串 ""

  2. 拷贝构造函数:string(const string& str)

    cpp 复制代码
    std::string src("Hello");
    std::string s2(src);  // s2 = "Hello"
  3. 子字符串构造:string(const string& str, size_t pos, size_t len = npos)

    cpp 复制代码
    std::string base("Programming");
    std::string s3(base, 3, 4);  // 从索引3取4字符 → "gram"
    std::string s4(base, 6);     // 从索引6取到结尾 → "ing"
  4. 从C字符串构造:string(const char* s)

    cpp 复制代码
    std::string s5("C++17");  // 直接初始化 → "C++17"
  5. 从字符数组构造:string(const char* s, size_t n)

    cpp 复制代码
    const char arr[] = {'A','B','C','\0','D'};
    std::string s6(arr, 3);  // 仅取前3字符 → "ABC"(忽略\0)
  6. 填充构造:string(size_t n, char c)
    std::string s7(5, '!'); // 重复5次'!' → "!!!!!"

  7. 范围构造:template <class InputIterator> string(InputIterator first, InputIterator last)

    cpp 复制代码
    std::vector<char> vec = {'a', 'b', 'c'};
    std::string s8(vec.begin(), vec.end());  // → "abc"
  8. 移动构造(C++11):string(string&& str) noexcept

    cpp 复制代码
    std::string tmp("Movable");
    std::string s9(std::move(tmp));  // s9="Movable", tmp变为空
  9. 初始化列表构造(C++11):string(initializer_list<char> il)

    cpp 复制代码
    std::string s10{'H', 'i', '!'};  // → "Hi!"(等同于 s10 = "Hi!")

[完整示例]:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>  

int main() {
   std::string s1;               // 空字符串 ""
   
   std::string src("Hello");
   std::string s2(src);          // s2 = "Hello"
   
   std::string base("Programming");
   std::string s3(base, 3, 4);  // 从索引3取4字符 → "gram"
   std::string s4(base, 6);     // 从索引6取到结尾 → "ing"
   
   std::string s5("C++17");      // 直接初始化 → "C++17"
   
   const char arr[] = {'A','B','C','\0','D'};
   std::string s6(arr, 3);       // 仅取前3字符 → "ABC"(忽略\0)
   
   std::string s7(5, '!');       // 重复5次'!' → "!!!!!"
   
   std::vector<char> vec = {'a', 'b', 'c'};
   std::string s8(vec.begin(),  vec.end());   // → "abc"
   
   
   std::string tmp("Movable");
   std::string s9(std::move(tmp));  // s9="Movable", tmp变为空
   
   std::string s10{'H', 'i', '!'};  // → "Hi!"(等同于 s10 = "Hi!")
   
   std::cout<<s1<<std::endl;
   std::cout<<src<<std::endl;
   std::cout<<s2<<std::endl;
   std::cout<<base<<std::endl;
   
   std::cout<<std::endl;
   std::cout<<s3<<std::endl;
   std::cout<<s4<<std::endl;
   
   std::cout<<std::endl;
   std::cout<<s5<<std::endl;
   std::cout<<s6<<std::endl;
   std::cout<<s7<<std::endl;
   
   std::cout<<std::endl;
   std::cout<<s8<<std::endl;
   std::cout<<s9<<std::endl;
   std::cout<<s10<<std::endl;
   std::cout<<tmp<<std::endl;

   return 0;
}

输出结果:

cpp 复制代码
Hello
Hello
Programming

gram
mming

C++17
ABC
!!!!!

abc
Movable
Hi!

- 赋值操作

  1. operator=: 拷贝赋值、移动赋值、C字符串赋值、字符赋值

    cpp 复制代码
    std::string str;
    str = "Direct";           // C字符串赋值 → "Direct"
    str = std::string("Copy");// 拷贝赋值 → "Copy"
    std::string tmp("Move");
    str = std::move(tmp);     // 移动赋值 → "Move"(tmp变空)
    str = 'X';                // 字符赋值 → "X"
  2. assign(): 多种重载形式,功能类似构造函数

    cpp 复制代码
    str.assign(3, 'Z');       // 重复赋值 → "ZZZ"
    str.assign("C-String");   // C字符串赋值 → "C-String"
    std::string src("Source");
    str.assign(src, 2, 3);    // 子串赋值 → "urc"(从索引2取3字符)
    const char arr[] = "Array";
    str.assign(arr, 4);       // 限定长度 → "Arra"(取前4字符)
    std::list<char> lst = {'l','i','s','t'};
    str.assign(lst.begin(), lst.end());  // 迭代器赋值 → "list"

💡 关键说明

  • 优先用移动操作处理临时字符串,避免不必要的拷贝开销。
  • assign() 灵活性支持更多场景,如子串截取和迭代器范围赋值。

二、容量操作

  1. size()/length(): 返回字符数
  2. max_size(): 返回最大可能长度
  3. capacity(): 返回当前分配的存储容量
  4. reserve(): 预留存储空间
  5. resize(): 改变字符串大小
  6. empty(): 判断是否为空
  7. shrink_to_fit()(C++11): 减少容量以适应大小

[完整示例]

cpp 复制代码
#include <iostream>
#include <string>

int main() {
   // ===== 1. 基础容量操作 ===== 
   std::string s = "Hello";
   std::cout << "1. size/length: " << s.size()         // 5 
            << " | max_size: " << s.max_size()        // 非常大(约4611686018427387897)
            << " | capacity: " << s.capacity() << "\n"; // 15(SSO优化)

   // ===== 2. 预分配空间 ===== 
   s.reserve(100);
   std::cout << "2. 预留100后 capacity: " << s.capacity()  // 100+
            << " | size: " << s.size() << "\n";            // 5(内容不变)

   // ===== 3. 调整大小 ===== 
   s.resize(8, '!');  // 扩展到8字符,填充!
   std::cout << "3. resize(8,'!'): \"" << s << "\"\n"; // "Hello!!!"

   s.resize(3);       // 截断到3字符 
   std::cout << "   resize(3): \"" << s << "\"\n";     // "Hel"

   // ===== 4. 内存优化 ===== 
   s.shrink_to_fit();
   std::cout << "4. 压缩后 capacity: " << s.capacity() // ≈3-15(实现相关)
            << " | empty? " << std::boolalpha << s.empty() << "\n"; // false 

   // ===== 5. 空字符串处理 ===== 
   std::string empty_str;
   std::cout << "5. 空字符串: size=" << empty_str.size() 
            << " | empty? " << empty_str.empty() << "\n"; // true 
}

输出结果:

cpp 复制代码
1. size/length: 5 | max_size: 9223372036854775807 | capacity: 15
2. 预留100后 capacity: 100 | size: 5
3. resize(8,'!'): "Hello!!!"
   resize(3): "Hel"
4. 压缩后 capacity: 15 | empty? false
5. 空字符串: size=0 | empty? true

🔍 关键函数解析

函数 说明 时间复杂度 典型输出
size()/length() 当前字符数(不含\0 O(1) "Hello" → 5
max_size() 系统支持的最大长度 O(1) 通常 > 4亿字符
capacity() 当前分配的内存容量 O(1) 初始值由SSO决定
reserve(n) 预分配内存避免重复扩容 O(n) 扩容后 ≥ 指定值
resize(n, c) 调整长度(不足时填充字符c) O(n) 可扩展或截断
empty() 检查是否空字符串 O(1) "" → true
shrink_to_fit() 释放多余内存(非强制) 实现相关 容量 ≈ 实际大小

💡 使用技巧与注意事项

  1. SSO优化(短字符串优化)

    cpp 复制代码
    std::string short_str = "SSO!"; // 栈存储(capacity=15)
    std::string long_str(500, 'x'); // 堆存储(capacity≥500)

    当字符串≤15字符(编译器实现相关)时,直接存储在对象内部,避免堆分配

  2. 扩容策略

    cpp 复制代码
    std::string s;
    for (int i=0; i<1000; ++i) {
        s += 'a';          // 可能多次扩容(低效)
        // 优化:s.reserve(1000) 先预分配 
    }

    连续追加时优先用reserve()避免反复扩容

  3. 内存释放限制

    cpp 复制代码
    s.resize(3); 
    s.shrink_to_fit();    // 容量可能仍>3(实现可忽略)

    shrink_to_fit() 非绑定请求,编译器可能保留缓冲

  4. 安全截断

    cpp 复制代码
    std::string path = "/home/user/file.txt";
    path.resize(path.find('.'));  // 危险!若找不到返回npos 
    // 正确:if(auto pos=path.find('.')) path.resize(pos);

    截断前务必验证位置有效性,避免resize(npos)导致异常

  5. C++17优化

    cpp 复制代码
    s.clear(); 
    s.shrink_to_fit(); // C++17前需两步释放内存 
    // C++17: s = "";  // 可能直接触发内存释放 

    现代编译器对空字符串赋值可能自动释放堆内存

⚠️ 性能提示:高频修改字符串时(如日志处理),优先用reserve()预分配+std::string_view切片,可提升10倍以上性能。

三、元素访问

  1. operator[]: 访问指定位置字符(不检查边界)

    cpp 复制代码
    std::string s = "Hello";
    char c1 = s[1];    // 取第二个字符 → 'e'
    char c2 = s[10];   // 越界访问 → 未定义行为(可能崩溃或返回垃圾值)
  2. at(): 访问指定位置字符(检查边界)

    cpp 复制代码
    std::string s = "World";
    char c1 = s.at(4);  // 取第五个字符 → 'd'
    char c2 = s.at(5);  // 越界 → 抛 std::out_of_range 异常 
  3. front()(C++11): 访问第一个字符

  4. back()(C++11): 访问最后一个字符

    cpp 复制代码
    std::string s = "C++17";
    char first = s.front();  // 首字符 → 'C'
    char last = s.back();    // 尾字符 → '7'
    • 等效于 s[0]s[s.size()-1],但语义更清晰
    • 空字符串调用将导致未定义行为------需提前检查 !s.empty()
  5. data(): 返回指向内部数组的指针

    cpp 复制代码
    std::string s = "Data";
    const char* p = s.data();  // 指向内部数组的指针 
    printf("%s", p);           // 输出 "Data"
    
    // C++17 起可修改(需非常谨慎):
    std::string s2 = "Test";
    char* p2 = s2.data();      // 非 const 指针 
    p2[0] = 'B';              // s2 变为 "Best"
    • data() 不一定以 \0 结尾(除非显式添加)
    • 修改需确保不越界且字符串未重新分配内存
  6. c_str(): 返回C风格字符串

    cpp 复制代码
    std::string s = "File.txt";
    FILE* file = fopen(s.c_str(), "r");  // 传递给C函数 
    • 返回以 \0 结尾的只读指针
    • 调用后若字符串被修改,指针可能失效(如 s += "new"

**综合示例与安全实践**

  • 示例:

    cpp 复制代码
    std::string s = "SafeAccess";
    if (!s.empty()) {
       s.front() = 'L';      // 修改首字符 → "LafeAccess"
       s.back() = '!';       // 修改尾字符 → "LafeAcces!"
       s.data()[4] = 'X';// 修改第五个字符 → "LafeXcces!"  // C++17 起 data() 返回可写指针
    }
    const char* cstr = s.c_str();  // 保证以 '\0' 结尾 

安全准则:

  • 优先用 at() 处理不确定索引
  • 避免在 data()/c_str() 指针存活期间修改字符串
  • 修改操作后立即弃用旧指针

四、修改操作

追加

  1. operator+=: 追加字符串、C字符串或字符

    cpp 复制代码
    std::string s = "C++";
    s += " STL";               // 追加C字符串 → "C++ STL"
    s += '!';                     // 追加字符 → "C++ STL!"
    s += std::string(" Rocks"); // 追加string → "C++ STL! Rocks"
  2. append(): 多种重载形式,功能类似构造函数

    cpp 复制代码
    std::string s = "Hello";
    s.append(3, '!');               // 追加重复字符 → "Hello!!!"
    s.append("World", 0, 3);            // 追加子串 → "Hello!!!Wor"
    s.append(s.begin(), s.begin()+5); // 迭代器范围 → "Hello!!!WorHello"
  3. push_back(): 追加单个字符

    cpp 复制代码
    std::string s = "Open";
    for (char c : {'S','o','u','r','c','e'}) 
       s.push_back(c);   // → "OpenSource"

插入

  1. insert(): 在指定位置插入字符串、C字符串或字符

    cpp 复制代码
    std::string s = "C++20";
    s.insert(3, " Standard");   // 索引3插入前插入"Standard" → "C++ Standard20"
    s.insert(0, 2, '>');        // 开头插入重复字符'>' → ">>C++ Standard20"
    s.insert(s.find('2'), "17");// 在'2'前插入"17" → ">>C++ Standard1720"

删除

  1. erase(): 删除字符或子串

    cpp 复制代码
    std::string s = "Remove[this]Text";
    s.erase(6, 6);           // 从索引6删除6字符 → "RemoveText"
    s.erase(s.find('T'));    // 删除'T'到结尾 → "Remove"
    s.erase(s.begin());      // 删除首字符 → "emove"
  2. clear(): 清空字符串

    cpp 复制代码
    std::string s = "Temporary";
    s.clear();  // s = "",capacity()不变
  3. pop_back()(C++11): 删除最后一个字符

    cpp 复制代码
    std::string path = "/home/user/file.txt/";
    while (!path.empty() && path.back() == '/')
       path.pop_back();  // → "/home/user/file.txt"

替换

  1. replace(): 替换子串

    cpp 复制代码
    std::string s = "I like Java";
    size_t pos = s.find("Java");
    if (pos != std::string::npos)
       s.replace(pos, 4, "C++");  // → "I like C++"
    
    // 高效替换(避免临时对象)
    s.replace(s.begin(), s.begin()+1, "You");  // → "You like C++"

💡 关键特性对比表

操作 时间复杂度 典型场景 安全提示
operator+= O(n) 快速追加已知内容 避免循环内多次调用(预分配)
append() O(n) 精确控制追加长度/重复字符 指定长度时需确保内存有效
push_back() O(1)* 单字符高频追加 循环中优先于 +=
insert() O(n) 在已知位置插入内容 索引越界导致未定义行为
erase() O(n) 删除指定区间 删除后迭代器失效
replace() O(n) 内容替换(比erase+insert高效) 新旧长度不同可能触发内存重分配

*注:push_back() 均摊 O(1),但扩容时 O(n);

性能提示:高频修改时先用 reserve() 预分配内存。

⚠️ 边界安全实践

cpp 复制代码
#include <iostream>
#include <string>

int main() {
   
   std::string s = "SafeOperation";
   // 安全插入(校验位置)
   if (s.size()  >= 5) 
      s.insert(5,  "Insert"); 
   
   std::cout<<s<<std::endl;

   // 安全替换(校验子串存在性)
   s = "SafeOperation";
   auto pos = s.find("Operation"); 
   if (pos != std::string::npos) 
      s.replace(pos,  9, "Processing");

   std::cout<<s<<std::endl;
   
   
   return 0;
}

输出结果:

tex 复制代码
SafeOInsertperation
SafeProcessing

五、字符串操作

子串操作

  1. substr(): 获取子串

    cpp 复制代码
    std::string s = "Programming";
    // 索引映射:P(0) r(1) o(2) g(3) r(4) a(5) m(6) m(7) i(8) n(9) g(10)
    
    // 示例1:substr(pos=3, len=4)
    std::string sub1 = s.substr(3,  4);   // 索引3开始取4字符 → "gram"
    
    // 示例2:substr(pos=6) → 截取到结尾 
    std::string sub2 = s.substr(6);       // 索引6到结尾 → // 输出 mming
    
    // 示例3:substr(pos=s.find('g'))  → 首个'g'的索引为3 
    std::string sub3 = s.substr(s.find('g'));  // 首个'g'到结尾 → "gramming"

查找

字符串索引是从0开始 的连续整数,空格也占用索引位置。

均返回 size_t,失败返回 string::npos

  1. find(): 查找子串或字符

  2. rfind(): 反向查找子串或字符

  3. find_first_of(): 查找给出字符集中任一字符首次出现

  4. find_last_of(): 查找给出字符集中任一字符最后出现

  5. find_first_not_of(): 查找不在给出字符集中的字符首次出现

  6. find_last_not_of(): 查找不在字给出符集中的字符最后出现

    cpp 复制代码
    std::string text = "C++ Standard Template Library";
    // 字符索引分布(含空格):
     // C(0) +(1) +(2) [空格](3)
     // S(4) t(5) a(6) n(7) d(8) a(9) r(10) d(11) [空格](12)
     // T(13) e(14) m(15) p(16) l(17) a(18) t(19) e(20) [空格](21)
     // L(22) i(23) b(24) r(25) a(26) r(27) y(28)
    
    // 正向查找
    size_t pos1 = text.find("Template");    // 子串位置 → 13 
    size_t pos2 = text.find('S');           // 字符位置 → 4
    // 反向查找 
    size_t pos3 = text.rfind("a");          // 最后一个'a' → 26 (在"Library"中)
    
    // 字符集查找 
    size_t pos4 = text.find_first_of("aeiou");      // → 实际输出:6   首元音字母为 a(索引6,位于 "Standard" 中)
    size_t pos5 = text.find_last_of("   ");         // → 实际输出:21  最后一个空格在 "Template" 后(索引21)
    size_t pos6 = text.find_first_not_of("C+   ");  // → 实际输出:4   首部开始不是 "C/+/空格"的字符
    size_t pos7 = text.find_last_not_of("ary   ");  // → 实际结果:24  尾部最后一个非 a/r/y/空格 字符是 b(索引24)

性能提示:高频操作建议使用 std::string_view 避免拷贝,或预计算关键位置索引。

实用场景示例:

cpp 复制代码
// 场景1:提取文件扩展名 
std::string filename = "report.pdf"; 
size_t dot_pos = filename.rfind('.'); 
if (dot_pos != std::string::npos) {
   std::string ext = filename.substr(dot_pos  + 1); // "pdf"
}

// 场景2:删除字符串尾部空格
std::string input = "Hello World   ";
size_t last_char = input.find_last_not_of("  ");
if (last_char != std::string::npos) {
   input = input.substr(0,  last_char + 1); // "Hello World"
}

// 场景3:分割键值对 
std::string config = "timeout=30";
size_t eq_pos = config.find('='); 
std::string key = config.substr(0,  eq_pos);   // "timeout"
std::string value = config.substr(eq_pos  + 1); // "30"

关键注释

  1. npos 是特殊值 static const size_type npos = -1;,表示未找到

  2. 查找函数均支持第二个参数:起始搜索位置例如:

    cpp 复制代码
    size_t pos2 = text.find('t',  10);    // 从索引10找字符t → 19
    size_t pos4 = text.rfind("Template",  15); // 在索引15前反向查找 → 13
  3. 字符集查找函数(如 find_first_of)效率显著高于循环遍历字符

比较

  1. compare(): 比较字符串

    注意:compare方法返回的是int,比较结果只关心其符号表示(大于0,小于0,等于0),不要关心但具体数值,不同环境可能每次都有不同结果。

    cpp 复制代码
    std::string s1 = "apple", s2 = "app";
    int res1 = s1.compare(s2);        // >0(s1 > s2:"apple" vs "app")
    int res2 = s2.compare("apple");  // <0(s2 < "apple")
    
    std::string s = "C++17 Standard";
    // 比较 s的子串"17" 与 "20"
    int res3 = s.compare(3,  2, "20"); // <0(17 < 20)
       
    // 比较 s的子串"Standard" 与另一字符串的子串 
    std::string other = "C++20 Library";
    int res4 = s.compare(5,  7, other, 5, 7); // >0("Standard" > "Library")
    
    const char* cstr = "Hello";
    std::string str = "Hello";
    // 比较 string 与 C字符串 
    int res5 = str.compare(cstr);  // ==0(相等)
    // 比较前3字符 
    int res6 = str.compare(0,  3, "Hel"); // ==0(相等)
    
    // 执行逐字符ASCII值比较,非数值解析 比较只在首个字符差异处结束,不解析后续字符
    std::string version1 = "2.3.4";
    int res7 = version1.compare(0,  3, "12.1"); // >0(实际行为:字典序比较(字符 '2' (50) > '1' (49) → 返回正值)
    
    auto compare_log = [](int result) {
       if (result == 0) std::cout << "相等\n";
       else if (result > 0)std::cout << "左侧大\n";
       else                std::cout << "右侧大\n";
    };
    compare_log(std::string("banana").compare("apple"));  // 输出 "左侧大"('b' > 'a')
  2. operator==, operator!=, operator<, operator>, operator<=, operator>=: 关系运算符

  • 🔑 运算符行为解析表

    运算符 等效调用 比较规则 典型误用场景
    == s1.compare(s2)==0 逐字符全等 忽略大小写差异("A"≠"a")
    != s1.compare(s2)!=0 至少一个字符不等 误判不同长度的相等前缀
    < s1.compare(s2)<0 首个差异字符ASCII较小,或更短且前缀相同 数字字符串比较("100"<"20")
    > s1.compare(s2)>0 首个差异字符ASCII较大,或更长且前缀相同 非字母字符顺序("#"<"1")
    <= !(s1>s2) 等价于"小于或等于" 组合逻辑理解错误
    >= !(s1<s2) 等价于"大于或等于" 空字符串的特殊处理

六、迭代器支持

  1. begin()/cbegin()(C++11): 返回指向开头的迭代器

  2. end()/cend()(C++11): 返回指向结尾的迭代器

    cpp 复制代码
    std::string text = "c++17";
    
    // 1. 非常量迭代器(可修改内容) 
    for (auto it = text.begin(); it != text.end(); ++it) {
       *it = std::toupper(*it);  //转换字符串为大写 修改字符:c++17 → C++17 
    }
    
    // 2. 常量迭代器(只读安全)
    std::cout << "字符序列: ";
    for (auto cit = text.cbegin(); cit != text.cend(); ++cit) {
       std::cout << *cit << " ";  // 输出: C + + 1 7
    }
  3. rbegin()/crbegin()(C++11): 返回反向开头迭代器

  4. rend()/crend()(C++11): 返回反向结尾迭代器

    cpp 复制代码
    std::string palindrome = "level";
    // 1. 反向非常量迭代器
    auto rit = palindrome.rbegin(); 
    *rit = 'L';  // 修改末尾字符:level → leveL 
    
    std::string filename = "document.backup.zip"; 
    //2. 反向查找最后一个点号 
    auto rdot = std::find(filename.crbegin(), filename.crend(),'.' );

⚙️迭代器类型对照表

迭代器类型 访问权限 方向 典型应用场景 图示示例
begin() 读写 正向 遍历修改字符 [C][+][+][1][7]
cbegin() 只读 const 正向 安全读取 [C][+][+][1][7]
rbegin() 读写 反向 从后向前修改(如路径处理) [C][+][+][1][7]
crbegin() 只读 const 反向 反向搜索(如扩展名提取) [C][+][+][1][7]

注:

  • end()/cend() 指向 末位后一位置(哨兵位,不可解引用)
  • rend()/crend() 指向 首位前一位置(哨兵位,不可解引用)
  • 反向迭代器 rbegin() 实际指向最后一个元素,rend() 指向第一个元素前
  • 一致性原则,始终使用 相同类型 的迭代器构造子串, const_iterator + cend()iterator + end()
  • 优先选择 cbegin()/cend() ,明确只读意图

七、数值转换(C++11起)

  1. 有符号转换为整数
    stoi() - 字符串转 int
    stol() - 字符串转 long
    stoll():- 字符串转 long long

    cpp 复制代码
    // stoi - 字符串转 int 
     std::string num1 = "42";
     int i = std::stoi(num1);
     std::cout << "stoi: " << i * 2 << std::endl;  // 84
     
     // stol - 字符串转 long(处理大数值)
     std::string num2 = "2147483647";  // > INT_MAX 
     long l = std::stol(num2);
     std::cout << "stol: " << l / 2 << std::endl;  // 1073741823
     
     // stoll - 字符串转 long long(极大值)
     std::string num3 = "9223372036854775807"; // LLONG_MAX 
     long long ll = std::stoll(num3, nullptr, 0);  // 自动识别进制 
     std::cout << "stoll: " << ll - 1 << std::endl;// 9223372036854775806 
    • 有符号整数转换函数对比

      | 函数 | 返回类型 | 典型应用场景 | 特殊处理机制 |

      |----------|---------------|-----------------------|----------------------|

      | stoi() | int | 通用整数解析 | 自动忽略前导空白 |

      | stol() | long | 大数值/跨平台兼容 | 支持0开头的八进制数 |

      | stoll()| long long | 超大整数(如时间戳) | 支持0x开头的十六进制 |

      示例差异:

      cpp 复制代码
      std::stoi("2147483647");  // ✅ 最大int值  
      std::stoi("2147483648");  // ❌ 抛出out_of_range  
      std::stoll("9223372036854775807");  // ✅ 最大long long
  2. 转换为无符号整数
    stoul() - 字符串转 unsigned long
    stoull()- 字符串转 unsigned long long

    cpp 复制代码
    std::cout << std::stoul("-100") << std::endl;//4294967196
    std::cout << std::stoul("0xFFFF", nullptr, 16) << std::endl;//65535
    std::cout << std::stoull("1111000011110000", nullptr, 2) << std::endl;//61680
    
    //进制处理示例:
    std::stoul("0xFF", nullptr, 16);  // 255(明确指定16进制)  
    std::stoull("0755", nullptr, 0);  // 493(0前缀自动识别为8进制)
    • 无符号整数转换函数对比
    函数 返回类型 关键特性 负值处理
    stoul() unsigned long 支持2-36进制解析 遇"-"立即抛出异常
    stoull() unsigned long long 可解析0x/0X前缀的十六进制 拒绝任何负号
  3. 转换为浮点数
    stof() - 字符串转 float
    stod() - 字符串转 double(更高精度)
    stold()- 字符串转 long double(最高精度)

    • 示例
    cpp 复制代码
    // stof - 字符串转 float 
    std::string pi_str = "3.14159";
    float f = std::stof(pi_str);
    std::cout << "stof: " << f * 2 << std::endl;  // 6.28318 
    
    // stod - 字符串转 double(更高精度)
    std::string sci_str = "6.022e23";
    double d = std::stod(sci_str);
    std::cout << "stod: " << d / 1e21 << std::endl;  // 602.2
    
    // stold - 字符串转 long double(最高精度)
    std::string precise = "0.1234567890123456789";
    long double ld = std::stold(precise);
    std::cout.precision(21); ////设置精度为21位,此精度为小数点位数
    std::cout << "stold: " << ld << std::endl;  // 0.123456789012345678901 
    • 精度差异验证:
    cpp 复制代码
    std::stof("0.123456789");    // → 0.123456791(第9位失真)  
    std::stod("0.1234567890123456"); // 保留15位精度  
    std::stold("0.1234567890123456789"); // 完整保留21位数字 
  4. to_string(): 数值转字符串
    to_string性能优势:比sprintf快3倍,比stringstream快5倍.

    cpp 复制代码
    std::to_string(42);          // int → "42"  
    std::to_string(3.14f);       // float → "3.140000"  
    std::to_string(true);        // bool → "1"  
    std::to_string(18446744073709551615ULL); // unsigned long long 
  5. 注意事项:

    • 范围与精度: 整型 ------ stoll > stol > stoi ,浮点 ------ stold > stod > stof

    • 整型转换均可指定进制(2~36),浮点仅支持10进制

    • 参数错误时抛出异常:
      std::cout<< std::stoi("hello")<< std::endl;

      异常:
      terminate called after throwing an instance of 'std::invalid_argument' what(): stoi

    • 超范围异常:
      std::cout<< std::stoi("2147483648")<< std::endl;

      异常:
      terminate called after throwing an instance of 'std::out_of_range' what(): stoi

    • 字符串转数字,遇到非数字格式的字符串时停止:
      size_t pos; std::cout<<std::stod("3,14159", &pos)<<' '<<pos<<std::endl;// 3 1,因逗号停止

    • 浮点精度陷阱

      cpp 复制代码
      // 错误用法:直接比较转换结果
      float f1 = std::stof("3.14"); 
      float f2 = 3.14f; 
      if (f1 == f2) // 可能失败!
      
      // 正确方案:允许误差范围
      if (std::abs(f1 - f2) < 1e-6) 
    • 所有的stox函数原型都是:

      cpp 复制代码
      stox ( const std::string& str, std::size_t* pos = nullptr,int base = 10 );

      且每个函数都有一个对应的宽字节版本,只有第一个参数的类型不同:

      cpp 复制代码
      stox ( const std::wstring& str, std::size_t* pos = nullptr,int base = 10 );
    • to_string()也有一个对应的宽字节版本to_wstring().

💎 跨函数核心差异总结

维度 整型转换 无符号转换 浮点转换 to_string()
符号处理 接受负号 拒绝负号 接受负号 保留原始符号
进制支持 支持2-36进制 支持2-36进制 仅十进制 固定十进制输出
异常类型 invalid_argument out_of_range out_of_range 永不抛出异常
前缀解析 自动识别0/0x 自动识别0/0x 忽略数字前缀 不适用
空白处理 跳过前导空白 跳过前导空白 跳过前导空白 无空白生成

八、其他操作

  1. swap(): 交换内容(高效交换底层数据)

    • 不涉及内存复制,仅交换内部指针
    • a = b 赋值操作高效10倍以上
    cpp 复制代码
    std::string a = "AAA";
    std::string b = "BBB";
    
    a.swap(b);   // 或 swap(a, b)
    
    std::cout << "a: " << a << "\n";  // 输出: BBB 
    std::cout << "b: " << b << "\n";  // 输出: AAA 
  2. get_allocator(): 获取分配器

    • 功能:获取字符串使用的内存分配器对象
    cpp 复制代码
    std::string s = "Allocator Demo";
    // 获取分配器副本 
    auto alloc = s.get_allocator();   
       
    // 使用相同分配器创建新字符串 
    std::string s2(alloc);
    s2 = "Same allocator";
  3. copy(): 复制字符序列到数组

    • 将字符串内容复制到C风格字符数组
    • copy() 不自动添加 \0,需手动添加
    cpp 复制代码
    std::string s = "Copy\0Demo";  // 含空字符 
    char buffer[20];
    
    // 复制操作(返回实际复制字符数)len最大为sizeof(buffer)-1 确保不越界
    size_t len = s.copy(buffer,  sizeof(buffer)-1); 
    
    buffer[len] = '\0';  // 手动添加终止符 
    
    std::cout << "内容: " << buffer           // 输出: Copy C风格字符串以\0终止
             << "\n长度: " << strlen(buffer)  // 输出: 4 
             << "\n实际复制: " << len;         // 输出: 4    \0Demo 部分被丢弃

九、非成员函数

  1. operator+: 字符串连接

    功能:创建新字符串(不修改原对象)

    • 效率较低(涉及内存分配和拷贝),循环连接优先用std::ostringstream
    • C++14起支持字面量后缀s简化操作
    cpp 复制代码
    auto s1 = "Hello, "s;
    auto s2 = "world!"s;
    
    // 三种连接方式 
    auto r1 = s1 + s2;           // 字符串+字符串 → "Hello, world!"
    auto r2 = s1 + "C++";        // 字符串+字面量 → "Hello, C++"
    auto r3 = "Code: "s + s2;    // 字面量+字符串 → "Code: world!"
  2. operator>>/operator<<: 流输入输出

    功能:类型安全的数据读写

    • 自动类型转换(数值↔字符串)
    • 线程安全(每个流独立缓冲区)
    • 支持自定义类型的重载(如<< vector
    cpp 复制代码
    #include <iostream>
    #include <string>
    #include <sstream>
    
    int main() {
       // 输出流(格式化写入)
       std::ostringstream oss;
       oss << "PI=" << 3.14159 << " Score:" << 95;
       std::cout<<oss.str()<<std::endl;//PI=3.14159 Score:95
    
       
       // 输入流(空格分隔解析)
       std::istringstream iss("John 25 175.5");
       std::string name;
       int age;  double height;
       
       // 把 iss.str()的内容以空格分隔给了 name,age,height
       iss >> name >> age >> height; // name="John", age=25, height=175.5
       std::cout<<name<<age<<height<<std::endl;//John25175.5
       
    }
  3. getline(): 从流中读取一行

    功能:读取整行文本(含空格)

    • 返回值是输入流对象本身的引用(std::istream&)
    • 默认移除换行符\n(可通过std::noskipws保留)
    • >>更安全(无缓冲区溢出风险)
    cpp 复制代码
    #include <iostream>
    #include <sstream>
    #include <string>
    
    int main() {
       std::string text = 
          "风急天高猿啸哀,渚清沙白鸟飞回。\n"
          "无边落木萧萧下,不尽长江滚滚来。\n"
          "\r\n"
          "万里悲秋常作客,百年多病独登台。\n"
          "    艰难苦恨繁霜鬓,潦倒新停浊酒杯。\n"  // 此行有行首空格 
          "End of text";
    
       std::istringstream stream(text);
       std::string line;
       int line_count = 0;
       //// ✅ 利用返回值状态控制循环
       while (std::getline(stream, line)) {
          ++line_count;
       }
       
       std::cout << "文本总行数: " << line_count << "\n";//文本总行数: 6
       return 0;
    }
  4. 关系运算符的非成员重载版本

    功能:支持混合类型比较

    比较规则:

    1. 字典序比较(区分大小写)
    2. 短字符串<长字符串(若前缀相同)
    3. 空指针nullptr会抛出异常(需提前检查)
    cpp 复制代码
    #include <string>
    #include <cstring>
    #include <iostream>
    
    int main() {
       std::string s = "Apple";
       const char* p = "Apple";
       
       // 等效比较(非成员函数)
       bool b1 = (s == p);    // true → operator==(const string&, const char*)
       bool b2 = (p < s);     // false → operator<(const char*, const string&)
       bool b3 = (std::string("Banana") > s); // true → operator>(const char*, const string&)
       
       std::cout<<b1<<std::endl;//1
       std::cout<<b2<<std::endl;//0
       std::cout<<b3<<std::endl;//1
       
       // C风格字符串直接比较
       if (std::strcmp(p, "Apple") == 0) { std::cout<<true<<std::endl; }//1
    }
  5. swap(): 特化的swap算法

    功能:高效交换内容(O(1)时间复杂度)

    • <algorithm>算法头文件
    cpp 复制代码
    #include <string>
    #include <cstring>
    #include <iostream>
    #include <string>
    #include <algorithm>  // std::swap
    
    int main() {
       std::string a = "苹果";
       std::string b = "香蕉";
       
       // 两种交换方式
       //成员函数swap()
       a.swap(b);            // 成员函数 → a="香蕉", b="苹果"
       std::cout<<a<<std::endl;//香蕉
       std::cout<<b<<std::endl;//苹果
    
       //算法std::swap()
       std::swap(a, b);      // 算法特化 → 恢复原值
       std::cout<<a<<std::endl;//苹果
       std::cout<<b<<std::endl;//香蕉
       
       
       // 大型数据交换优势 
       std::string big(1000000, 'A');  // 1MB数据 
       std::string small("Hi");
       big.swap(small);      // 瞬间完成(仅交换内部指针)
       
       std::cout<<big<<std::endl;//Hi
       std::cout<<small<<std::endl;// 输出了1000000个'A'
       
    }

注意事项💡

  • 高频查找大文本时优先用 std::string_view,避免 substr() 的拷贝开销。
  • 优先用+=替代频繁+,循环连接用reserve()预分配内存.
cpp 复制代码
// 每次比较都会构造临时string对象 
if ("temporary" == s) { ... }

// 优化方案:提前构造变量 
const std::string temp = "temporary";
if (temp == s) { ... }

[附录]

cpp 复制代码
string 一些类型定义
类型           定义
string             char
wstring          wchar_t
u8string        char8_t
u16string      char16_t
u32string      char32_t

详见

参考\]: \[1\] \[2\]