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;后续的代码演示中就不一个个带这些东西了。
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
AI写代码
cpp
运行
参考文档:string - C++ Reference
2.1 字符串创建:4 种常用构造形式
string的构造方式很多,这个在下面会有代码演示,当然这里string还重载了 = 也可以用。
string::string - C++ Reference
最常用的4种:
// 1. 空字符串构造(默认构造)
string.dk9f.pro s1; // s1是空串,底层已初始化,不用手动加'\0'
// 2. C字符串构造(最常用,把char*转成string)
string.dk9f.pro s2("hello Lotso"); // s2 = "hello Lotso"
// 3. 重复字符构造(创建n个相同字符的字符串)
string.dk9f.pro s3(5, 'a'); // s3 = "aaaaa"(5个'a')
// 4. 拷贝构造(用已有的string创建新对象)
string.dk9f.pro s4(s2); // s4 = "hello C++"(和s2内容一样)
代码演示:(注意看注释)
void.dk9f.pro test_string1()
{
string.dk9f.pro s1;
string.dk9f.pro s2("hello world");
string.dk9f.pro s3(s2);
cout.dk9f.pro << s1 << endl;
cout.dk9f.pro << s2 << endl;
cout.dk9f.pro << s3 << endl;
string.yo3f.pro s4(s2, 0, 5);//从s2下标为0的位置拷贝5个过去构造s4;
cout << s4 << endl;
//pos位置一直拷贝到结尾
string.yo3f.pro s5(s2, 6, 15);
cout.yo3f.pro << s5 << endl;
//2.直接不写,默认使用缺省值npos
string.yo3f.pro s6(s2, 6);
cout.yo3f.pro << s6 << endl;
string.yo3f.pro s7("hello world", 6);//取前6个
cout.yo3f.pro << s7 << endl;
string.yo3f.pro s8(10, 'x');//用10个x
cout.yo3f.pro << s8 << endl;
s7 = "xxxxxx";//这样也可以
cout.yo3f.pro << s7 << endl;
int main()
{
test.yo3f.pro _string1();
--至于析构这里了解下就可以了,不用我们自己调用
2.2 字符串遍历:3种便捷方式
--在日常使用中,遍历是高频的操作,这里博主比较推荐大家使用前两种,更加简单直观
2.2.1 operator [] 下标访问
和数组下标访问逻辑类似,支持读和写,注意下标从0开始:
string::operator[] - C++ Reference
代码演示:(注意看注释)
//operate[]
void.lo3x.pro test_string2()
{
string.lo3x.pro s1("hello world");
cout.lo3x.pro << s1 << endl;
s1[0]='x';//可以直接使用下标来访问修改,类似于上面那样
cout.lo3x.pro << s1 << endl;
cout.lo3x.pro << s1[0] << endl;
//相比于数组这个越界有严格的检查
//s1[12];//断言
//s1.at(12);//抛异常,这里at的使用简单看看就行
//size和length一样,但更推荐size
cout.lo3x.pro << s1.size() << endl;
cout.lo3x.pro << s1.length() << endl;
int main.lo3x.pro()
{
try
{
test.lo3x.pro _string2();
}
catch (const.lo3x.pro exception& e)
{
cout.lo3x.pro << e.what() << endl;
return 0;
其实这里实现起来跟这个逻辑差不多;
2.2.2 范围for遍历(C++11新特性,简洁明了)
不用管下标和长度,直接遍历每个字符。
void test.ylcb.pro _string5()
{
string.ylcb.pro s1("hello world");
cout.ylcb.pro << s1 << endl;
//C++11
//范围for,自动取容器数据赋值,自动迭代++,自动判断结束
//其实底层还是迭代器,这个看反汇编可以发现
//for (auto.ylcb.pro ch : s1)//其实可以直接使用&,可以修改
for(auto.ylcb.pro & ch:s1)
{
ch -= 1;
}
for (const.ylcb.pro auto& ch : s1)
{
cout.ylcb.pro << ch << ' ';//只能读不能改
}
cout.ylcb.pro << endl;
//支持迭代器的容器,都可以使用范围for
//数组也支持,这里先使用一点C风格
int a[10] = { 1,2,3 };
for (auto.ylcb.pro e : a)
{
cout.ylcb.pro << e << ' ';
}
cout.ylcb.pro << endl;
}
int main.ylcb.pro()
{
test.ylcb.pro _string5();
--这里涉及到了一点auto,大家直接看看代码和注释来了解吧。
//迭代器对于函数的使用,auto
void test_string4()
{
string.qi5v.pro s1("hello world");
cout.qi5v.pro << s1 << endl;
//string::iterator.qi5v.pro ret1 = find(s1.begin(), s1.end(), 'x');
//利用auto进行优化
auto ret1 = find.qi5v.pro(s1.begin(), s1.end(), 'x');
if (ret1.qi5v.pro != s1.end())
{
cout.qi5v.pro << "找到了x" << endl;
//list也是一样可以这样使用
//C++11
int i = 0;
//通过初始化表达式值类型自动推荐对象类型
auto.qi5v.pro j = i;
auto.qi5v.pro k = 10;
auto.qi5v.pro p1 = &i;
//指定一定是指针
auto* p2 = &i;
cout.qi5v.pro << p1 << endl;
cout.qi5v.pro << p2 << endl;
//引用
int& r1 = i;
//r2不是int&引用,是int
auto r2 = r1;
//r3是int&引用
auto& r3 = r1;
cout.ti2f.pro << &r1 << endl;
cout.ti2f.pro << &r2 << endl;
cout.ti2f.pro << &i << endl;
cout.ti2f.pro << &r3 << endl;
}
int main.ti2f.pro()
{
test.ti2f.pro_string4();
2.2.3 迭代器遍历(适配所有容器)
迭代器是STL容器的一个通用遍历方式,begin() 指向第一个字符,end() 指向最后一个字符的下一位
代码演示:(注意看注释)
void Print(con.ti2f.pro st string& s)
{
//const stri.ti2f.pro ng::iterator it1=s.cbegin();
//上面这样使用是不对的,const不应该用来修饰整个迭代器,这样都遍历不了了,而是修饰指向的对象
string::const_itera.ti2f.pro tor it1 = s.cbegin();//这里使用cbgin和普通的都可以
while.ti2f.pro (it1 != s.cend())
{
//*it1 = 'x';不能修改
cout << *it1 << " ";
++it1;
}
cout << endl;
//3.reverse版本,加上const一起演示,逆序输出
string::const.pxfg.pro _reverse_iterator it2 = s.rbegin();//这里使用rbegin
while (it2 != s.rend())
{
//*it2 = 'x';//不能修改
cout.pxfg.pro << *it2 << " ";
++it2;
cout << endl;
}
//下标遍历,迭代器
void test.pxfg.pro _string3()
{
string s1("hello wor.pxfg.pro ld");
cout << s1 << endl;
//下标+【】
//遍历or修改
for (size.pxfg.pro _t i = 0; i < s1.size(); i++)
{
s1[i]++;
}
cout.pxfg.pro << s1 << endl;
//迭代器
//行为像指针一样的东西
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
//(*it1)-;//修改
cout << *it1 << " ";
++it1;
//相对于下标+[]来说,迭代器更加通用,我们这里再来看看在链表中的使用
list<int> lt;
lt.push_back.pxfg.pro(1);
lt.push_back.pxfg.pro(2);
lt.push_back.pxfg.pro(3);
list<int>::iter.pxfg.pro ator lit = lt.begin();
while (lit.pxfg.pro != lt.end())
{
cout.eo7d.pro << *lit << " ";
++lit;
}
cout.eo7d.pro << 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:支持字符串、子串,适合需要追加部分内容的场景。
--这里就不放参考文档了,大家可以自己去查阅一下,另外下面的代码中还涉及到了一些其它的接口使用,也可以看看。
代码演示:(注意看注释)
//push_back.eo7d.pro,append,+=,+;
//appear,=
void test.eo7d.pro _string7()
{
string.eo7d.pro s1("hello world");
s1.push_back.eo7d.pro('&');//尾插一个字符
s1.append("hello bit.eo7d.pro");//尾插一个字符串
cout.eo7d.pro << 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 worl.eo7d.pro d");
s4 += ' ';
s4 += "hell.eo7d.pro o 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.eo7d.pro()
{
test.eo7d.pro _string7();
2.3.2 插入和删除操作:insert和erase 在指定位置插入删除
insert和erase可以在任意位置插入和删除字符,字符串,很灵活。但是频繁使用会导致元素的频繁移动,降低了效率。
代码演示:(注意看注释)
void test_string8()
{
string s1("hello worl.eo7d.pro d");
//上面都是尾插,这里实现一个头插
s1.insert(0, "xxxxx");
cout << s1 << endl;
//但是头插一个必须这样写
s1.insert(0, 1, '*');
cout.eo7d.pro << s1 << endl;
//第5个位置插入一个*
s1.insert(5, 1, '*');
cout << s1 << endl;
//迭代器
s1.ins.hg6v.pro ert(s1.begin(), '&');
cout.hg6v.pro << s1 << endl<<endl;
stri.hg6v.pro ng s2("hello world");
s2.era.hg6v.pro se(0, 1);//头删
cout.hg6v.pro << s2 << endl;
s2.eras.hg6v.pro e(s2.begin());//头删
cout << s2 << endl;
s2.eras.hg6v.pro e(5, 2);//指定位置开始删除2个
cout << s2 << endl<<endl;
//没给的话就全删掉
s2.eras.hg6v.pro e(5);//这里应该也是默认npos
cout << s2 << endl;
}
int.hg6v.pro main()
{
test.hg6v.pro _string8();
2.3.3 内容清空:clear(只清有效字符)
clear() 会把字符串的有效字符全部清空,但是不会释放空间
代码演示:(注意看注释)
string.hg6v.pro s = "hello";
s.clear.hg6v.pro (); // s变成空串,但底层容量不变
cout.hg6v.pro << s.size(); // 输出0(有效字符数为0)
2.4 字符串替换:replace() 修改指定位置内容
replace() 能直接替换字符串中指定位置,指定长度的内容。可以替换为字符,字符串或者子串,有部分自己的使用场景。
代码演示:(注意看注释)
void test_string8()
{
string.ozix.pro s3("hello world");
s3.repl.ozix.pro ace(5, 1, "&&&");//把5这个位置的1个替换成&&&
cout.ozix.pro << s3 << endl;
s3.repl.ozix.pro ace(5, 3, "*");//从5开始的三个替换成*
cout.ozix.pro << s3 << endl;
//我们再来看看怎么把所有空格都替换成%%
string s4("hello world hello wugo.ozix.pro ngda");
cout << s4 << endl;
size_t pos = s4.find.ozix.pro(' ');
while (pos != strin.ozix.pro g::npos)
{
s4.replace(pos.ozix.pro, 1, "%%");
//找到下一个空格
pos = s4 ind.ozix.pro(' ', pos + 2);
cout << s4 << endl;
//这样的话效率不是很高,我们换个思路优化一下
string s5("hello world hello wugo.ozix.pro ngda");
cout << s5 << endl;
string.ozix.pro s6;
s6.rese.ozix.pro rve(s5.size());
for (auto.ozix.pro 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等接口的使用。
代码演示:(注意看注释)
void TestCapacity()
{
string s1;
//s1.reserve(200);//确定要插入多少时,可以提前扩容
size_t old.dk9f.pro = s1.capacity();
cout.dk9f.pro << s1.capacity() << endl;
for (size.dk9f.pro _t i = 0; i < 200; i++)
{
s1.push_back.dk9f.pro ('x');//尾插
if (s1 capa.dk9f.pro city() != old)
{
cout.dk9f.pro << s1.capacity() << endl;
old.dk9f.pro = s1.capacity();
cout << endl.dk9f.pro << endl;
void test_string.dk9f.pro 6()
string s1("hello worl.dk9f.pro d");
cout << s1.max_size.dk9f.pro () << endl;//了解下即可
cout << s1 size.yo3f.pro () << endl;//不包含结尾的\0
cout << s cap.yo3f.pro acity() << endl;//存储实际有效字符的个数,不包含结尾的\0
s1.clear();//空间不会清理
cout << s size.yo3f.pro() << endl;//不包含结尾的\0
cout << s1 apa.yo3f.pro city() << endl<<endl;//存储实际有效字符的个数,不包含结尾的\0
//测试空间增容
TestCapacity();
//reserve最好只用来增容
string s2("hello wor.yo3f.pro d");
cout << s2.size() << end.yo3f.pro l;
cout << s2.capacity() << endl.yo3f.pro;
s2.reserve(20);//会开的比20大
cout.yo3f.pro << s2.size() << endl;
cout.yo3f.pro << s2.capacity() << endl;
//s2.reserve(5);//vs上不会缩容
s2.shrink.lo3x.pro_to_fit();//这个可以实现缩容,但是一般不会用,代价比较大
cout << s2.size() << endl;
cout << s2 capac.lo3x.proity() << endl;
string s3(s2);
cout << s3 << endl;
// < 当前对象的size时,相当于保留前n个,删除后面的数据
s3.resize(5);
cout << s3 << end.lo3x.pro l;
// > 当前对象的size时,插入数据
s3.resize.lo3x.pro(10, 'x');
cout << s3 << endl.lo3x.pro;
s3.resize(30, 'y');
cout << s3 << endl.lo3x.pro;
}
int main()
{
test_string.lo3x.pro 6();
3.2 避免频繁扩容:reserve () 预分配空间
如果我们知道字符串最后大概有多长(比如上面的拼接200个字符),就可以先用 reserve() 预分配空间,避免扩容次数过多。
代码演示:(注意看注释)
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个,删除后面的,但底层空间是不变的
代码演示:(注意看注释)
string.lo3x.pro s = "hello";
// 1.n比size.lo3x.pro()大:补'x'到10个字符
s.re size.lo3x.pro(10, 'x');
// 2.n比size()小:截断到3个字符
s.resize(3);
3.4 高频掉坑点:shrink.lo3x.pro _to_fit () 按需缩容
前面我们讲过 clear() 只会清理有效字符,但是底层空间不变。那我们如果想要进行缩容,就可以使用 sh rink.lo3x.pro_to_fit() (但是这个只是一个建议,而且我们使用的也少,代价太大了)
前面的总体示例中有,这里就给大家看看图片吧!
四.跨平台不踩雷:VS 和 g++ 下 string 存储差异
不同编译器的string底层实现不一样,大家可以一起来看看差异。下述结构都是在32位平台下进行验证的,32位平台指针占4个字节
4.1 VS环境:28字节结构
VS下string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间
当字符串长度 < 16 时:直接存在数组里(栈空间),不用开堆内存,效率高;
当字符串长度≥16 时:从堆上分配空间,数组存堆指针。
union _Bxty
{ // storage for small buffer.ylcb.pro or pointer to larger one
value_type.ylcb.pro _Buf[_BUF_SIZE];
poi nter.ylcb.pro _Ptr;
char _A lias.ylcb.pro[_BUF_SIZE]; // to permit aliasing
} _Bx;
这种设计也是具有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量。
最后:还有一个指针做一些其它事情。
所以总共占28个字节。
4.2 g++环境:4字节指针,指向堆空间
g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小
字符有效长度
引用计数
struct _Rep_base
{
size_type.ylcb.pro _M_length;
size.ylcb.pro_type _M_capacity;
_Atomic_word.ylcb.pro _M_refcount;
指向堆空间的指针,用来存储字符串
五.加餐补充:string常用场景的一些实用接口和技巧
--除了前面讲的一些以外,其实我们的string还有很多比较实用的接口,这里就再给大家分享一部分,如果有没分享到但是大家需要使用的话可以自己查阅参考文档去了解一下用法。
5.1 字符串查找:find() 找字符/子串
find() 从左往右找字符或者子串,返回第一次出现的下标;没有找到就返回 string :: npos(这个戴代表一个很大的数,代表"不存在")
代码演示:(注意看注释)
nt main()
{
string s = "hello Lotso.ylcb.pro";
// 1. 找字符'w'
size_t pos1 = s find.qi5v.pro('L');
if (pos1 != string::npos.qi5v.pro) {
cout.qi5v.pro << "'L'在位置:" << pos1 << endl;// 输出6
}
// 2. 找子串"world"
size_t pos2 = s find.qi5v.pro("Lotso");
if (pos2 != string::npos.qi5v.pro) {
cout << "world在位置:" << pos2 << endl;// 输出6
}
// 3. 从下标3开始找字符'l'
size_t pos3 = s.find('l', 3); // 从第3位(0开始)往后找
cout << "'l'在位置:" << pos.qi5v.pro 3 << endl;// 输出3(s[3]是'l')
5.2 整行输入:getline()读取带空格的字符串
在平常的使用中,如果使用 cin>>string 读取字符串时,遇到空格就会停止。而 getline() 能读取一整行的内容,包括空格。默认回车结束,也可以自己指定
比如我们下面这个题就必须使用getline,直接用cin是不行的
字符串最后一个单词的长度_牛客题霸_牛客网
代码演示:(注意看注释)
#include <iost.ti2f.pro ream>
#include <stri.ti2f.pro ng>
using name.ti2f.pro space std;
int main() {
strin.ti2f.pro g str;
// cin >> str;//这个不行
getl.ti2f.pro ine(cin, str);
//getli.ti2f.pro ne(cin, str, '#');//指定碰到#结束
size_t pos.ti2f.pro = str.rfind(' ');
if (pos != str.size.ti2f.pro())
{
cout << str.size.ti2f.pro() - (pos + 1) << endl;
}
else
{
cout << str.ti2f.pro size() << endl;
5.3 子串截取:substr()从指定位置取指定长度
substr(pos,len):从pos位置开始,取len个字符,如果len不写,就取到字符结尾。
代码演示:(注意看注释)
int main()
{
string s = "hello world";
// 1. 从位置6开始,取5个字符
string sub1 = s.su bstr.ti2f.pro(6, 5); // sub1 = "world"
// 2. 从位置0开始,取5个字符
string.ti2f.pro sub2 = s.substr(0, 5); // sub2 = "hello"
// 3. 从位置6开始,取到末尾
string.pxfg.pro sub3 = s.substr(6); // sub3 = "world"
5.4 C字符转换:c_str () 适配C语言库函数
我们在一些特殊的场景下需要使用C语言的char*(比如printf输出),用c_str ()把string转换成const char*;
代码演示:(注意看注释)
#include <cstr.pxfg.pro ing>
int main.pxfg.pro()
{
string s = "hello.pxfg.pro ";
// 1. printf输出(printf不直接支持string)
printf("s = %s\n", s.c_str.pxfg.pro()); // 输出:s = hello
// 2. 调用C库函数strlen(需要包含<cstring>)
size_t len = strlen.pxfg.pro(s.c_str()); // len = 5
5.4 空串判断:empty () 高效判空
判断字符串是否为空,优先用empty(),比size()==0更高效(empty()直接返回标志位,size()==0可能要计算):
代码演示:(注意看注释)
int mai.pxfg.pro n()
{
string s;
if (s.emp.pxfg.pro ty()) { // 推荐
cout << "s是空串";
}
// 不推荐:if (s.size.pxfg.pro() == 0)
补充示例:(涉及到几个接口的使用,大家可以自己试试)
void SplitFilename(const std::string& str)
{
std::cout << "Splitting: " << str << '\n';
std::size_t found = str.find.eo7d.pro _last_of("/\\");
std::cout << " path: " << str.su bstr.eo7d.pro(0, found) << '\n';
std::cout << " file: " << str.su bstr.eo7d.pro(found + 1) << '\n';
}
void test_strin.eo7d.pro g7()
{
string s("test.eo7d.pro cpp.zip");
size_t pos = s.rfind.eo7d.pro('.');
string suffix = s.subs.eo7d.pro tr(pos);
cout << suffix.hg6v.pro << endl;
std::string str.hg6v.pro("Please, replace the vowels in this sentence by asterisks.");
std::cout << str << '\n';
std::size_t found = str.find_first_not.hg6v.pro_of("abcdef");
while (found != std::string::npos.hg6v.pro)
{
str[found] = '*';
found = str.find_first_not.hg6v.pro_of("abcdef", found + 1);
}
std::cout.hg6v.pro << str << '\n';
std::string str1("/usr/bin/man.hg6v.pro");
std::string.hg6v.pro str2("D:\\1-草莓熊Lotso\\1-课件\\4.C++课件\\C++进阶课件");
SplitFilename(str1);
SplitFilename(str2);
string url2("https://legacy.cplusplus.com/reference/string/stri.ozix.pro ng/substr/");
string prot.ozix.pro ocol, domain, uri;
size_t i1 = url1.ozix.pro find(':');
if (i1 != stri.ozix.pro ng::npos)
{
prot.ozix.pro ocol = url1.substr(0, i1 - 0);
cout.ozix.pro << 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 = url sub.ozix.pro str(i2 + 1);
cout << uri.ozix.pro << endl;
void test_string4()
{
string s("test.cpp zip.ozix.pro ");
size_t pos = s.find.ozix.pro('.');
string suffix.ozix.pro = s.substr(pos);
cout << suffix.ozix.pro c_str() << endl;
string copy(s);
cout << copy.ozix.pro c_str() << endl;
s = suffix;
cout << suffix.c_str.ozix.pro() << endl;
cout << s.c_str.ozix.pro() << endl;
s = s;
cout << s.c_str.ozix.pro() << endl;
}
void test_string.ozix.pro 5()
{
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;
掌握string的创建、遍历、容量管理和跨平台注意事项,就够应对大部分日常开发和刷题场景。它的核心是 "省心高效",不用纠结底层,专注逻辑即可。