当前位置: 首页 > news >正文

36 C++ STL模板库5-string

C++ STL模板库5-string

文章目录

  • C++ STL模板库5-string
    • 一、构造与赋值
      • - 构造函数
      • - 赋值操作
    • 二、容量操作
    • 三、元素访问
    • 四、修改操作
      • 追加
      • 插入
      • 删除
      • 替换
    • 五、字符串操作
      • 子串操作
      • 查找
      • 比较
    • 六、迭代器支持
    • 七、数值转换(C++11起)
    • 八、其他操作
    • 九、非成员函数
    • [附录]

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

一、构造与赋值

- 构造函数

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

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

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

    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)

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

    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)

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

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

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

[完整示例]:

#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;
}

输出结果:


Hello
Hello
Programminggram
mmingC++17
ABC
!!!!!abc
Movable
Hi!

- 赋值操作

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

    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(): 多种重载形式,功能类似构造函数

    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): 减少容量以适应大小

[完整示例]

#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 
}

输出结果:

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()当前字符数(不含\0O(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优化(短字符串优化)

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

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

  2. 扩容策略

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

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

  3. 内存释放限制

    s.resize(3); 
    s.shrink_to_fit();    // 容量可能仍>3(实现可忽略)
    

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

  4. 安全截断

    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优化

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

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

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

三、元素访问

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

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

    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): 访问最后一个字符

    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(): 返回指向内部数组的指针

    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风格字符串

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

[综合示例与安全实践]

  • 示例:

    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字符串或字符

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

    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(): 追加单个字符

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

插入

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

    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(): 删除字符或子串

    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(): 清空字符串

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

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

替换

  1. replace(): 替换子串

    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() 预分配内存。

⚠️ 边界安全实践

#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;
}

输出结果:

SafeOInsertperation
SafeProcessing

五、字符串操作

子串操作

  1. substr(): 获取子串

    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(): 查找不在字给出符集中的字符最后出现

    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 避免拷贝,或预计算关键位置索引。
实用场景示例:

// 场景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. 查找函数均支持第二个参数:起始搜索位置例如:

    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),不要关心但具体数值,不同环境可能每次都有不同结果。

    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): 返回指向结尾的迭代器

    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): 返回反向结尾迭代器

    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

    // 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开头的十六进制 |

      示例差异:

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

    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(最高精度)

    • 示例
    // 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 
    
    • 精度差异验证:
    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倍.

    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,因逗号停止

    • 浮点精度陷阱

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

      stox ( const std::string& str, std::size_t* pos = nullptr,int base = 10 );
      

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

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

💎 跨函数核心差异总结

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

八、其他操作

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

    • 不涉及内存复制,仅交换内部指针
    • a = b 赋值操作高效10倍以上
    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(): 获取分配器

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

    • 将字符串内容复制到C风格字符数组
    • copy() 不自动添加 \0,需手动添加
    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简化操作
    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
    #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,heightiss >> name >> age >> height; // name="John", age=25, height=175.5std::cout<<name<<age<<height<<std::endl;//John25175.5}
    
  3. getline(): 从流中读取一行
    功能:读取整行文本(含空格)

    • 返回值是输入流对象本身的引用(std::istream&)
    • 默认移除换行符\n(可通过std::noskipws保留)
    • >>更安全(无缓冲区溢出风险)
    #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";//文本总行数: 6return 0;
    }
    
  4. 关系运算符的非成员重载版本
    功能:支持混合类型比较
    比较规则:

    1. 字典序比较(区分大小写)
    2. 短字符串<长字符串(若前缀相同)
    3. 空指针nullptr会抛出异常(需提前检查)
    #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;//1std::cout<<b2<<std::endl;//0std::cout<<b3<<std::endl;//1// C风格字符串直接比较if (std::strcmp(p, "Apple") == 0) { std::cout<<true<<std::endl; }//1
    }
    
  5. swap(): 特化的swap算法
    功能:高效交换内容(O(1)时间复杂度)

    • <algorithm>算法头文件
    #include <string>
    #include <cstring>
    #include <iostream>
    #include <string>
    #include <algorithm>  // std::swapint 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;//Histd::cout<<small<<std::endl;// 输出了1000000个'A'}
    

注意事项💡

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

[附录]

string 一些类型定义
类型           定义
string             char
wstring          wchar_t
u8string        char8_t
u16string      char16_t
u32string      char32_t

详见

[参考]:

[1]https://cppreference.com/w/cpp/string/basic_string.html
[2]https://legacy.cplusplus.com/reference/string/string

http://www.dtcms.com/a/330370.html

相关文章:

  • Python网络爬虫(二) - 解析静态网页
  • IPTV系统:开启视听与管理的全新篇章
  • CMake 如何查找 Python2和Python3
  • 利用 Python 爬虫按图搜索 1688 商品(拍立淘)实战指南
  • 17. 如何判断一个对象是不是数组
  • 肖臻《区块链技术与应用》第十一讲:比特币核心概念重温:一文读懂私钥、交易、挖矿与网络现状
  • Redis7学习——Redis的十大类型String、List、Hash、Set、Zset
  • 解决:Gazebo连接模型数据库失败
  • linux 内核 - 内存管理概念
  • Apifox精准定义复杂API参数结构(oneOf/anyOf/allOf)
  • aave v3 存款与借款利息的计算方式
  • 码上爬第七题【协程+参数加密+响应解密+格式化检测】
  • C#面试题及详细答案120道(11-20)-- 面向对象编程(OOP)
  • LeetCode Day5 -- 二叉树
  • 嵌入式学习(day26)frambuffer帧缓冲
  • 【系统安装】虚拟机中安装win10企业版系统记录
  • HarmonyOS 开发实战:搞定应用名字与图标更换,全流程可运行示例
  • 101、【OS】【Nuttx】【周边】文档构建渲染:reStructuredText 格式
  • 硬件工程师八月实战项目分享
  • AI抢饭碗,软件测试该何去何从?
  • 基于离散余弦变换的激活水印(DCT-AW)
  • 交错字符串-二维dp
  • 如何通过 Actor 网络压缩为概率分布实现
  • RK3568 Linux驱动学习——新字符设备驱动
  • 人工智能入门①:AI基础知识(上)
  • Vue3 vs Vue2:全面对比与面试宝典
  • 接口添加了 @Transactional 注解并开启事务,而其中一个小方法启动了新线程并手动提交数据,会有什么影响?
  • 红黑树的特性与实现
  • 打靶日常-文件上传
  • 【Python】新手入门:什么是python运算符?python运算符有哪些种类?运算符优先级是怎么样的?