【C++STL:string(中)】string进阶指南:增删改查、截取查找、字符定位全搞定
前言
在上一篇string(上)博文中,博主向大家介绍了string类的成员函数(默认构造、拷贝构造、赋值运算符重载),使用迭代器和operator [ ]遍历和修改字符串,并区分了 const_iterator 与普通迭代器的使用场景,另外还介绍了容量的相关接口:size、rsize、clear、empty、reserve。本篇博主将对string类常见接口作最后介绍,例如:对字符串进行增、删、查、改,以及一些字符串的运算接口,后续文章string(下)会带大家自己实现string类的接口,让大家对string类有更深刻的认识!前文参考:【C++STL:string(上)】:手把手教你使用官方文档高效学习string,并熟练使用string的常用接口-CSDN博客
目录
5.find_last_of / find_not_first_of / find_not_last_of
(一)Modifiers(修改字符串内容的操作)

1.append/operator +=(重载为成员函数)
append和operator += 功能几乎重合,我们在日常使用中为了方便,大多数时候都是使用 operator+=。
//append
void Test_String4()
{
string s1("hello world");
s1.append("every one");
cout << s1 << endl;
string s2 = "have a nice day";
s1.append(s2);
cout << s1 << endl;
string s3 = "xxxx";
s2.append(s3, 1, 3);
cout << s2 << endl;
}
运行结果:

void Test_String5()
{
string s1("hello world");
s1 += 'x';
cout << s1 << endl;
s1 += "yyy";
cout << s1 << endl;
string s2("zzzz");
s1 += s2;
cout << s1 << endl;
}
运行结果:

2.operator +(重载为全局函数)
operator +=重载为全局函数是莫大的智慧,下面我将提供两段代码,请读者先自己体会。
string s3("hello world");
s3 += ' ';
s3 += "hello bit";
cout << s3 << endl;
cout << s3 + "xxxx" << endl;
cout << "xxxx" + s3 << endl;
全局函数:允许任意一侧进行隐式类型转换,保证左右操作数完全对称。
成员函数:会强制左操作数必须是 string 类型,导致 "hello " + s 这样的常见表达式编译失败。
这正是 C++ 标准库将 std::string 的 operator+ 实现为非成员函数的设计原因,也是很多二元运算符(如 ==、< 等)被推荐为非成员函数的原则。
3.push_back(尾插)

代码演示
list<int> lit;
lit.push_back(1);
lit.push_back(2);
lit.push_back(3);
string::iterator ret = find(s1.begin(), s1.end(), 'x');
if (ret != s1.end())
{
cout << "找到x" << endl;
}
list<int>::iterator ret2 = find(lit.begin(), lit.end(), 2);
4.insert(可实现头插)
insert重载的参数列表和append极为相似。同样的,日常使用中尽量避免使用头插,因为insert的时间复杂度会明显提高。
5.erase(删除)

代码实现:
void test_string5()
{
string s1("hello world");
cout << s1 << endl;
s1.insert(0, "xxx");
cout << s1 << endl;
s1.insert(0, 1, '#');
cout << s1 << endl;
s1.insert(5, 1, '#');
cout << s1 << endl;
s1.insert(s1.begin(), '$');
cout << s1 << endl;
string s2("hello world");
cout << s2 << endl;
s2.erase(s2.begin()); // 头删
cout << s2 << endl;
s2.erase(0, 1); // 头删
cout << s2 << endl;
s2.erase(5, 2);
cout << s2 << endl;
s2.erase(5);
cout << s2 << endl << endl;
}
6. replace(替换)
replace(替换一部分)——这个接口设计得贼复杂,要小心,效率不高,少用,如果是少量数据用没什么大问题,要挪动数据,谨慎用。
假设有一个string类对象s1=“hello world”,要求将一个空格替换成“%%%”(三个%)
很容易实现:
string s3("hello world");
cout << s3 << endl;
s3.replace(5, 1, "%%%");
cout << s3 << endl;
如果这个字符串中有十几个空格,使用replace效率就太低了。这时我们以前介绍的范围for九耀派上用场了!
void Test_String2()
{
string s5("hello world hello everyone");
cout << s5 << endl;
string s6;
s6.reserve(s5.size());
for (auto ch : s5)
{
if (ch != ' ')
{
s6 += ch;
}
else
{
s6 += "%%%";
}
}
cout << s6 << endl;
}
(二)String operations(字符串运算)
1.find / rfind
find与 rfind重载的参数列表是一样的,使用方法也是一样的,不同点在于:
find是从前往后查找,rfind是从后往前查找。

上文在介绍replace时引入了一个题目:把字符串里的空格替换为“%%%”,这里可以先用find找到空格,再“%%%”替换掉空格。代码实现如下:
void Test_String3()
{
// 所有空格替换为%%
string s4("hello world hello");
cout << s4 << endl;
size_t pos = s4.find(' ');
while (pos != string::npos)
{
s4.replace(pos, 1, "%%%");
// 找下一个空格
pos = s4.find(' ', pos + 3);
}
cout << s4 << endl;
}
2.c_str
使用场景:c_str用于返回string对象的指针,C++有些接口的参数必须传const char*,此时需要调用c_str。如下图:

3.substr
使用场景:取字符串的一部分,并返回一个新的string对象。(一般和find、rfind搭配使用)

//取文件名后缀
void Test1()
{
string file("Test.tar.zip");
size_t pos = file.rfind('.');
if (pos != string::npos)
{
string suffix = file.substr(pos);
cout << suffix << endl;
}
}
4.find_first_of
接口说明:在当前字符串中查找任意一个与指定字符集合中字符匹配的字符,并返回第一次出现的位置。

将一个字符串里的‘a’、‘e’、‘i’、‘o’、‘u’全都替换为‘*’。
void Test2()
{
string str("Please, replace the vowels in this sentence by asterisks.");
size_t found = str.find_first_not_of("abcdefg");
while (found != string::npos)
{
str[found] = '*';
found = str.find_first_not_of("aeiou", found + 1);
}
cout << str << endl;
}
运行结果:

5.find_last_of / find_not_first_of / find_not_last_of
使用方法和 find_first_of 基本一致:
| 函数名 | 搜索方向 | 匹配条件 | 返回值 |
|---|---|---|---|
find_first_of | 从左向右 | 字符在指定的字符集合中 | 第一个匹配的位置(下标),未找到返回 npos |
find_last_of | 从右向左 | 字符在指定的字符集合中 | 最后一个匹配的位置(下标),未找到返回 npos |
find_first_not_of | 从左向右 | 字符不在指定的字符集合中 | 第一个不匹配的位置(下标),未找到返回 npos |
find_last_not_of | 从右向左 | 字符不在指定的字符集合中 | 最后一个不匹配的位置(下标),未找到返回 npos |
(三)补充
1.getline与operator>>的区别
>> 读字符串时碰到空格或换行就停,只能读一个词。
getline 专门读整行,碰到换行才停(默认),空格啥的都算内容,全收下。

2.成员变量npos

小结
至此,string的常用接口已基本梳理完毕。本文重点讲解了 Modifiers(修改) 和 String operations(字符串操作) 两大模块:
Modifiers:包括 append、operator+=、operator+、push_back、insert、erase、replace 等,掌握了如何增、删、改字符串中的内容。
String operations:涵盖 find/rfind、c_str、substr、find_first_of 及其 _last_of、_not_of 变体,可以灵活地在字符串中查找、截取、定位字符。
补充了如何区分了 getline 与 operator>> 的读取行为,并认识了成员变量 npos 的作用。
string 作为 STL 中最常用的容器之一,其接口设计体现了现代 C++ 的许多重要思想:RAII、迭代器、深拷贝与引用计数(某些实现)、异常安全等。仅仅学会使用是不够的,理解其底层实现才能写出更高效、更健壮的代码。因此,下一篇我们将从零开始手写一个简化版的 string 类,实现构造、析构、拷贝、赋值、常用修改及访问操作。通过亲自实现,你将彻底弄清 string 的内存管理、深拷贝与浅拷贝的区别、迭代器的底层封装等问题,为日后阅读 STL 源码打下坚实基础。
创作不易,如果你觉得有帮助,欢迎点赞、收藏、评论。我们下篇见!
更多推荐

所有评论(0)