admin管理员组

文章数量:1028592

STL string 实用攻略:打造优质 C++ 代码

引言:STL

C++ 标准模板库:STL 是 C++ 编程语言中的一个重要库,提供了一系列通用的模板类和函数,用于实现常见的数据结构和算法。

STL中的容器可以分为三类: 序列容器:

vector:动态数组,支持快速随机访问。 list:双向链表,支持快速插入和删除。 deque:双端队列,支持在两端快速插入和删除。 array:固定大小的数组(C++11 引入)。 forward_list:单向链表(C++11 引入)。

关联容器:

set:存储唯一元素的集合,按值排序。 map:存储键值对的集合,按键排序。 multiset:允许重复元素的集合,按值排序。 multimap:允许重复键的键值对集合,按键排序。

无序关联容器:

unordered_set:存储唯一元素的集合,基于哈希表。 unordered_map:存储键值对的集合,基于哈希表。 unordered_multiset:允许重复元素的集合,基于哈希表。 unordered_multimap:允许重复键的键值对集合,基于哈希表。

特殊的容器适配器:

stack:后进先出(LIFO)的栈。 queue:先进先出(FIFO)的队列。 priority_queue:优先级队列,元素按优先级排序。(默认小堆)。

1.0.string

1.1.c语言字符串的局限性

char数组的局限性,如固定大小、手动管理内存等。 1.1.1固定大小 char数组在声明时需要指定固定大小,且大小在编译时确定。如果字符串长度超过数组大小,会导致缓冲区溢出,引发未定义行为或安全问题。

代码语言:javascript代码运行次数:0运行复制
char str[10] = "Hello"; // 只能容纳最多9个字符 + '\0'

1.1.2手动分配内存 char数组需要开发者手动分配和释放内存,尤其是在动态分配时(如使用malloc和free)忘记释放内存会导致内存泄漏,而重复释放则可能引发程序崩溃。

代码语言:javascript代码运行次数:0运行复制
char *str = (char *)malloc(20 * sizeof(char));
strcpy(str, "Dynamic memory");
free(str); // 必须手动释放

1.1.3手动扩容 char数组的大小是固定的,无法动态扩展。如果需要存储更长的字符串,必须手动重新分配内存(如使用realloc),这增加了代码的复杂性。

代码语言:javascript代码运行次数:0运行复制
char *str = (char *)malloc(10 * sizeof(char));
str = (char *)realloc(str, 20 * sizeof(char)); // 手动扩展

这时候C++就有了string,string容器可以解决上面的各种问题。

2.0.string

string为我们提供了很多的接口。

2.1.迭代器

STL给容器提供了迭代器(iterator),使得开发者可以像操作容器一样遍历和操作字符串。迭代器是一种抽象的概念,它提供了对字符串中字符的访问方式,类似于指针的行为(但并不是所有的容器都是指针)。 2.1.1.begin()和end()函数

在 string 中,begin()end() 函数是获取迭代器的基本方式。begin() 函数返回一个指向字符串第一个字符的迭代器,而 end() 函数返回的迭代器则指向字符串末尾的下一个位置(在 C 风格字符串中对应于 ‘\0’ 的位置,但在 string 中并不实际存储 ‘\0’)。 使用:

代码语言:javascript代码运行次数:0运行复制
int main() {
   string s = "hello";
   // 获取字符串的起始迭代器
   string::iterator it = s.begin();
   // 使用 for 循环通过迭代器遍历字符串
    for (it = s.begin(); it != s.end(); it++) 
    {
       cout << *it;
    }
    cout << std::endl;

    // 使用 while 循环通过迭代器遍历字符串
    it = s.begin();
    while (it != s.end())
    {
        cout << *it;
        it++;
    }
    cout << std::endl;
    return 0;
}

2.1.2.rbegin()和end() 除了正向遍历,string 还提供了反向遍历的方式,即通过 rbegin() rend()函数。rbegin() 函数返回一个指向字符串最后一个字符的反向迭代器,而 rend() 函数返回的反向迭代器则指向字符串第一个字符的前一个位置。

代码语言:javascript代码运行次数:0运行复制
int main() {
    string s = "hello";
    string::reverse_iterator it = s.rbegin();
    // 使用 for 循环通过迭代器反向遍历字符串
    for (it = s.rbegin(); it != s.rend(); it++)
    {
        cout << *it;
    }
    cout << endl;

    // 使用 while 循环通过迭代反向器遍历字符串
    it = s.rbegin();
    while (it != s.rend())
    {
        cout << *it;
        it++;
    }
    cout << endl;
    return 0;
}

2.2.string的构造函数和析构函数

2.2.1.构造函数

1. 默认构造函数:构造一个为空的字符串。 2. 拷贝构造函数:拷贝一个str的副本。 3. 子字符串构造函数:创建一个新字符串,该字符串是现有字符串(str)的子字符串,从位置pos开始,长度为len。如果未指定len,则默认为npos,通常表示“直到字符串的末尾”。 4. 从C字符串构造函数:用C-string来构造string类对象 5. 从序列构造函数 :字符数组(s)的前n个字符创建一个新字符串。 6. 填充构造函数:创建一个包含n个字符c的新字符串。 7. 范围构造函数:从由迭代器first和last定义的字符范围创建一个新字符串。

代码语言:javascript代码运行次数:0运行复制
#include<string> //需要包含头文件才可以使用string
int main()
{
	string s("hello world!");
	//string s= "hello world!";
	const char* cstr = "Hello, C++!";

	string str1;  // 使用默认构造函数创建一个空字符串
	string str2(s);// 使用拷贝构造函数
	string str3(s,6,6);// 从位置6开始,长度为6的子字符串
	string str4(cstr);// 从C字符串创建
	string str5(cstr, 5);  // 从C字符串的前5个字符创建
	string str6(10, 'x');  // 创建包含10个'x'字符的字符串
	string str7(str6.begin(), str6.end());  // 从string str6的范围创建字符串


	cout << "str1: \"" << str1 << "\"" << endl;
	cout << "str2: \"" << str2 << "\"" << endl;
	cout << "str3: \"" << str3 << "\"" << endl;
	cout << "str4: \"" << str4 << "\"" << endl;
	cout << "str5: \"" << str5 << "\"" << endl;
	cout << "str6: \"" << str6 << "\"" << endl;
	cout << "str7: \"" << str7 << "\"" << endl;
	return 0;
}

2.2.2析构函数

当调用结束后,sting会自动调用~string析构函数

2.3.string类对象的容量操作

下面是一些容量接口

函数名

功能描述

size

返回字符串的长度

length

返回字符串的长度

max_size

返回字符串的最大尺寸

resize

调整字符串的大小

capacity

返回已分配存储的大小

clear

清空字符串

empty

测试字符串是否为空

shrink_to_fit(C++11)

收缩以匹配实际使用的大小

代码语言:javascript代码运行次数:0运行复制
int main()
{
	string s("Hello World");

	cout << s.size() << endl;//字符串长度
	cout << s.length() << endl;//字符串长度
	cout << s.capacity() << endl;//空间大小
	cout << s.max_size() << endl;//最大容量

	return 0;
}

2.3.1.string的扩容函数capacity

capacity在不同的编译器下扩容方式不同。

代码语言:javascript代码运行次数:0运行复制
int main()
{
	string s;
	size_t sz = s.capacity();//默认开十六
	cout << "Initial capacity: " << sz << '\n';

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
	return 0;
}

在vs2022下。

可以看到,初始容量为15,加上终止符\0,实际分配了16个字符的空间。,当字符串长度超过当前容量时,容量会按一定比例增加。观察到的容量变化是:15 -> 31 -> 47 -> 70 -> 105。这些变化表明,容量是按大约1.5倍的比例增长的。

在Linux的g++下。

因编译器的不同,Linux下是呈指数增长。

扩容的两种方式 第一种:n > capacity 第二种:n < capacity 在不同的编译器下还是不一样的。

代码语言:javascript代码运行次数:0运行复制
int main()
{
	string s("Hello world! Hello C++");
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;

	s.reserve(28);//n > capacity
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;

	s.reserve(40);//n < capacity
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;

	s.clear();
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;
	return 0;
}

clear() 只会将字符串的长度 size() 设置为 0,不会改变 capacity()。

vs2022下。

当s.size() < capacity时,进行1.5倍扩容。 当s.size() > capacity时,不会进行任何操作。

在Linux下。

当s.size() < capacity时,进行两倍扩容。 当s.size() > capacity时,会进行缩容,但不会影响字符串。

2.3.2reserve和resize 虽然reserve(),resize()都有扩容的意思,但整体的使用差别还是有点打的。

resize resize的核心功能是改变字符串的实际长度。当新长度比原长度大时,它会在字符串末尾填充指定字符(默认是’\0’);若新长度小于原长度,字符串就会被截断。 reserve reserve主要负责为字符串预先分配内存空间,设定字符串的最小容量。它不会改变字符串的长度和内容,只是给后续添加字符预留足够空间,减少内存重新分配的开销。

代码语言:javascript代码运行次数:0运行复制
void StringTest12()
{
	 string str = "example";
	// reserve操作
	 cout << "reserve操作前:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;
	str.reserve(15);
	 cout << "reserve操作后:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;

	// resize操作
	 cout << "\nresize操作前:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;
	str.resize(10, '!');
	 cout << "resize操作后:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;
}

resize 能改变 std::string 实际长度,内容也会随之变动 。比如变长就补字符,变短则截断。而 reserve 主要用来规划容量,优化内存,不影响字符串内容和长度。

2.4.string访问操作

函数名

功能描述

operator

获取字符串中的字符

at

获取字符串中的字符

back

访问字符串的最后一个字符

front

访问字符串的第一个字符

2.4.1.operator[]和at

这两个函数都是返回pos位置的字符,类型是const char&,这也方便了修改。

代码语言:javascript代码运行次数:0运行复制
void StringTest1()
{
	string s("Hello World!");
	for (int i = 0; i < s.size(); i++)
	{
		cout << s[i] << " ";
		//s[i] = 'a';可以进行修改
	}
	cout << endl;
	for (int i = 0; i < s.size(); i++)
	{
		cout << s.at(i) << " ";
		//s.at(i);可以进行修改
	}
}

输出结果: H e l l o W o r l d ! H e l l o W o r l d !

operator[]和at函数几乎一模一样,唯一的不同点就是,at()函数会进行边界检查

2.4.2.back和front

back放回字符串的最后一个字符,front放回字符串的第一个字符。

代码语言:javascript代码运行次数:0运行复制
void  StringTest2()
{
	string s("Hello World");
	cout << s.front() << " ";
	cout << s.back() << " ";
}

输出结果: H d

2.5.string的修改操作

函数名

功能描述

operator+=

向字符串追加内容

append

向字符串追加内容

push_back

向字符串追加一个字符

insert

向字符串中插入内容

assign

为字符串分配内容

swap

交换两个字符串的值

copy

从字符串中复制字符序列

replace

替换字符串的一部分

erase

从字符串中删除字符

pop_back(C++11)

删除字符串的最后一个字符

2.5.1.operator+=

代码语言:javascript代码运行次数:0运行复制
void  StringTest3()
{
	string s1("This");
	string s2("is");
	s1 += s2; //追加一个string类型的字符串
	s1 += "cpp.exe";//追加C风格字符串
	s1 += '!';//追加一个字符
}

operator+= 是string 中用于追加内容的核心操作符,支持追加字符串C 风格字符串和单个字符

2.5.2.append和push_back

代码语言:javascript代码运行次数:0运行复制
void StringTest4()
{
	string s;
	s.append("Hello");          // "Hello"
	s.append(", ");             // "Hello, "
	s.append("World");          // "Hello, World"
	s.append(1, '!');           // "Hello, World!"
	s.append(s);
	s.append(" Have a nice day!"); // "Hello, World!Hello, World! Have a nice day!"
	s.push_back('!');     //尾插一个字符!
}

insert

代码语言:javascript代码运行次数:0运行复制
void StringTest7()
{
	string str = "Hello, World!";
	string str1 = "C++ ";
	string str2 = "12345";
	str.insert(7, str2);  // 在位置 7 插入 "C++ " 
	str.insert(11, "haha", 0, 5);  // 在位置 11 插入 "haha"
	str.insert(16, " good!");  // 在位置 16 插入 "good"
	str.insert(16, "one", 8);  // 在位置 16 插入 "one "
	str.insert(24, 3, '!');  // 在位置 24 插入 3 个 '!'
	str.insert(str.begin() + 5, '!');  // 在位置 5 插入 '!'
	str.insert(str.begin() + 10, str2.begin(), str2.end());
	cout << str << endl;
}

2.5.3.assign和replace

代码语言:javascript代码运行次数:0运行复制
void StringTest8()
{
	string str = "Hello, World!";

	// 使用 assign 赋值
	str.assign("C++ Programming");  // 用 C 风格字符串赋值
	cout << "After assign: " << str << endl;  // 输出 "C++ Programming"

	// 使用 replace 替换
	str.replace(4, 11, "is fun!");  // 从位置 4 开始,替换 11 个字符为 "is fun!"
	cout << "After replace: " << str << endl;  // 输出 "C++ is fun!"

	// 再次使用 assign 赋值
	str.assign(10, '*');  // 用 10 个 '*' 赋值
	cout << "After assign with fill: " << str << endl;  // 输出 "**********"

	// 再次使用 replace 替换
	str.replace(5, 2, "C++");  // 从位置 5 开始,替换 2 个字符为 "C++"
	cout << "After replace: " << str << endl;  // 输出 "*****C++**"
}

2.5.4.swap和copy

代码语言:javascript代码运行次数:0运行复制
void StringTest5()
{
	string s1("Hello World!");
	string s2("Hello Shawn!");
	cout << "交换前:" << endl;
	cout << "s1:" << s1 << endl;
	cout << "s2:" << s2 << endl;

	s1.swap(s2);
	cout << "交换后:" << endl;
	cout <<"s1:" << s1 << endl;
	cout << "s2:" << s2 << endl;

	// 目标字符数组
	char buffer[20];
	// 从s2的第 6 个字符开始,复制 5 个字符到 buffer
	size_t sz = s2.copy(buffer, 5, 6);
	buffer[sz] = '\0';

	cout <<  buffer << endl;
}

string里的swap算法库里的swap交换逻辑是不一样的,库里面的是一个值一个值的进行交换,string::swap 是成员函数,直接交换两个字符串的内部指针数据,效率非常高(常数时间复杂度)。

2.5.5.erase和pop_back

代码语言:javascript代码运行次数:0运行复制
void StringTest6()
{
	string s ("Hello, World!");
	// 序列删除,删除"World",结果为"Hello, !"。
	s.erase(7, 5);
	// 删除单个字符,删除',',结果为"Hello 
	s.erase(s.begin() + 5);
	// 删除字符范围,删除' '后的所有字符,结果为"Hello"
	s.erase(s.begin() + 5, s.end());
	s.pop_back();//尾删
}

2.6.string的其他操作

函数名

功能描述

find

在字符串中查找内容

rfind

在字符串中查找内容

find_first_of

在字符串中查找指定字符

find_last_of

从字符串末尾开始查找指定字符

find_first_not_of

在字符串中查找不存在的指定字符

find_last_not_of

从字符串末尾开始查找不匹配的字符

substr

生成子字符串

compare

比较字符串

2.6.1.find和rfind

代码语言:javascript代码运行次数:0运行复制
void StringTest8()
{
	string s1("Hello world!");
	string s2("ll");
	size_t a = s1.find(s1,0);//找单个字符,不传默认从0开始找。
	a = s1.find("Hello");//找字符串,不传默认从0开始找
	a = s1.find("world!", 4, 6);//找字符串,从4开始找,最多6个字符
	a = s1.find('H', 0);//找单个字符,不传默认从0开始找。
}

findrfind 的功能非常相似,主要区别在于查找方向:find 从字符串开头向末尾查找(从左到右),而 rfind 从字符串末尾向开头查找(从右到左。如果找到目标,它们返回匹配的子字符串或字符的起始位置;如果未找到,则返回 string::npos。find 返回第一次匹配的位置,而 rfind 返回最后一次匹配的位置。这两个函数非常适合用于查找子字符串或字符的位置。

2.6.2.find_first_not_of、find_first_of、find_last_not_of和find_last_of 下面这四个函数用法几乎一样,只解释一种。

代码语言:javascript代码运行次数:0运行复制
void StringTest10()
{
	string str("Please, replace the vowels in this sentence by asterisks.");
	cout << str << '\n';

	size_t found = str.find_first_not_of("abcdef");
	while (found != string::npos)
	{
		str[found] = '*';
		found = str.find_first_not_of("abcdef", found + 1);
	}
	cout << str << '\n';
}

find_first_not_of放回不是子字符串中的字符,而find_first_of则是相反的,放回第一个是子字符串的第一个位置。而find_last_not_of是从右到左。

2.6.3.substr

代码语言:javascript代码运行次数:0运行复制
void StringTest11()
{	
	string s("hello world!");
	size_t pos = s.find('w');
	//从下标6开始截取长度为6的字符串
	string str = s.substr(pos, 6);
	cout << str << endl;
}

3.0.auto和范围for

auto:是c++11重新引入的,用来自动识别变量的类型。 而范围for是一种新的东西

代码语言:javascript代码运行次数:0运行复制
语法:
for (auto element : container) {
    // 对element进行操作
}

这个范围的for循环遍历string的底层原理是基于迭代器实现的。 当使用基于范围的for循环遍历string时,编译器会将其转换为等价的传统for循环,并在幕后调用string类的begin()和end()成员函数。begin()函数返回一个指向string中第一个字符的迭代器,end()函数返回一个指向字符串末尾字符下一个位置的迭代器,这个迭代器通常被称为 “超出末端迭代器”,用于标记字符串的结束位置。

代码语言:javascript代码运行次数:0运行复制
void StringTest13()
{
	string s("Hello World!");
	for (auto x : s)
	{
		cout << x;
	}
	cout << endl;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-24,如有侵权请联系 cloudcommunity@tencent 删除stlstring函数字符串c++

STL string 实用攻略:打造优质 C++ 代码

引言:STL

C++ 标准模板库:STL 是 C++ 编程语言中的一个重要库,提供了一系列通用的模板类和函数,用于实现常见的数据结构和算法。

STL中的容器可以分为三类: 序列容器:

vector:动态数组,支持快速随机访问。 list:双向链表,支持快速插入和删除。 deque:双端队列,支持在两端快速插入和删除。 array:固定大小的数组(C++11 引入)。 forward_list:单向链表(C++11 引入)。

关联容器:

set:存储唯一元素的集合,按值排序。 map:存储键值对的集合,按键排序。 multiset:允许重复元素的集合,按值排序。 multimap:允许重复键的键值对集合,按键排序。

无序关联容器:

unordered_set:存储唯一元素的集合,基于哈希表。 unordered_map:存储键值对的集合,基于哈希表。 unordered_multiset:允许重复元素的集合,基于哈希表。 unordered_multimap:允许重复键的键值对集合,基于哈希表。

特殊的容器适配器:

stack:后进先出(LIFO)的栈。 queue:先进先出(FIFO)的队列。 priority_queue:优先级队列,元素按优先级排序。(默认小堆)。

1.0.string

1.1.c语言字符串的局限性

char数组的局限性,如固定大小、手动管理内存等。 1.1.1固定大小 char数组在声明时需要指定固定大小,且大小在编译时确定。如果字符串长度超过数组大小,会导致缓冲区溢出,引发未定义行为或安全问题。

代码语言:javascript代码运行次数:0运行复制
char str[10] = "Hello"; // 只能容纳最多9个字符 + '\0'

1.1.2手动分配内存 char数组需要开发者手动分配和释放内存,尤其是在动态分配时(如使用malloc和free)忘记释放内存会导致内存泄漏,而重复释放则可能引发程序崩溃。

代码语言:javascript代码运行次数:0运行复制
char *str = (char *)malloc(20 * sizeof(char));
strcpy(str, "Dynamic memory");
free(str); // 必须手动释放

1.1.3手动扩容 char数组的大小是固定的,无法动态扩展。如果需要存储更长的字符串,必须手动重新分配内存(如使用realloc),这增加了代码的复杂性。

代码语言:javascript代码运行次数:0运行复制
char *str = (char *)malloc(10 * sizeof(char));
str = (char *)realloc(str, 20 * sizeof(char)); // 手动扩展

这时候C++就有了string,string容器可以解决上面的各种问题。

2.0.string

string为我们提供了很多的接口。

2.1.迭代器

STL给容器提供了迭代器(iterator),使得开发者可以像操作容器一样遍历和操作字符串。迭代器是一种抽象的概念,它提供了对字符串中字符的访问方式,类似于指针的行为(但并不是所有的容器都是指针)。 2.1.1.begin()和end()函数

在 string 中,begin()end() 函数是获取迭代器的基本方式。begin() 函数返回一个指向字符串第一个字符的迭代器,而 end() 函数返回的迭代器则指向字符串末尾的下一个位置(在 C 风格字符串中对应于 ‘\0’ 的位置,但在 string 中并不实际存储 ‘\0’)。 使用:

代码语言:javascript代码运行次数:0运行复制
int main() {
   string s = "hello";
   // 获取字符串的起始迭代器
   string::iterator it = s.begin();
   // 使用 for 循环通过迭代器遍历字符串
    for (it = s.begin(); it != s.end(); it++) 
    {
       cout << *it;
    }
    cout << std::endl;

    // 使用 while 循环通过迭代器遍历字符串
    it = s.begin();
    while (it != s.end())
    {
        cout << *it;
        it++;
    }
    cout << std::endl;
    return 0;
}

2.1.2.rbegin()和end() 除了正向遍历,string 还提供了反向遍历的方式,即通过 rbegin() rend()函数。rbegin() 函数返回一个指向字符串最后一个字符的反向迭代器,而 rend() 函数返回的反向迭代器则指向字符串第一个字符的前一个位置。

代码语言:javascript代码运行次数:0运行复制
int main() {
    string s = "hello";
    string::reverse_iterator it = s.rbegin();
    // 使用 for 循环通过迭代器反向遍历字符串
    for (it = s.rbegin(); it != s.rend(); it++)
    {
        cout << *it;
    }
    cout << endl;

    // 使用 while 循环通过迭代反向器遍历字符串
    it = s.rbegin();
    while (it != s.rend())
    {
        cout << *it;
        it++;
    }
    cout << endl;
    return 0;
}

2.2.string的构造函数和析构函数

2.2.1.构造函数

1. 默认构造函数:构造一个为空的字符串。 2. 拷贝构造函数:拷贝一个str的副本。 3. 子字符串构造函数:创建一个新字符串,该字符串是现有字符串(str)的子字符串,从位置pos开始,长度为len。如果未指定len,则默认为npos,通常表示“直到字符串的末尾”。 4. 从C字符串构造函数:用C-string来构造string类对象 5. 从序列构造函数 :字符数组(s)的前n个字符创建一个新字符串。 6. 填充构造函数:创建一个包含n个字符c的新字符串。 7. 范围构造函数:从由迭代器first和last定义的字符范围创建一个新字符串。

代码语言:javascript代码运行次数:0运行复制
#include<string> //需要包含头文件才可以使用string
int main()
{
	string s("hello world!");
	//string s= "hello world!";
	const char* cstr = "Hello, C++!";

	string str1;  // 使用默认构造函数创建一个空字符串
	string str2(s);// 使用拷贝构造函数
	string str3(s,6,6);// 从位置6开始,长度为6的子字符串
	string str4(cstr);// 从C字符串创建
	string str5(cstr, 5);  // 从C字符串的前5个字符创建
	string str6(10, 'x');  // 创建包含10个'x'字符的字符串
	string str7(str6.begin(), str6.end());  // 从string str6的范围创建字符串


	cout << "str1: \"" << str1 << "\"" << endl;
	cout << "str2: \"" << str2 << "\"" << endl;
	cout << "str3: \"" << str3 << "\"" << endl;
	cout << "str4: \"" << str4 << "\"" << endl;
	cout << "str5: \"" << str5 << "\"" << endl;
	cout << "str6: \"" << str6 << "\"" << endl;
	cout << "str7: \"" << str7 << "\"" << endl;
	return 0;
}

2.2.2析构函数

当调用结束后,sting会自动调用~string析构函数

2.3.string类对象的容量操作

下面是一些容量接口

函数名

功能描述

size

返回字符串的长度

length

返回字符串的长度

max_size

返回字符串的最大尺寸

resize

调整字符串的大小

capacity

返回已分配存储的大小

clear

清空字符串

empty

测试字符串是否为空

shrink_to_fit(C++11)

收缩以匹配实际使用的大小

代码语言:javascript代码运行次数:0运行复制
int main()
{
	string s("Hello World");

	cout << s.size() << endl;//字符串长度
	cout << s.length() << endl;//字符串长度
	cout << s.capacity() << endl;//空间大小
	cout << s.max_size() << endl;//最大容量

	return 0;
}

2.3.1.string的扩容函数capacity

capacity在不同的编译器下扩容方式不同。

代码语言:javascript代码运行次数:0运行复制
int main()
{
	string s;
	size_t sz = s.capacity();//默认开十六
	cout << "Initial capacity: " << sz << '\n';

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
	return 0;
}

在vs2022下。

可以看到,初始容量为15,加上终止符\0,实际分配了16个字符的空间。,当字符串长度超过当前容量时,容量会按一定比例增加。观察到的容量变化是:15 -> 31 -> 47 -> 70 -> 105。这些变化表明,容量是按大约1.5倍的比例增长的。

在Linux的g++下。

因编译器的不同,Linux下是呈指数增长。

扩容的两种方式 第一种:n > capacity 第二种:n < capacity 在不同的编译器下还是不一样的。

代码语言:javascript代码运行次数:0运行复制
int main()
{
	string s("Hello world! Hello C++");
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;

	s.reserve(28);//n > capacity
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;

	s.reserve(40);//n < capacity
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;

	s.clear();
	cout << s.size() << endl;
	cout << s.capacity() << endl << endl;
	return 0;
}

clear() 只会将字符串的长度 size() 设置为 0,不会改变 capacity()。

vs2022下。

当s.size() < capacity时,进行1.5倍扩容。 当s.size() > capacity时,不会进行任何操作。

在Linux下。

当s.size() < capacity时,进行两倍扩容。 当s.size() > capacity时,会进行缩容,但不会影响字符串。

2.3.2reserve和resize 虽然reserve(),resize()都有扩容的意思,但整体的使用差别还是有点打的。

resize resize的核心功能是改变字符串的实际长度。当新长度比原长度大时,它会在字符串末尾填充指定字符(默认是’\0’);若新长度小于原长度,字符串就会被截断。 reserve reserve主要负责为字符串预先分配内存空间,设定字符串的最小容量。它不会改变字符串的长度和内容,只是给后续添加字符预留足够空间,减少内存重新分配的开销。

代码语言:javascript代码运行次数:0运行复制
void StringTest12()
{
	 string str = "example";
	// reserve操作
	 cout << "reserve操作前:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;
	str.reserve(15);
	 cout << "reserve操作后:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;

	// resize操作
	 cout << "\nresize操作前:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;
	str.resize(10, '!');
	 cout << "resize操作后:" <<  endl;
	 cout << "字符串内容: " << str <<  endl;
	 cout << "字符串长度: " << str.size() <<  endl;
	 cout << "字符串容量: " << str.capacity() <<  endl;
}

resize 能改变 std::string 实际长度,内容也会随之变动 。比如变长就补字符,变短则截断。而 reserve 主要用来规划容量,优化内存,不影响字符串内容和长度。

2.4.string访问操作

函数名

功能描述

operator

获取字符串中的字符

at

获取字符串中的字符

back

访问字符串的最后一个字符

front

访问字符串的第一个字符

2.4.1.operator[]和at

这两个函数都是返回pos位置的字符,类型是const char&,这也方便了修改。

代码语言:javascript代码运行次数:0运行复制
void StringTest1()
{
	string s("Hello World!");
	for (int i = 0; i < s.size(); i++)
	{
		cout << s[i] << " ";
		//s[i] = 'a';可以进行修改
	}
	cout << endl;
	for (int i = 0; i < s.size(); i++)
	{
		cout << s.at(i) << " ";
		//s.at(i);可以进行修改
	}
}

输出结果: H e l l o W o r l d ! H e l l o W o r l d !

operator[]和at函数几乎一模一样,唯一的不同点就是,at()函数会进行边界检查

2.4.2.back和front

back放回字符串的最后一个字符,front放回字符串的第一个字符。

代码语言:javascript代码运行次数:0运行复制
void  StringTest2()
{
	string s("Hello World");
	cout << s.front() << " ";
	cout << s.back() << " ";
}

输出结果: H d

2.5.string的修改操作

函数名

功能描述

operator+=

向字符串追加内容

append

向字符串追加内容

push_back

向字符串追加一个字符

insert

向字符串中插入内容

assign

为字符串分配内容

swap

交换两个字符串的值

copy

从字符串中复制字符序列

replace

替换字符串的一部分

erase

从字符串中删除字符

pop_back(C++11)

删除字符串的最后一个字符

2.5.1.operator+=

代码语言:javascript代码运行次数:0运行复制
void  StringTest3()
{
	string s1("This");
	string s2("is");
	s1 += s2; //追加一个string类型的字符串
	s1 += "cpp.exe";//追加C风格字符串
	s1 += '!';//追加一个字符
}

operator+= 是string 中用于追加内容的核心操作符,支持追加字符串C 风格字符串和单个字符

2.5.2.append和push_back

代码语言:javascript代码运行次数:0运行复制
void StringTest4()
{
	string s;
	s.append("Hello");          // "Hello"
	s.append(", ");             // "Hello, "
	s.append("World");          // "Hello, World"
	s.append(1, '!');           // "Hello, World!"
	s.append(s);
	s.append(" Have a nice day!"); // "Hello, World!Hello, World! Have a nice day!"
	s.push_back('!');     //尾插一个字符!
}

insert

代码语言:javascript代码运行次数:0运行复制
void StringTest7()
{
	string str = "Hello, World!";
	string str1 = "C++ ";
	string str2 = "12345";
	str.insert(7, str2);  // 在位置 7 插入 "C++ " 
	str.insert(11, "haha", 0, 5);  // 在位置 11 插入 "haha"
	str.insert(16, " good!");  // 在位置 16 插入 "good"
	str.insert(16, "one", 8);  // 在位置 16 插入 "one "
	str.insert(24, 3, '!');  // 在位置 24 插入 3 个 '!'
	str.insert(str.begin() + 5, '!');  // 在位置 5 插入 '!'
	str.insert(str.begin() + 10, str2.begin(), str2.end());
	cout << str << endl;
}

2.5.3.assign和replace

代码语言:javascript代码运行次数:0运行复制
void StringTest8()
{
	string str = "Hello, World!";

	// 使用 assign 赋值
	str.assign("C++ Programming");  // 用 C 风格字符串赋值
	cout << "After assign: " << str << endl;  // 输出 "C++ Programming"

	// 使用 replace 替换
	str.replace(4, 11, "is fun!");  // 从位置 4 开始,替换 11 个字符为 "is fun!"
	cout << "After replace: " << str << endl;  // 输出 "C++ is fun!"

	// 再次使用 assign 赋值
	str.assign(10, '*');  // 用 10 个 '*' 赋值
	cout << "After assign with fill: " << str << endl;  // 输出 "**********"

	// 再次使用 replace 替换
	str.replace(5, 2, "C++");  // 从位置 5 开始,替换 2 个字符为 "C++"
	cout << "After replace: " << str << endl;  // 输出 "*****C++**"
}

2.5.4.swap和copy

代码语言:javascript代码运行次数:0运行复制
void StringTest5()
{
	string s1("Hello World!");
	string s2("Hello Shawn!");
	cout << "交换前:" << endl;
	cout << "s1:" << s1 << endl;
	cout << "s2:" << s2 << endl;

	s1.swap(s2);
	cout << "交换后:" << endl;
	cout <<"s1:" << s1 << endl;
	cout << "s2:" << s2 << endl;

	// 目标字符数组
	char buffer[20];
	// 从s2的第 6 个字符开始,复制 5 个字符到 buffer
	size_t sz = s2.copy(buffer, 5, 6);
	buffer[sz] = '\0';

	cout <<  buffer << endl;
}

string里的swap算法库里的swap交换逻辑是不一样的,库里面的是一个值一个值的进行交换,string::swap 是成员函数,直接交换两个字符串的内部指针数据,效率非常高(常数时间复杂度)。

2.5.5.erase和pop_back

代码语言:javascript代码运行次数:0运行复制
void StringTest6()
{
	string s ("Hello, World!");
	// 序列删除,删除"World",结果为"Hello, !"。
	s.erase(7, 5);
	// 删除单个字符,删除',',结果为"Hello 
	s.erase(s.begin() + 5);
	// 删除字符范围,删除' '后的所有字符,结果为"Hello"
	s.erase(s.begin() + 5, s.end());
	s.pop_back();//尾删
}

2.6.string的其他操作

函数名

功能描述

find

在字符串中查找内容

rfind

在字符串中查找内容

find_first_of

在字符串中查找指定字符

find_last_of

从字符串末尾开始查找指定字符

find_first_not_of

在字符串中查找不存在的指定字符

find_last_not_of

从字符串末尾开始查找不匹配的字符

substr

生成子字符串

compare

比较字符串

2.6.1.find和rfind

代码语言:javascript代码运行次数:0运行复制
void StringTest8()
{
	string s1("Hello world!");
	string s2("ll");
	size_t a = s1.find(s1,0);//找单个字符,不传默认从0开始找。
	a = s1.find("Hello");//找字符串,不传默认从0开始找
	a = s1.find("world!", 4, 6);//找字符串,从4开始找,最多6个字符
	a = s1.find('H', 0);//找单个字符,不传默认从0开始找。
}

findrfind 的功能非常相似,主要区别在于查找方向:find 从字符串开头向末尾查找(从左到右),而 rfind 从字符串末尾向开头查找(从右到左。如果找到目标,它们返回匹配的子字符串或字符的起始位置;如果未找到,则返回 string::npos。find 返回第一次匹配的位置,而 rfind 返回最后一次匹配的位置。这两个函数非常适合用于查找子字符串或字符的位置。

2.6.2.find_first_not_of、find_first_of、find_last_not_of和find_last_of 下面这四个函数用法几乎一样,只解释一种。

代码语言:javascript代码运行次数:0运行复制
void StringTest10()
{
	string str("Please, replace the vowels in this sentence by asterisks.");
	cout << str << '\n';

	size_t found = str.find_first_not_of("abcdef");
	while (found != string::npos)
	{
		str[found] = '*';
		found = str.find_first_not_of("abcdef", found + 1);
	}
	cout << str << '\n';
}

find_first_not_of放回不是子字符串中的字符,而find_first_of则是相反的,放回第一个是子字符串的第一个位置。而find_last_not_of是从右到左。

2.6.3.substr

代码语言:javascript代码运行次数:0运行复制
void StringTest11()
{	
	string s("hello world!");
	size_t pos = s.find('w');
	//从下标6开始截取长度为6的字符串
	string str = s.substr(pos, 6);
	cout << str << endl;
}

3.0.auto和范围for

auto:是c++11重新引入的,用来自动识别变量的类型。 而范围for是一种新的东西

代码语言:javascript代码运行次数:0运行复制
语法:
for (auto element : container) {
    // 对element进行操作
}

这个范围的for循环遍历string的底层原理是基于迭代器实现的。 当使用基于范围的for循环遍历string时,编译器会将其转换为等价的传统for循环,并在幕后调用string类的begin()和end()成员函数。begin()函数返回一个指向string中第一个字符的迭代器,end()函数返回一个指向字符串末尾字符下一个位置的迭代器,这个迭代器通常被称为 “超出末端迭代器”,用于标记字符串的结束位置。

代码语言:javascript代码运行次数:0运行复制
void StringTest13()
{
	string s("Hello World!");
	for (auto x : s)
	{
		cout << x;
	}
	cout << endl;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-24,如有侵权请联系 cloudcommunity@tencent 删除stlstring函数字符串c++

本文标签: STL string 实用攻略打造优质 C 代码