前言

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

目录

前言

(一)Modifiers(修改字符串内容的操作)

1.append/operator +=(重载为成员函数)

2.operator +(重载为全局函数)

3.push_back(尾插)

4.insert(可实现头插)

5.erase(删除)

6. replace(替换)

(二)String operations(字符串运算)

1.find / rfind

2.c_str

3.substr

4.find_first_of

5.find_last_of / find_not_first_of / find_not_last_of

(三)补充

1.getline与operator>>的区别

2.成员变量npos

小结


(一)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 源码打下坚实基础。

创作不易,如果你觉得有帮助,欢迎点赞、收藏、评论。我们下篇见!

更多推荐