文章目录
1. 为什么学习string类?
1.1 C语言中的字符串
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,
但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可
能还会越界访问。
在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本
都使用string类,很少有人去使用C库中的字符串操作函数。
2. 标准库中的string类
2.1 string类(了解)
string类的文档介绍
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
息,请参阅basic_string)。 - string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。 - 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结
:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
string; - 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
2.2 string类的常用接口说明
2.2.1 string类对象的常见构造
(constructor)函数名称 | 功能说明 |
---|---|
string() | 构造空的string类对象,即空字符 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
#include <iostream>
#include <string>
using namespace std;
void test_string1()
{
string s1;//构造空的string类对象s1
//string s2("hello world!!!");
// string (const char* s);
string s2 = "hello world!!!";// 用C格式字符串构造string类对象s2
cout << s1 << endl;
cout << s2 << endl;
string s3(s2);//拷贝构造s3
cout << s3 << endl;
string s4(s2, 6, 5);//拷贝字符串s2中第六个字母开始拷贝五个字母到s4中
cout << s4 << endl;
// 第三个参数len大于后面字符长度,有多少拷贝多少拷贝到结尾
string s5(s2, 6, 15);
cout << s5 << endl;
string s6(s2, 6);
cout << s6 << endl;
string s7("hello world", 5);
cout << s7 << endl;
string s8(100, 'x');
cout << s8 << endl;
//cin >> s1 >> s2;
//cout << s1 << endl;
//cout << s2 << endl;
}
2.2.2 string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size | 返回字符串的有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 简则字符串释放为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 为字符串预留空间n |
resize | 将有效字符的个数改成你个,多出的空间用字符c填充 |
注意
:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。 - clear()只是将string中有效字符清空,不改变底层空间大小。
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
2.2.3. string类对象的访问及遍历操作
- [ ]遍历
std::string::operator[]
让类像数组一样访问
void test_string3()
{
string s1("hello world");
cout << s1[0] << endl;
s1[0] = 'x';
cout << s1[0] << endl;
cout << s1 << endl;
// 要求遍历string,每个字符+1
for (size_t i = 0; i < s1.size(); ++i)
//for (size_t i = 0; i < s1.length(); ++i)
{
s1[i]++;
}
cout << s1 << endl;
const string s2("world");//只能遍历不能修改
for (size_t i = 0; i < s2.size(); ++i)
{
//s2[i]++;
cout << s2[i] << " ";
}
cout << endl;
cout << s2 << endl;
//s2[6]; 内部会检查越界
}
s1[0] = 'x';
s1.opertor[](0)
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];//返回的是别名
}
不是传值返回而是引用返回,好处:可读可修改
- 减少拷贝
- 修改返回对象
std::string::at
at与[]的功能一致
区别:越界之后会抛异常
虽然迭代器是通用类型,但是string中会更喜欢用[]
- 迭代器 遍历 内嵌类型
四种:
- 正向 iteratior
- const 正向 const_iteration
- 反向 reverse_iterator
- const 反向 const_reverse_iterator
iteratior像指针一样的类型,但不一定是指针
只是用法像指针
区间是左闭右开:
[s.begin() , s.end())
不是传值返回而是引用返回,好处:可读可修改
减少拷贝
修改返回对象
std::string::at
at与[]的功能一致
区别:越界之后会抛异常
虽然迭代器是通用类型,但是string中会更喜欢用[]
2.迭代器 遍历 内嵌类型
四种:
正向 iteratior
const 正向 const_iteration
反向 reverse_iterator
const 反向 const_reverse_iterator
iteratior像指针一样的类型,但不一定是指针
只是用法像指针
区间是左闭右开
[s.begin() , s.end())
srting 不喜欢用iteratior,因为[]更好用
vector 不喜欢用iteratior,因为[]更好用
list/map/set…只能用迭代器访问
iteratior是所有容器通用的访问方式,用法都是类似的
void test_string5()
{
string s("hello");
string::reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
PrintString(s);
}
3. 范围for 智能
但底层就是迭代器
只能正向
void test_string6()
{
// 范围for -- 自动迭代,自动判断结束
// 依次取s中每个字符,赋值给ch
/*for (auto ch : s)
{
ch++;
cout << ch << " ";
}*/
for (auto& ch : s)//加了&引用才能修改
{
ch++;
cout << ch << " ";
}
cout << endl;
cout << s << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
// 范围for底层其实就是迭代器
}
2.2.4. string类对象的修改操作
- 插入
push_back 单个 如果空间不够就扩容
append 多个
++最好用 都行 底层其实就是push_back
void test_string6()
{
string s("hello");
s.push_back('-');
s.push_back('-');
s.append("world");
cout << s << endl;
string str("我来了");
s += '@';
s += str;
s += "!!!";
cout << s << endl;
s.append(++str.begin(), --str.end());
cout << s << endl;
//string copy(++s.begin(), --s.end());
string copy(s.begin() + 5, s.end() - 5);
cout << copy << endl;
} test_string5()
{
string s("hello");
string::reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
PrintString(s);
}
- insert 插入
erase 删除
repace 替换
空格后插入20%
void test_string8()
{
string str("wo lai le");
for (size_t i = 0; i < str.size();)
{
if (str[i] == ' ')
{
str.insert(i, "20%");
i += 4;
}
else
{
++i;
}
}
cout << str << endl;
}
将空格替换成20%
- 先插再删(效率不高)
- 双指针
- 以空间换时间
- replace
void test_string8()
{
//1.
for (size_t i = 0; i < str.size(); ++i)
{
if (str[i] == ' ')
{
str.insert(i, "20%");
i += 3;
}
}
cout << str << endl;
for (size_t i = 0; i < str.size(); ++i)
{
if (str[i] == ' ')
{
str.erase(i, 1);
}
}
cout << str << endl;
//3.先遍历字符串
string newstr;
for (size_t i = 0; i < str.size(); ++i)
{
if (str[i] != ' ')//不是空格
{
newstr += str[i];//+= 字符串后加字符串
}
else//是空格
{
newstr += "20%";
}
}
cout << newstr << endl;
}
- c_str 兼容c的接口
返回值是 const char*
以c的形式读文件
void test_string9()
{
string filename("test.cpp");
cout << filename << endl;
cout << filename.c_str() << endl;
FILE* fout = fopen(filename.c_str(), "r");
assert(fout);
char ch = fgetc(fout);
while (ch != EOF)
{
cout << ch;
ch = fgetc(fout);
}
}
filename 与 filename.c_str()的区别
+= 的结果不一样 底层有区别
void test_string10()
{
string filename("test.cpp");
cout << filename << endl;
cout << filename.c_str() << endl;
filename += '\0';
filename += "string.cpp";
cout << filename << endl; // string 对象size为准
cout << filename.c_str() << endl; // 常量字符串对象\0
cout << filename.size() << endl;
string copy = filename;
cout << copy << endl << endl;
for (unsigned char ch = 0; ch < 128; ++ch)
{
cout << ch;
}
cout << endl;
cout << "\\0" << endl;
}
data与c_str 一样 但是习惯用后者
find
查找
默认从0开始找,但也可以从中间某个位置找
-
找后缀
substr
不用算长度 不给长度就有多少找多少
rfind
适合用来取最后一个后缀 -
分割网址
网址(url):3部分构成
协议名(protocol) + 域名(domain) + 路径(uri)
void DealUrl(const string& url)
{
size_t pos1 = url.find("://");
if (pos1 == string::npos)
{
cout << "非法url" << endl;
return;
}
string protocol = url.substr(0, pos1);
cout << protocol << endl;
size_t pos2 = url.find('/', pos1 + 3);
if (pos2 == string::npos)
{
cout << "非法url" << endl;
return;
}
string domain = url.substr(pos1 + 3, pos2 - pos1 - 3);
cout << domain << endl;
string uri = url.substr(pos2 + 1);
cout << uri << endl << endl;
}
void test_string11()
{
string filename("test.cpp.tar.zip");
// 后缀
//size_t pos = filename.find('.');
size_t pos = filename.rfind('.');
if (pos != string::npos)
{
//string suff = filename.substr(pos, filename.size() - pos);
string suff = filename.substr(pos);
cout << suff << endl;
}
string url1 = "https://cplusplus.com/reference/string/string/";
string url2 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=ascall&step_word=&hs=0&pn=0&spn=0&di=7108135681917976577&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=undefined&cs=2613959014%2C543572025&os=2740573600%2C1059518451&simid=2613959014%2C543572025&adpicid=0&lpn=0&ln=179&fr=&fmq=1660115697093_R&fm=&ic=undefined&s=undefined&hd=undefined&latest=undefined©right=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fimg.php.cn%2Fupload%2Fimage%2F147%2F157%2F796%2F1593765739620093.png%26refer%3Dhttp%3A%2F%2Fimg.php.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1662707704%26t%3Da68cb238bbb3f99d0554098c785d526e&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Brir_z%26e3BvgAzdH3FuwqAzdH3F9c9amd_z%26e3Bip4s&gsm=1&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDIsNCw2LDEsNSw3LDgsOQ%3D%3D";
string url3 = "ftp://ftp.cs.umd.edu/pub/skipLists/skiplists.pdf";
DealUrl(url1);
DealUrl(url2);
DealUrl(url3);
}
find_first(last)_of 搜索第一个匹配的字符
与find的区别:find 全部匹配 of 只要出现的任意一个字符就返回
加餐
:
Functions
转型
void test_string12()
{
int ival;
double dval;
cin >> ival >> dval;
string istr = to_string(ival);
string dstr = to_string(dval);
cout << istr << endl;
cout << dstr << endl;
istr = "9999";
dstr = "9999.99";
ival = stoi(istr);
dval = stod(dstr);//有些浮点数无法精确存储,所以结果会有些不一样
}
2.2.5 string类非成员函数
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operators | 大小比较 |