首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++ string 类使用超全攻略:从入门到高效避坑,日常开发直接使用

C++ string 类使用超全攻略:从入门到高效避坑,日常开发直接使用

作者头像
LOTSO
发布2025-10-29 15:07:15
发布2025-10-29 15:07:15
4300
代码可运行
举报
文章被收录于专栏:C++C++
运行总次数:0
代码可运行

🔥草莓熊Lotso:个人主页

❄️个人专栏:《C++知识分享》《Linux 入门到实践:零基础也能懂》

生活是默默的坚持,毅力是永久的享受。


🎬博主简介:

前言

用char*处理字符串总踩内存、越界的坑?C++string类早帮你搞定这些 —— 自动管内存、接口丰富,开发和 OJ 题都高频用。这篇博客只讲string核心使用:从创建、遍历、修改,到容量优化、跨平台避坑,附实用代码,帮你快速上手解决需求。


一. 别再折腾 char*!学会string 类让你轻松处理字符串

1.1 C语言字符串的三大痛点

--我们之前用C语言字符串时,大家应该多少都踩过下面这些坑

  • 内存需要手动管理:malloc分配空间,free释放空间,经常出现漏写或者多写的问题
  • 容易越界访问:靠'\0'判断结束,一旦忘记加上终止符就可能出现一些问题
  • 接口零散,使用麻烦:批接使用strcat,拷贝用strcpy,求长度用strlen,函数和字符串本身分离,不符合使用习惯,而且需要自己对空间进行提前的处理。

1.2 string类的优势

--那么我们学习string类,可以把这些痛点都变成过去式;

  • 自动内存管理:不用手动new/delete,底层会按需进行扩容,释放,有效避免了内存的泄漏
  • OOP设计:把字符串和操作(如拼接,查找等)封装在一起,比如 s += “Lotso” 直接实现拼接,比strcat 简洁很多
  • 接口丰富,使用方便:从构造到修改,查找这些常用操作全部都实现成了接口,不用自己造轮子,直接使用就行。
  • 使用场景广泛:不管是日常开发还是OJ刷题,string类的使用都很频繁,并且天然兼容中文,英文,Unicode字符。

二. 0基础上手:string的核心接口使用(附实操代码)

--在使用之前我们先需要对string有个基础的了解,并且不要忘记带上头文件以及using namespace std;后续的代码演示中就不一个个带这些东西了。

代码语言:javascript
代码运行次数:0
运行
复制
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

2.1 字符串创建:4 种常用构造形式

string的构造方式很多,这个在下面会有代码演示,当然这里string还重载了 = 也可以用。

string::string - C++ Reference

最常用的4种:

代码语言:javascript
代码运行次数:0
运行
复制
// 1. 空字符串构造(默认构造)
string s1;  // s1是空串,底层已初始化,不用手动加'\0'

// 2. C字符串构造(最常用,把char*转成string)
string s2("hello Lotso");  // s2 = "hello Lotso"

// 3. 重复字符构造(创建n个相同字符的字符串)
string s3(5, 'a');  // s3 = "aaaaa"(5个'a')

// 4. 拷贝构造(用已有的string创建新对象)
string s4(s2);  // s4 = "hello C++"(和s2内容一样)

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
void test_string1()
{
	string s1;
	string s2("hello world");
	string s3(s2);

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;

	string s4(s2, 0, 5);//从s2下标为0的位置拷贝5个过去构造s4;
	cout << s4 << endl;

	//pos位置一直拷贝到结尾
	//1.写一个超过s2长度的
	string s5(s2, 6, 15);
	cout << s5 << endl;
	//2.直接不写,默认使用缺省值npos
	string s6(s2, 6);
	cout << s6 << endl;

	string s7("hello world", 6);//取前6个
	cout << s7 << endl;

	string s8(10, 'x');//用10个x
	cout << s8 << endl;

	s7 = "xxxxxx";//这样也可以
	cout << s7 << endl;

}

int main()
{
	test_string1();
}

--至于析构这里了解下就可以了,不用我们自己调用

2.2 字符串遍历:3种便捷方式

--在日常使用中,遍历是高频的操作,这里博主比较推荐大家使用前两种,更加简单直观

2.2.1 operator [] 下标访问

和数组下标访问逻辑类似,支持读和写,注意下标从0开始:

string::operator[] - C++ Reference

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
//operate[]
void test_string2()
{
	string s1("hello world");
	cout << s1 << endl;

	s1[0]='x';//可以直接使用下标来访问修改,类似于上面那样
	cout << s1 << endl;
	cout << s1[0] << endl;

	//相比于数组这个越界有严格的检查
	//s1[12];//断言
	//s1.at(12);//抛异常,这里at的使用简单看看就行

	//size和length一样,但更推荐size
	cout << s1.size() << endl;
	cout << s1.length() << endl;
}

int main()
{
	try
	{
		test_string2();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

其实这里实现起来跟这个逻辑差不多;

2.2.2 范围for遍历(C++11新特性,简洁明了)

不用管下标和长度,直接遍历每个字符。

代码语言:javascript
代码运行次数:0
运行
复制
void test_string5()
{
	string s1("hello world");
	cout << s1 << endl;

	//C++11
	//范围for,自动取容器数据赋值,自动迭代++,自动判断结束
	//其实底层还是迭代器,这个看反汇编可以发现
	
	//for (auto ch : s1)//其实可以直接使用&,可以修改
	for(auto& ch:s1)
	{
		ch -= 1;
	}

	for (const auto& ch : s1)
	{
		cout << ch << ' ';//只能读不能改
	}
	cout << endl;

	//支持迭代器的容器,都可以使用范围for
	//数组也支持,这里先使用一点C风格
	int a[10] = { 1,2,3 };
	for (auto e : a)
	{
		cout << e << ' ';
	}
	cout << endl;
}
int main()
{
	test_string5();
}

--这里涉及到了一点auto,大家直接看看代码和注释来了解吧。

代码语言:javascript
代码运行次数:0
运行
复制
//迭代器对于函数的使用,auto
void test_string4()
{
	string s1("hello world");
	cout << s1 << endl;

	//string::iterator ret1 = find(s1.begin(), s1.end(), 'x');
	//利用auto进行优化
	auto ret1 = find(s1.begin(), s1.end(), 'x');
	if (ret1 != s1.end())
	{
		cout << "找到了x" << endl;
	}

	//list也是一样可以这样使用

	//C++11
	int i = 0;
	//通过初始化表达式值类型自动推荐对象类型
	auto j = i;
	auto k = 10;
	auto p1 = &i;
	//指定一定是指针
	auto* p2 = &i;
	cout << p1 << endl;
	cout << p2 << endl;

	//引用
	int& r1 = i;
	//r2不是int&引用,是int
	auto r2 = r1;
	//r3是int&引用
	auto& r3 = r1;
	cout << &r1 << endl;
	cout << &r2 << endl;
	cout << &i << endl;
	cout << &r3 << endl;
}
int main()
{
	test_string4();
}
2.2.3 迭代器遍历(适配所有容器)

迭代器是STL容器的一个通用遍历方式,begin() 指向第一个字符,end() 指向最后一个字符的下一位

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
void Print(const string& s)
{
	//2.const版本
	//const string::iterator it1=s.cbegin();
	//上面这样使用是不对的,const不应该用来修饰整个迭代器,这样都遍历不了了,而是修饰指向的对象
	string::const_iterator it1 = s.cbegin();//这里使用cbgin和普通的都可以
	while (it1 != s.cend())
	{
		//*it1 = 'x';不能修改
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;

	//3.reverse版本,加上const一起演示,逆序输出
	string::const_reverse_iterator it2 = s.rbegin();//这里使用rbegin
	while (it2 != s.rend())
	{
		//*it2 = 'x';//不能修改
		cout << *it2 << " ";
		++it2;
	}
	cout << endl;
}
//下标遍历,迭代器
void test_string3()
{
	string s1("hello world");
	cout << s1 << endl;

	//下标+【】
	//遍历or修改
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;
	}
	cout << s1 << endl;

	//迭代器
	//行为像指针一样的东西
	//1.常规使用
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		//(*it1)-;//修改
		cout << *it1 << " ";
		++it1;
	}

	//相对于下标+[]来说,迭代器更加通用,我们这里再来看看在链表中的使用
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	list<int>::iterator lit = lt.begin();
	while (lit != lt.end())
	{
		cout << *lit << " ";
		++lit;
	}
	cout << endl;

	//迭代器的其它使用形式
	Print(s1);
}
int main()
{
	test_string3();
}

2.3 字符串修改:高频操作(拼接/插入/删除/清空)

2.3.1 尾部追加:3种方式对比(operator+=/push_back/append)

尾部追加是我们比较常见的修改场景,3中方式各有优势,大家可以按需选择:

  • operator+=:支持追加单个字符和字符串,比 push_back 和 append 更加灵活
  • push_back:仅支持单个字符,功能单一,适合明确追加单个字符的场景;
  • append:支持字符串、子串,适合需要追加部分内容的场景。

--这里就不放参考文档了,大家可以自己去查阅一下,另外下面的代码中还涉及到了一些其它的接口使用,也可以看看。

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
//push_back,append,+=,+;
//appear,=
void test_string7()
{
	string s1("hello world");
	s1.push_back('&');//尾插一个字符
	s1.append("hello bit");//尾插一个字符串
	cout << s1 << endl;

	s1.append(10, 'x');//尾插10个x
	cout << s1 << endl;

	//还可以配着迭代器使用
	string s3;
	string s2(" apple hello!");
	//我不想要空格和!
	s3.append(++s2.begin(), --s2.end());
	cout << s3 << endl;

	//其实我们直接使用+=更加方便
	string s4("hello world");
	s4 += ' ';
	s4 += "hello bit";
	cout << s4 << endl;

	//为什么不把 + 重载为成员的而是全局,因为这样可以不用一定把成员变量写在左边
	cout << s4 + "xxxx" << endl;
	cout << "xxxx" + s4 << endl;

	//assign,没有直接赋值好用
	s4 = "xxx";
	cout << s4 << endl;
	s4.assign("yyy");
	cout << s4 << endl;
}
int main()
{
	test_string7();
}
2.3.2 插入和删除操作:insert和erase 在指定位置插入删除
  • insert和erase可以在任意位置插入和删除字符,字符串,很灵活。但是频繁使用会导致元素的频繁移动,降低了效率。

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
void test_string8()
{
	string s1("hello world");

	//上面都是尾插,这里实现一个头插
	s1.insert(0, "xxxxx");
	cout << s1 << endl;

	//但是头插一个必须这样写
	s1.insert(0, 1, '*');
	cout << s1 << endl;

	//第5个位置插入一个*
	s1.insert(5, 1, '*');
	cout << s1 << endl;

	//迭代器
	s1.insert(s1.begin(), '&');
	cout << s1 << endl<<endl;

	string s2("hello world");

	s2.erase(0, 1);//头删
	cout << s2 << endl;

	s2.erase(s2.begin());//头删
	cout << s2 << endl;

	s2.erase(5, 2);//指定位置开始删除2个
	cout << s2 << endl<<endl;

	//没给的话就全删掉
	s2.erase(5);//这里应该也是默认npos
	cout << s2 << endl;

}
int main()
{
	test_string8();
}
2.3.3 内容清空:clear(只清有效字符)
  • clear() 会把字符串的有效字符全部清空,但是不会释放空间

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
string s = "hello";
s.clear();  // s变成空串,但底层容量不变
cout << s.size();  // 输出0(有效字符数为0)

2.4 字符串替换:replace() 修改指定位置内容

replace() 能直接替换字符串中指定位置,指定长度的内容。可以替换为字符,字符串或者子串,有部分自己的使用场景。

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
void test_string8()
{
	
	string s3("hello world");

	s3.replace(5, 1, "&&&");//把5这个位置的1个替换成&&&
	cout << s3 << endl;

	s3.replace(5, 3, "*");//从5开始的三个替换成*
	cout << s3 << endl;

	//我们再来看看怎么把所有空格都替换成%%
	string s4("hello            world hello wugongda");
	cout << s4 << endl;

	size_t pos = s4.find(' ');
	while (pos != string::npos)
	{
		s4.replace(pos, 1, "%%");

		//找到下一个空格
		pos = s4.find(' ', pos + 2);
	}

	cout << s4 << endl;

	//这样的话效率不是很高,我们换个思路优化一下
	string s5("hello            world hello wugongda");
	cout << s5 << endl;
	string s6;
	s6.reserve(s5.size());
	for (auto ch : s5)
	{
		if (ch != ' ')
		{
			s6 += ch;
		}
		else
		{
			s6 += "%%";
		}
	}
	cout << s6 << endl;
	//s5 = s6;
	
}
int main()
{
	test_string8();
}

三. 效率拉满:string容量管理避坑攻略

我们在使用string时,如果不注意容量,可能会导致频繁扩容,拖慢程序的效率。我们先了解一下下面会涉及的三个接口,再来学学优化技巧。

3.1 先理清:size ()、length ()、capacity () 的区别

很多人会混淆这三个接口的作用,我们先通过表格对比看看吧。

接口

功能

日常用哪个?

string::size - C++ Reference

返回有效字符个数(比如 "hello" 返回 5)

优先用,和其他 STL 容器接口一致

string::length - C++ Reference

和size()功能完全一样(历史遗留接口)

少用,不如size()通用

string::capacity - C++ Reference

返回底层已分配的空间大小(能存多少字符,不含 '\0')

优化时用

--我们先来看看一个整体的代码,里面还涉及到了resize,shrink_to_fit等接口的使用。

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
void TestCapacity()
{
	string s1;
	//s1.reserve(200);//确定要插入多少时,可以提前扩容
	size_t old = s1.capacity();
	cout << s1.capacity() << endl;
	for (size_t i = 0; i < 200; i++)
	{
		s1.push_back('x');//尾插
		if (s1.capacity() != old)
		{
			cout << s1.capacity() << endl;
			old = s1.capacity();
		}
	}
	cout << endl << endl;
}

void test_string6()
{
	string s1("hello world");
	cout << s1.max_size() << endl;//了解下即可

	cout << s1.size() << endl;//不包含结尾的\0
	cout << s1.capacity() << endl;//存储实际有效字符的个数,不包含结尾的\0

	s1.clear();//空间不会清理
	cout << s1.size() << endl;//不包含结尾的\0
	cout << s1.capacity() << endl<<endl;//存储实际有效字符的个数,不包含结尾的\0

	//测试空间增容
	TestCapacity();

	//reserve最好只用来增容
	string s2("hello world");
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;

	s2.reserve(20);//会开的比20大
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;

	//s2.reserve(5);//vs上不会缩容
	s2.shrink_to_fit();//这个可以实现缩容,但是一般不会用,代价比较大
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;


	string s3(s2);
	cout << s3 << endl;

	// < 当前对象的size时,相当于保留前n个,删除后面的数据
	s3.resize(5);
	cout << s3 << endl;

	// > 当前对象的size时,插入数据
	s3.resize(10, 'x');
	cout << s3 << endl;

	s3.resize(30, 'y');
	cout << s3 << endl;

}
int main()
{
	test_string6();
}

3.2 避免频繁扩容:reserve () 预分配空间

如果我们知道字符串最后大概有多长(比如上面的拼接200个字符),就可以先用 reserve() 预分配空间,避免扩容次数过多。

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
string s;
// 已知要存1000个字符,提前预分配
s.reserve(1000);  
// 后续拼接1000个字符,不会频繁扩容
for (int i = 0; i < 1000; ++i) {
    s += 'a';
}
  • 注意点:如果 reserve(n) 的 n 比当前 capacity() 小,vs上不会缩小空间。

3.3 调整有效字符:resize()的2种使用情况

resize(n) 用来修改有效字符的个数,分以下两种情况:

  • 若n比当前size()大:补字符(默认会补‘\0’,'也可以自己指定);
  • 若n比当前size()小:保留前n个,删除后面的,但底层空间是不变的

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
string s = "hello";
// 1.n比size()大:补'x'到10个字符
s.resize(10, 'x');  

// 2.n比size()小:截断到3个字符
s.resize(3);       

3.4 高频掉坑点:shrink_to_fit () 按需缩容

前面我们讲过 clear() 只会清理有效字符,但是底层空间不变。那我们如果想要进行缩容,就可以使用 shrink_to_fit() (但是这个只是一个建议,而且我们使用的也少,代价太大了)

前面的总体示例中有,这里就给大家看看图片吧!


四.跨平台不踩雷:VS 和 g++ 下 string 存储差异

不同编译器的string底层实现不一样,大家可以一起来看看差异。下述结构都是在32位平台下进行验证的,32位平台指针占4个字节

4.1 VS环境:28字节结构

VS下string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间

  • 当字符串长度 < 16 时:直接存在数组里(栈空间),不用开堆内存,效率高;
  • 当字符串长度≥16 时:从堆上分配空间,数组存堆指针。
代码语言:javascript
代码运行次数:0
运行
复制
union _Bxty
{ // storage for small buffer or pointer to larger one
	value_type _Buf[_BUF_SIZE];
	pointer _Ptr;
	char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是具有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其它事情。

所以总共占28个字节。

4.2 g++环境:4字节指针,指向堆空间

g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符有效长度
  • 引用计数
代码语言:javascript
代码运行次数:0
运行
复制
struct _Rep_base
{
	size_type _M_length;
	size_type _M_capacity;
	_Atomic_word _M_refcount;
}
  • 指向堆空间的指针,用来存储字符串

五.加餐补充:string常用场景的一些实用接口和技巧

--除了前面讲的一些以外,其实我们的string还有很多比较实用的接口,这里就再给大家分享一部分,如果有没分享到但是大家需要使用的话可以自己查阅参考文档去了解一下用法。

5.1 字符串查找:find() 找字符/子串

find() 从左往右找字符或者子串,返回第一次出现的下标;没有找到就返回 string :: npos(这个戴代表一个很大的数,代表“不存在”)

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
nt main()
{
    string s = "hello Lotso";
    // 1. 找字符'w'
    size_t pos1 = s.find('L');
    if (pos1 != string::npos) {
        cout << "'L'在位置:" << pos1 << endl;// 输出6
    }

    // 2. 找子串"world"
    size_t pos2 = s.find("Lotso");
    if (pos2 != string::npos) {
        cout << "world在位置:" << pos2 << endl;// 输出6
    }

    // 3. 从下标3开始找字符'l'
    size_t pos3 = s.find('l', 3);  // 从第3位(0开始)往后找
    cout << "'l'在位置:" << pos3 << endl;// 输出3(s[3]是'l')
}

5.2 整行输入:getline()读取带空格的字符串

在平常的使用中,如果使用 cin>>string 读取字符串时,遇到空格就会停止。而 getline() 能读取一整行的内容,包括空格。默认回车结束,也可以自己指定

比如我们下面这个题就必须使用getline,直接用cin是不行的

字符串最后一个单词的长度_牛客题霸_牛客网

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
using namespace std;

int main() {
	string str;
	// cin >> str;//这个不行
	getline(cin, str);
	//getline(cin, str, '#');//指定碰到#结束


	size_t pos = str.rfind(' ');
	if (pos != str.size())
	{
		cout << str.size() - (pos + 1) << endl;
	}
	else
	{
		cout << str.size() << endl;
	}
}

5.3 子串截取:substr()从指定位置取指定长度

  • substr(pos,len):从pos位置开始,取len个字符,如果len不写,就取到字符结尾。

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
int main()
{
	string s = "hello world";
	// 1. 从位置6开始,取5个字符
	string sub1 = s.substr(6, 5);  // sub1 = "world"

	// 2. 从位置0开始,取5个字符
	string sub2 = s.substr(0, 5);  // sub2 = "hello"

	// 3. 从位置6开始,取到末尾
	string sub3 = s.substr(6);     // sub3 = "world"
}

5.4 C字符转换:c_str () 适配C语言库函数

我们在一些特殊的场景下需要使用C语言的char*(比如printf输出),用c_str ()把string转换成const char*;

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
#include <cstring>
int main()
{
	string s = "hello";
	// 1. printf输出(printf不直接支持string)
	printf("s = %s\n", s.c_str());  // 输出:s = hello

	// 2. 调用C库函数strlen(需要包含<cstring>)
	size_t len = strlen(s.c_str());  // len = 5
}

5.4 空串判断:empty () 高效判空

判断字符串是否为空,优先用empty(),比size()==0更高效(empty()直接返回标志位,size()==0可能要计算):

代码演示:(注意看注释)

代码语言:javascript
代码运行次数:0
运行
复制
int main()
{
	string s;
	if (s.empty()) {  // 推荐
		cout << "s是空串";
	}
	// 不推荐:if (s.size() == 0)
}

补充示例:(涉及到几个接口的使用,大家可以自己试试)

代码语言:javascript
代码运行次数:0
运行
复制
void SplitFilename(const std::string& str)
{
	std::cout << "Splitting: " << str << '\n';
	std::size_t found = str.find_last_of("/\\");

	std::cout << " path: " << str.substr(0, found) << '\n';
	std::cout << " file: " << str.substr(found + 1) << '\n';
}
void test_string7()
{
	string s("test.cpp.zip");
	size_t pos = s.rfind('.');
	string suffix = s.substr(pos);
	cout << suffix << endl;

	std::string str("Please, replace the vowels in this sentence by asterisks.");
	std::cout << str << '\n';

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

	std::cout << str << '\n';

	std::string str1("/usr/bin/man");
	std::string str2("D:\\1-草莓熊Lotso\\1-课件\\4.C++课件\\C++进阶课件");

	SplitFilename(str1);
	SplitFilename(str2);

string url2("https://legacy.cplusplus.com/reference/string/string/substr/");
string url1("http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=65081411_1_oem_dg&wd=%E5%90%8E%E7%BC%80%20%E8%8B%B1%E6%96%87&fenlei=256&rsv_pq=0xc17a6c03003ede72&rsv_t=7f6eqaxivkivsW9Zwc41K2mIRleeNXjmiMjOgoAC0UgwLzPyVm%2FtSOeppDv%2F&rqlang=en&rsv_dl=ib&rsv_enter=1&rsv_sug3=4&rsv_sug1=3&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=1588&rsv_sug4=6786");

	string protocol, domain, uri;
	size_t i1 = url1.find(':');
	if (i1 != string::npos)
	{
		protocol = url1.substr(0, i1 - 0);
		cout << protocol << endl;
	}

	// strchar
	size_t i2 = url1.find('/', i1+3);
	if (i2 != string::npos)
	{
		domain = url1.substr(i1+3, i2-(i1+3));
		cout << domain << endl;

		uri = url1.substr(i2 + 1);
		cout << uri << endl;
	}
}

void test_string4()
	{
		string s("test.cpp.zip");
		size_t pos = s.find('.');
		string suffix = s.substr(pos);
		cout << suffix.c_str() << endl;

		string copy(s);
		cout << copy.c_str() << endl;

		s = suffix;
		cout << suffix.c_str() << endl;
		cout << s.c_str() << endl;

		s = s;
		cout << s.c_str() << endl;
	}

	void test_string5()
	{
		string s1("hello world");
		string s2("hello world");

		cout << (s1 < s2) << endl;
		cout << (s1 == s2) << endl;
		cout << ("hello world" < s2) << endl;
		cout << (s1 == "hello world") << endl;
		//cout << ("hello world" == "hello world") << endl;

		cout << s1 << s2 << endl;

		string s0;
		cin >> s0;
		cout << s0 << endl;
	}

	void test_string6()
	{
		string s1("hello world");
		string s2 = s1;

		cout << s1 << endl;
		cout << s2 << endl;

		string s3("xxxxxxxxxxxxxx");
		s1 = s3;

		cout << s1 << endl;
		cout << s3 << endl;
	}

结尾

往期回顾:

《从崩溃到精通:C++ 内存管理避坑指南,详解自定义类型 new/delete 调用构造 / 析构的关键逻辑》

别再用函数重载堆代码了!C++ 模板初阶教程:原理 + 实例 + 避坑,新手也能秒懂

C++ 开发者必看!STL 库 + 字符编码一篇通,告别乱码与重复造轮子

结语:掌握string的创建、遍历、容量管理和跨平台注意事项,就够应对大部分日常开发和刷题场景。它的核心是 “省心高效”,不用纠结底层,专注逻辑即可。你常用string哪个接口?评论区聊聊~zhi

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一. 别再折腾 char*!学会string 类让你轻松处理字符串
    • 1.1 C语言字符串的三大痛点
    • 1.2 string类的优势
  • 二. 0基础上手:string的核心接口使用(附实操代码)
    • 2.1 字符串创建:4 种常用构造形式
    • 2.2 字符串遍历:3种便捷方式
      • 2.2.1 operator [] 下标访问
      • 2.2.2 范围for遍历(C++11新特性,简洁明了)
      • 2.2.3 迭代器遍历(适配所有容器)
    • 2.3 字符串修改:高频操作(拼接/插入/删除/清空)
      • 2.3.1 尾部追加:3种方式对比(operator+=/push_back/append)
      • 2.3.2 插入和删除操作:insert和erase 在指定位置插入删除
      • 2.3.3 内容清空:clear(只清有效字符)
    • 2.4 字符串替换:replace() 修改指定位置内容
  • 三. 效率拉满:string容量管理避坑攻略
    • 3.1 先理清:size ()、length ()、capacity () 的区别
    • 3.2 避免频繁扩容:reserve () 预分配空间
    • 3.3 调整有效字符:resize()的2种使用情况
    • 3.4 高频掉坑点:shrink_to_fit () 按需缩容
  • 四.跨平台不踩雷:VS 和 g++ 下 string 存储差异
    • 4.1 VS环境:28字节结构
    • 4.2 g++环境:4字节指针,指向堆空间
  • 五.加餐补充:string常用场景的一些实用接口和技巧
    • 5.1 字符串查找:find() 找字符/子串
    • 5.2 整行输入:getline()读取带空格的字符串
    • 5.3 子串截取:substr()从指定位置取指定长度
    • 5.4 C字符转换:c_str () 适配C语言库函数
    • 5.4 空串判断:empty () 高效判空
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档