欢迎光临
我们一直在努力

C++字符串操作全解析:从基础到高级技巧

std::string 是 C++ 标准库中最常用的类之一,本质是 std::basic_string<char> 的别名。下面从基础用法、内部机制、常见陷阱到现代特性,逐步拆解。

一、头文件与基本定义

#include <string>

using std::string;
using std::wstring; // std::basic_string<wchar_t>
using std::u16string; // std::basic_string<char16_t>
using std::u32string; // std::basic_string<char32_t>

std::string 负责管理动态字符数组,自动处理内存分配与释放,并带有一个长度字段,不以 '\\0' 的数量计长。

二、构造与赋值

1. 常见构造方式

string s1; // 空字符串
string s2("hello"); // 从 C 风格字符串
string s3(s2); // 拷贝构造
string s4(s2, 2); // "llo",从位置2到末尾
string s5(s2, 1, 3); // "ell",从位置1,取3字符
string s6(5, 'x'); // "xxxxx"
string s7(s2.begin(), s2.end()-1); // 从迭代器范围,"hell"
string s8 = "world"; // 隐式转换

2. 赋值操作

s1 = s2; // 拷贝赋值
s1 = "abc"; // 从 C 字符串赋值
s1 = 'A'; // 单字符
s1.assign(s2, 1, 2); // 从 s2[1] 开始取 2 字符
s1.assign(3, 'B'); // "BBB"

注意:std::string 不像某些语言那样不可变,它是可变的字符序列。

三、容量与大小

s.size() // 字符数(O(1))
s.length() // 同 size()
s.capacity() // 已分配内存可容纳的字符数
s.max_size() // 理论上限
s.empty() // 是否为空
s.reserve(n) // 预留至少 n 个字符的内存,避免反复重分配
s.shrink_to_fit() // 请求释放多余容量(非强制)

  • size() 和 length() 语义相同,都是 char 的数量。

  • capacity() 通常大于等于 size(),多出的空间用于减少 push_back 等操作的重新分配。

四、元素访问

s[i] // 不检查越界
s.at(i) // 越界抛出 std::out_of_range
s.front() // s[0]
s.back() // s[size()-1]
s.data() // 返回内部字符数组指针(C++11 起包含 '\\0',可修改)
s.c_str() // 返回 const char*,保证以 '\\0' 结尾

  • data() 和 c_str() 返回相同指针。

  • 修改 data() 返回的缓冲区时,不能超出 size() 并需手动维护结尾 '\\0'(如果后续要作为 C 字符串使用)。

五、修改操作

1. 追加 / 插入 / 删除

s += " world"; // 拼接
s.append("!!!"); // 追加
s.push_back('!'); // 追加单字符
s.insert(0, "prefix"); // 在位置0插入
s.insert(0, 3, 'A'); // 插入3个'A'
s.erase(2, 4); // 从索引2删除4个字符
s.pop_back(); // 删除最后一个字符(C++11)
s.clear(); // 清空

2. 替换

s.replace(pos, len, "new"); // 替换区间 [pos, pos+len)
s.replace(it1, it2, "new"); // 替换迭代器区间

3. 比较

s1 == s2 // 内容相等
s1 != s2
s1 < s2 // 字典序
s1.compare(s2) // 返回 int(<0, 0, >0)
s1.compare(pos, len, s2)

六、查找

全部返回 std::string::npos (即 static const size_type npos = -1) 如果未找到。

s.find("sub") // 子串首次出现位置
s.rfind("sub") // 最后一次出现
s.find_first_of("aeiou") // 在 s 中找第一个属于 "aeiou" 的字符
s.find_last_of("aeiou")
s.find_first_not_of(" ") // 第一个不在参数中的字符
s.find_last_not_of(" ")

七、子串与转换

string sub = s.substr(2, 5); // 从索引2开始取5个字符
int n = stoi("123"); // 字符串转整数
long l = stol("123L");
float f = stof("3.14");
double d = stod("3.14");
string num = to_string(456); // 数值转字符串

八、迭代器

s.begin() // 指向第一个字符
s.end() // 尾后
s.rbegin() // 反向
s.rend()
for (char c : s) { … }
sort(s.begin(), s.end());
reverse(s.begin(), s.end());

九、常见陷阱

1.s[s.size()] 不是 '\\0':C++11 起 s[s.size()] 定义为 CharT()(即 '\\0'),但修改它会引发未定义行为,不要依赖它。

2.data() 和 c_str() 失效:任何非 const 成员函数调用后,返回的指针可能失效,需重新获取。

3.reserve 不改变 size:它只分配内存,若直接用下标访问需确保索引在 size 内,或使用 resize。

4.stoi 等转换异常:转换失败抛出 std::invalid_argument 或 std::out_of_range。

5.npos 是无符号:若 find 返回 npos,与有符号数比较前注意类型转换,勿写 int pos = s.find(…); if (pos < 0)。

十、string常用的函数

1. 构造与赋值

#include <string>
using namespace std;

// ① 默认构造:空字符串
string s1; // s1 == ""

// ② 从 C 风格字符串构造
string s2("hello"); // s2 == "hello"

// ③ 拷贝构造
string s3(s2); // s3 == "hello"

// ④ 重复字符构造:5 个字符 'a'
string s4(5, 'a'); // s4 == "aaaaa"

// ⑤ 赋值运算符
string s5;
s5 = "world"; // s5 == "world"
s5 = s2; // s5 == "hello"

// ⑥ assign —— 功能等价,但有细粒度控制
s5.assign(s2, 1, 2); // 从 s2[1] 开始取 2 个字符,s5 == "el"
s5.assign(3, 'X'); // s5 == "XXX"

2. 容量与信息

string s = "Hello";
// ① size() / length() —— 字符个数
s.size(); // 5
s.length(); // 5

// ② empty() —— 是否为空
s.empty(); // false
string e;
e.empty(); // true

// ③ capacity() —— 已分配空间大小
s.capacity(); // 至少 >= 5,具体值依赖实现

// ④ reserve(n) —— 预留内存,防止反复扩容
s.reserve(100); // capacity 变为 >= 100,size 仍为 5

// ⑤ clear() —— 清空内容,size 归零
s.clear(); // s == "",capacity 通常不变

3. 访问字符

string s = "abcdef";
// ① operator[] —— 不检查越界
char c1 = s[2]; // 'c'
s[0] = 'A'; // s 变为 "Abcdef"

// ② at() —— 越界抛 std::out_of_range
char c2 = s.at(2); // 'c'
// s.at(100); // 抛出异常

// ③ front() —— 第一个字符
char first = s.front(); // 'A'
s.front() = 'Z'; // s 变为 "Zbcdef"

// ④ back() —— 最后一个字符
char last = s.back(); // 'f'
s.back() = 'F'; // s 变为 "ZbcdeF"

// ⑤ c_str() —— 返回以 '\\0' 结尾的 const char*
const char* p = s.c_str(); // 可用于 printf("%s", p);
// 注意:一旦修改 s 或 s 析构,p 就失效

// ⑥ data() —— C++17 起可修改的内部缓冲区
char* pdata = s.data(); // C++17: 可修改
pdata[1] = 'Y'; // s 变为 "ZYcdeF"
// 修改需保证不越界,且保持 size 一致,若需要结尾 '\\0' 要自行维护

4. 修改字符串

string s = "Hello";
// ① operator+= —— 追加字符串/字符
s += " World"; // s == "Hello World"
s += '!'; // s == "Hello World!"

// ② append —— 与 += 类似,支持多种形式
s.append("!!!"); // s == "Hello World!!!!"
s.append(2, '?'); // s == "Hello World!!!!??"

// ③ push_back —— 尾部加单字符
s.push_back('~'); // s == "Hello World!!!!??~"

// ④ pop_back —— 删除尾部单字符 (C++11)
s.pop_back(); // 删除 '~',s == "Hello World!!!!??"

// ⑤ insert —— 在指定位置插入
s.insert(5, "Beautiful "); // 在索引5插入,s == "HelloBeautiful World!!!!??"
s.insert(0, 3, '>'); // 开头插入3个'>',s == ">>>HelloBeautiful World!!!!??"

// ⑥ erase —— 删除子串
s.erase(0, 3); // 删除前3个字符,s == "HelloBeautiful World!!!!??"
s.erase(s.begin(), s.begin()+5); // 删除前5个字符(迭代器)

// ⑦ replace —— 替换区间
s.replace(0, 5, "Hi"); // 将[0,5)替换为"Hi",s == "HiBeautiful World!!!!??"

// ⑧ swap —— 交换两个 string 的内容
string other = "swap";
s.swap(other); // s == "swap",other == "HiBeautiful World!!!!??"
swap(s, other); // 非成员函数版,效果相同

// ⑨ clear —— 清空
s.clear(); // s == ""

5. 查找与搜索

string s = "Hello World, Hello Universe";
string key = "Hello";
// ① find —— 子串首次出现的位置
size_t pos = s.find(key); // 0
pos = s.find("World"); // 6
pos = s.find("xyz"); // string::npos

// ② rfind —— 子串最后一次出现的位置
pos = s.rfind("Hello"); // 13

// ③ find_first_of —— 从 s 中找第一个属于字符集的字符
pos = s.find_first_of("aeiou"); // 1 (第一个元音 'e')

// ④ find_last_of —— 最后一个属于字符集的字符
pos = s.find_last_of("aeiou"); // 29 (最后一个 'e' 在 Universe 中)

// ⑤ find_first_not_of —— 第一个不属于字符集的字符
pos = s.find_first_not_of(" \\t"); // 0 (第一个不是空白)

// ⑥ find_last_not_of —— 最后一个不属于字符集的字符
pos = s.find_last_not_of(" "); // 28 (最后一个非空格是 'e')

// 判断是否包含子串的常用写法:
if (s.find("World") != string::npos) {
// 找到了
}

6. 子串与比较

string s = "Hello World";
// ① substr(pos, len) —— 提取子串
string sub = s.substr(0, 5); // "Hello"
sub = s.substr(6); // "World" (len 默认到结尾)
sub = s.substr(6, 2); // "Wo"

// ② compare —— 字典序比较 (返回 0, <0, >0)
int cmp = s.compare("Hello"); // >0 (World 部分导致更大)
cmp = s.compare(0, 5, "Hello"); // 0
cmp = s.compare(6, 5, "World"); // 0
// 日常推荐直接使用比较运算符

// ③ 比较运算符
bool eq = (s == "Hello World"); // true
bool ne = (s != "hi"); // true
bool lt = (s < "Hello World!"); // true (较短更小)
bool le = (s <= "Hello World"); // true
bool gt = (s > "Apple"); // true
bool ge = (s >= "Hello World"); // true

7. 数值与字符串转换(非成员函数)

// ① to_string —— 数值转字符串
string numStr = to_string(123); // "123"
string piStr = to_string(3.14159); // "3.141590"

// ② stoi —— 字符串转 int
int n = stoi("42"); // 42
n = stoi("0xFF", nullptr, 16); // 按16进制解析,n=255

// ③ stol —— 字符串转 long
long l = stol("1000000"); // 1000000

// ④ stod —— 字符串转 double
double d = stod("3.14"); // 3.14

// 其他变体:stoul, stoull, stof, stold 等用法一致
// 错误处理:若无法转换或超出范围会抛异常
try {
int x = stoi("not_a_number");
} catch (const invalid_argument& e) {
// 非数字
} catch (const out_of_range& e) {
// 数值越界
}

8. 流输入与字面量

#include <iostream>
#include <sstream> // istringstream
using namespace std;

// ① getline —— 读取一行(可指定分隔符)
string line;
getline(cin, line); // 从标准输入读一行,丢弃换行符
// 若从文件流也是相同用法
istringstream iss("apple,banana");
getline(iss, line, ','); // line == "apple"

// ② operator<< 和 operator>> —— 流输出/输入
string s = "Hello";
cout << s; // 输出 "Hello"
cin >> s; // 以空白分隔读取,不适合含空格的字符串

// ③ ""s 字面量 (C++14)
using namespace string_literals;
auto str = "I am a string"s; // 类型为 std::string,等价于 string("I am a string")

9. 迭代器

string s = "example";
// ① begin() / end() —— 正向迭代器
for (auto it = s.begin(); it != s.end(); ++it) {
*it = toupper(*it); // 转为大写
}
// s == "EXAMPLE"

// ② 基于范围的 for 循环
for (char& c : s) {
c = tolower(c); // 转为小写
}
// s == "example"

// 配合标准算法
#include <algorithm>
reverse(s.begin(), s.end()); // s == "elpmaxe"
sort(s.begin(), s.end()); // s == "aeelmpx" (按字符排序)

赞(0)
未经允许不得转载:171主机测评 » C++字符串操作全解析:从基础到高级技巧
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址