MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C++ string类用法详解

2022-08-137.1k 阅读

C++ string 类基础介绍

在 C++ 中,string 类定义在 <string> 头文件中,它提供了一种方便的方式来处理字符串。与传统的 C 风格字符串(以 '\0' 结尾的字符数组)相比,string 类具有诸多优势,例如自动管理内存,提供丰富的成员函数来操作字符串等。

在使用 string 类之前,必须包含 <string> 头文件:

#include <string>

1. 定义和初始化

  • 默认初始化

    std::string s1;
    

    这会创建一个空的 string 对象。

  • 使用字符串字面量初始化

    std::string s2 = "Hello, World!";
    std::string s3("Hello, again");
    

    这两种方式都使用字符串字面量初始化了 string 对象。

  • 使用其他 string 对象初始化

    std::string s4 = s2;
    std::string s5(s3);
    

    这里 s4s5 分别通过拷贝 s2s3 进行初始化。

  • 使用部分字符串初始化

    std::string s6(s2, 0, 5); // 从 s2 的第 0 个位置开始,取 5 个字符,s6 为 "Hello"
    
  • 使用字符重复初始化

    std::string s7(5, 'a'); // s7 为 "aaaaa"
    

2. 基本操作

字符串连接

  • 使用 + 运算符

    std::string s1 = "Hello";
    std::string s2 = " World";
    std::string s3 = s1 + s2; // s3 为 "Hello World"
    

    也可以将 string 对象与字符串字面量连接:

    std::string s4 = s1 + " from C++"; // s4 为 "Hello from C++"
    
  • 使用 += 运算符

    std::string s5 = "Goodbye";
    s5 += " cruel world"; // s5 为 "Goodbye cruel world"
    

字符串比较

  • 使用 ==!=<><=>= 运算符
    std::string s1 = "apple";
    std::string s2 = "banana";
    if (s1 < s2) {
        std::cout << s1 << " comes before " << s2 << std::endl;
    }
    
    这些运算符按照字典序比较字符串。

获取字符串长度

length()size() 成员函数可以获取 string 对象中字符的个数:

std::string s = "example";
std::cout << "Length of s: " << s.length() << std::endl;
std::cout << "Size of s: " << s.size() << std::endl;

length()size() 在功能上是等效的。

访问字符

  • 使用 [] 运算符

    std::string s = "programming";
    std::cout << "The first character: " << s[0] << std::endl;
    

    可以通过 [] 运算符访问 string 对象中的单个字符,索引从 0 开始。但是这种方式不会进行越界检查,如果访问越界,行为是未定义的。

  • 使用 at() 成员函数

    std::string s = "example";
    try {
        std::cout << "The 10th character: " << s.at(9) << std::endl;
    } catch (const std::out_of_range& e) {
        std::cerr << "Out of range error: " << e.what() << std::endl;
    }
    

    at() 函数会进行越界检查,如果访问越界,会抛出 std::out_of_range 异常。

3. 字符串修改

插入字符或字符串

  • 在指定位置插入单个字符

    std::string s = "hello";
    s.insert(1, 'x'); // s 变为 "hxello"
    

    第一个参数是插入位置的索引,第二个参数是要插入的字符。

  • 在指定位置插入字符串

    std::string s1 = "world";
    std::string s2 = "hello ";
    s1.insert(0, s2); // s1 变为 "hello world"
    

    这里在 s1 的开头(索引 0 处)插入了 s2

删除字符或字符串

  • 删除指定位置的单个字符

    std::string s = "hello";
    s.erase(1, 1); // s 变为 "hllo",从索引 1 处删除 1 个字符
    

    第一个参数是起始位置索引,第二个参数是要删除的字符个数。

  • 删除指定范围的字符

    std::string s1 = "programming";
    s1.erase(3, 4); // s1 变为 "progming",从索引 3 处开始删除 4 个字符
    

替换字符或字符串

  • 替换指定范围的字符

    std::string s = "hello world";
    s.replace(6, 5, "C++"); // s 变为 "hello C++",从索引 6 处开始,用 "C++" 替换 5 个字符
    

    第一个参数是起始位置索引,第二个参数是要替换的字符个数,第三个参数是用于替换的字符串。

  • 使用另一个字符串的部分进行替换

    std::string s1 = "goodbye";
    std::string s2 = "hello world";
    s1.replace(0, 7, s2, 6, 5); // s1 变为 "world",用 s2 从索引 6 处开始的 5 个字符替换 s1 从索引 0 处开始的 7 个字符
    

4. 查找子字符串和字符

查找子字符串

  • find() 函数

    std::string s = "hello world";
    size_t pos = s.find("world");
    if (pos != std::string::npos) {
        std::cout << "Substring found at position: " << pos << std::endl;
    } else {
        std::cout << "Substring not found" << std::endl;
    }
    

    find() 函数从字符串开头开始查找子字符串,返回子字符串首次出现的位置。如果未找到,返回 std::string::npos,这是一个表示不存在位置的特殊值。

  • rfind() 函数

    std::string s1 = "banana";
    size_t pos1 = s1.rfind("na");
    if (pos1 != std::string::npos) {
        std::cout << "Substring found at position (rfind): " << pos1 << std::endl;
    }
    

    rfind() 函数从字符串末尾开始查找子字符串,返回子字符串最后一次出现的位置。

查找字符

  • find_first_of() 函数

    std::string s = "hello world";
    size_t pos2 = s.find_first_of('o');
    if (pos2 != std::string::npos) {
        std::cout << "Character 'o' found at position: " << pos2 << std::endl;
    }
    

    find_first_of() 函数查找字符串中首次出现的指定字符,返回字符首次出现的位置。

  • find_last_of() 函数

    std::string s2 = "hello world";
    size_t pos3 = s2.find_last_of('o');
    if (pos3 != std::string::npos) {
        std::cout << "Character 'o' found at position (last): " << pos3 << std::endl;
    }
    

    find_last_of() 函数查找字符串中最后一次出现的指定字符,返回字符最后出现的位置。

5. 字符串流操作

<sstream> 头文件提供了 stringstream 类,用于在字符串和其他数据类型之间进行转换。

字符串到数值的转换

#include <sstream>
#include <iostream>

int main() {
    std::string str = "123";
    int num;
    std::stringstream ss(str);
    ss >> num;
    std::cout << "Converted number: " << num << std::endl;
    return 0;
}

这里使用 stringstream 将字符串 "123" 转换为整数 123

数值到字符串的转换

#include <sstream>
#include <iostream>

int main() {
    int num = 456;
    std::stringstream ss;
    ss << num;
    std::string str = ss.str();
    std::cout << "Converted string: " << str << std::endl;
    return 0;
}

此代码将整数 456 转换为字符串 "456"

6. C 风格字符串的转换

string 转 C 风格字符串

  • c_str() 函数
    std::string s = "example";
    const char* cstr = s.c_str();
    
    c_str() 函数返回一个指向以 '\0' 结尾的 C 风格字符串的指针。注意返回的指针指向的内存是 string 对象内部管理的,并且只要 string 对象存在,该指针就有效。

C 风格字符串转 string

const char* cstr = "hello";
std::string s(cstr);

直接使用 C 风格字符串作为 string 构造函数的参数即可完成转换。

7. 迭代器

string 类支持迭代器,这使得可以像处理其他容器一样遍历字符串。

正向迭代

std::string s = "hello";
for (std::string::iterator it = s.begin(); it != s.end(); ++it) {
    std::cout << *it;
}
std::cout << std::endl;

这里使用 begin() 获取指向字符串开头的迭代器,end() 获取指向字符串末尾(但不包含末尾字符)的迭代器。

反向迭代

std::string s1 = "world";
for (std::string::reverse_iterator rit = s1.rbegin(); rit != s1.rend(); ++rit) {
    std::cout << *rit;
}
std::cout << std::endl;

rbegin() 获取指向字符串末尾的反向迭代器,rend() 获取指向字符串开头之前的反向迭代器,从而实现反向遍历。

8. 内存管理和性能

string 类在内部管理字符串的内存。当字符串内容发生变化时,string 类会根据需要重新分配内存。

容量和预留

  • 容量(capacity)capacity() 函数返回当前 string 对象分配的内存空间能够容纳的字符数(不包括结尾的 '\0')。
    std::string s = "hello";
    std::cout << "Capacity of s: " << s.capacity() << std::endl;
    
  • 预留(reserve)reserve() 函数可以预先分配一定大小的内存,以避免频繁的内存重新分配。
    std::string s1;
    s1.reserve(100);
    
    这会为 s1 预留至少能容纳 100 个字符的内存空间。

收缩到合适大小

shrink_to_fit() 函数可以将 string 对象的容量调整为实际使用的大小,释放多余的内存:

std::string s2 = "example";
s2.reserve(100);
// 做一些操作后
s2.shrink_to_fit();

9. 本地化和多字节字符串

在处理不同语言和字符编码时,string 类也有一定的支持。

宽字符字符串

C++ 提供了 wstring 类来处理宽字符字符串,定义在 <string> 头文件中。宽字符通常用于表示非 ASCII 字符,例如中文、日文等。

#include <iostream>
#include <string>

int main() {
    std::wstring ws = L"你好,世界";
    std::wcout << ws << std::endl;
    return 0;
}

这里使用 L 前缀来表示宽字符字符串字面量。wcout 用于输出宽字符字符串。

多字节字符串处理

在处理多字节字符编码(如 UTF - 8)时,string 类本身按字节处理字符串。对于更复杂的多字节字符串操作,可以使用一些库,如 iconv 库(在 Unix - like 系统上)来进行编码转换。例如,将 UTF - 8 编码的字符串转换为其他编码:

#include <iostream>
#include <string>
#include <iconv.h>

std::string convertEncoding(const std::string& input, const char* fromCode, const char* toCode) {
    iconv_t cd = iconv_open(toCode, fromCode);
    if (cd == (iconv_t)-1) {
        std::cerr << "Failed to open conversion" << std::endl;
        return "";
    }

    std::string output;
    const char* inbuf = input.c_str();
    size_t inbytesleft = input.size();
    char outbuf[1024];
    char* outptr = outbuf;
    size_t outbytesleft = sizeof(outbuf);

    while (inbytesleft > 0) {
        size_t result = iconv(cd, &inbuf, &inbytesleft, &outptr, &outbytesleft);
        if (result == (size_t)-1) {
            std::cerr << "Conversion error" << std::endl;
            iconv_close(cd);
            return "";
        }
        output.append(outbuf, sizeof(outbuf) - outbytesleft);
        outptr = outbuf;
        outbytesleft = sizeof(outbuf);
    }

    iconv_close(cd);
    return output;
}

int main() {
    std::string utf8Str = "你好";
    std::string gb2312Str = convertEncoding(utf8Str, "UTF - 8", "GB2312");
    std::cout << "Converted string: " << gb2312Str << std::endl;
    return 0;
}

此代码示例展示了如何使用 iconv 库将 UTF - 8 编码的字符串转换为 GB2312 编码的字符串。实际应用中,需要根据具体需求处理错误和优化代码。

10. 与其他库的结合使用

string 类在与其他 C++ 库结合使用时也非常方便。

与 STL 算法结合

STL(标准模板库)提供了丰富的算法,string 类可以很好地与这些算法配合。例如,使用 std::find 算法查找字符串中的字符:

#include <iostream>
#include <string>
#include <algorithm>

int main() {
    std::string s = "hello world";
    auto it = std::find(s.begin(), s.end(), 'o');
    if (it != s.end()) {
        std::cout << "Character 'o' found at position: " << std::distance(s.begin(), it) << std::endl;
    }
    return 0;
}

这里使用 std::find 算法在 string 对象 s 中查找字符 'o'

与文件操作结合

在文件读写操作中,string 类可以方便地处理读取和写入的字符串内容。例如,从文件中读取一行内容到 string 对象:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::ifstream file("test.txt");
    std::string line;
    if (file.is_open()) {
        std::getline(file, line);
        std::cout << "Read line: " << line << std::endl;
        file.close();
    } else {
        std::cerr << "Could not open file" << std::endl;
    }
    return 0;
}

此代码从名为 test.txt 的文件中读取一行内容并存储到 string 对象 line 中。同样,也可以将 string 对象的内容写入文件:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::ofstream file("output.txt");
    std::string content = "This is some content to write";
    if (file.is_open()) {
        file << content << std::endl;
        file.close();
    } else {
        std::cerr << "Could not open file" << std::endl;
    }
    return 0;
}

这里将 string 对象 content 的内容写入名为 output.txt 的文件中。

通过以上详细的介绍和丰富的代码示例,希望你对 C++ 中 string 类的用法有了更深入全面的理解,能够在实际编程中灵活高效地使用 string 类来处理各种字符串相关的任务。无论是简单的文本处理,还是复杂的国际化应用开发,string 类都能提供强大而便捷的功能支持。