本文主要介绍string的常见的接口的使用
文章目录
- 一、什么是string类
- 二、string类的使用
- 1、string类对象的常见构造
- 2、string类对象的容量操作
- 3、string类对象的访问及遍历操作
- ①operator[ ]的用法
- ②迭代器
- ③范围for
- 4、string类对象的修改操作
- ①push_back
- ②append
- ③operator+=(这是最常用的一个函数)
- ④c_str
- ⑤find函数
- ⑥rfind
- ⑦substr
- ⑧利用find取出网站的域名以及删除前缀
- ⑨利用replace和find替换字符
- ⑩swap
- 5、string类非成员函数
- getline
- 6、其他函数
- ① stoi——字符串转为整型
- ② to_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。
二、string类的使用
1、string类对象的常见构造
函数名称 | 功能 |
---|---|
string s; | 构造空的string类对象,即空字符串 |
string s1(const char* s); | 复制s指向的字符串 |
string s2(size_t n,char c); | 生成n个c字符的字符串 |
string s3(const string&s); | 用string类对象拷贝构造 |
具体使用:
string s; // 构造空的string类对象s
string s2("hello");//复制hello字符串给s2对象
string s3(10, 'x');//生成10个x的字符串
string s4(s2); //用s2拷贝构造对象s4
2、string类对象的容量操作
函数 | 功能 |
---|---|
size | 返回字符串有效个数 |
length | 返回有效字符串个数 |
capacity | 返回总空间的大小 |
empty | 判断字符串是否为空 |
clear | 清空字符串 |
reserve | 为字符串预留空间(有点像malloc) |
resize | 将有效的字符个数改成n个,多出的空间用字符c填充(有点像calloc) |
具体使用
void test2()
{
string s("hello");
cout <<"size:"<< s.size() << endl;
cout <<"length:"<< s.length() << endl;
cout <<"capacity:"<< s.capacity() << endl;
cout <<"empty:"<< s.empty() << endl;
}
结果如下:
对于clear函数,只是将s中的字符串清空,size置为0,但是不改变底层空间capacity的大小
void test2()
{
string s("hello");
cout <<"size:"<< s.size() << endl;
cout <<"length:"<< s.length() << endl;
cout <<"capacity:"<< s.capacity() << endl;
cout <<"empty:"<< s.empty() << endl;
cout << "----------" << endl;
s.clear();
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << "empty:" << s.empty() << endl;
}
我们再来详细看看resize:
void resize (size_t n);
void resize (size_t n, char c);
①按照给定的字符扩容
void test3()
{
string s("hello");
s.resize(10, 'x');//将s中的有效字符个数增加到10个,后面多出的位置用x填充
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << s << endl;
}
结果如下:
②默认给定的“\0”来填充扩容
void test4()
{
string s("hello");
s.resize(15);//将s中的有效字符个数增加到15个,后面多出的位置用"\0"填充
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << s << endl;
}
结果如下:
③当是缩小大小时,会截断字符
void test5()
{
string s("hello");
s.resize(3);//将s中的有效字符个数减少到3个,只剩下原来的前三个
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << s << endl;
}
结果如下:
我们再来详细看看reserve:
void reserve (size_t n = 0);
①当n大于当前对象的容量时,将容量扩大到n或者大于n
void test6()
{
string s;
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << "----------------" << endl;
s.reserve(100);//将原来的capacity扩大到100
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
}
结果如下:
②当n小于容量时,不会缩小到容量
void test6()
{
string s;
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << "----------------" << endl;
s.reserve(100);//将原来的capacity扩大到100
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
cout << "----------------" << endl;
s.reserve(20);//不会将之前的capacity缩小
cout << "size:" << s.size() << endl;
cout << "capacity:" << s.capacity() << endl;
}
结果如下:
我们可以看到容量并没有缩小!!!
总结:
① 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的底层空间总大小时,reserve不会改变容量大小
3、string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[] (重点) | 返回pos位置的字符,const string类对象调用 |
at | 返回pos位置的字符,const string类对象调用 |
begin+ end begin | 获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend begin | 获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
operator [] (i)
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
我们知道string类字符串的底层其实是数组实现的,于是我们可以采用下标的方式进行遍历
举例说明:
void test7()
{
string s1("hello");
const string s2("HELLO");
cout << s1 << " " << s2 << endl;
cout << s1[0] << " " << s2[0] << endl;
s1[0] = 'x';
cout << s1 << endl;
//s2[0] = 'x';const类对象不能修改
}
at
char& at (size_t pos);
const char& at (size_t pos) const;
at的使用:
#include <iostream>
#include <string>
using namespace std;
int main()
{
std::string str("test string");
for (size_t i = 0; i < str.size(); ++i)
{
cout << str.at(i) << " ";
}
return 0;
}
结果展示:
总结:
operator[ ]和at函数功能相同,都是返回pos位置的字符,但是不同的是,at失败会assert断言错误,operator[ ]会抛异常
三种遍历操作:(常用)
①operator[ ]的用法
void test8()
{
string s1("hello");
string s2(s1);//拷贝构造
for (size_t i = 0; i < s2.size(); ++i)
{
cout << s2.operator[](i) << " ";
cout << s2[i] << " ";
}
cout << endl;
for (size_t i = 0; i < s2.size(); ++i)
{
s2[i] += 1;//支持修改
}
cout << s2 << endl;
}
②迭代器
注意:这里暂时将迭代器想象成像指针一样的类型
void test9()
{
string s("hello");
//迭代器的用法
//string::iterator it = s.begin();
auto it = s.begin();// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
while (it !=s.end())
{
cout << *it << endl;
++it;
}
}
③范围for
其实范围for的底层是将它给处理成了迭代器
void test10()
{
string s("hello");
for (auto ch : s)
cout << ch << endl;
//把s中的每个字符取出来,赋值给ch,不需要++,判断结束,自动往后迭代
}
4、string类对象的修改操作
函数 | 功能 |
---|---|
push_back | 在字符串后面尾插字符c |
append | 在字符串后面追加字符串 |
operator+= | 在字符串后面追加字符串 |
c_str | 返回C格式字符串(C形式的字符串打印时,遇到\0就停止了) |
find+npos | 从字符串pos位置开始,往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始,往前找字符c,返回该字符在字符串中的位置 |
substr | 在字符串中,从pos位置开始,截取n个字符,然后将其返回 |
replace | 将指定位置替换为自己想要的 |
swap | 交换两个两个字符串(直接交换指针,效率更高) |
注意:
- 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般
情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
①push_back
void push_back (char c);
void test11()
{
string s("xxx");
cout << s << endl;
s.push_back('H');
s.push_back('E');
s.push_back('L');
s.push_back('L');
s.push_back('O');
cout << s << endl;//将HELLO依次拼接到xxx后面
}
结果如下:
②append
string& append (const string& str);
string& append (const char* s);
void test12()
{
string s1("hello");
string s2("C++");
s1.append(s2);//直接拼接一个对象字符串
cout << s1 << endl;
s1.append("haha");//直接拼接一个字符串
cout << s1 << endl;
}
结果展示:
③operator+=(这是最常用的一个函数)
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
void test13()
{
string s1("hello");
string s2("world");
s1 += s2;
cout << s1 << endl;
s1 += "xxx";
cout << s1 << endl;
s1 += 'A';
cout << s1 << endl;
}
结果如下:
④c_str
const char* c_str() const;
void test14()
{
string s("hello");
//以语言的格式打印字符串
cout << s.c_str() << endl;
}
结果如下:
⑤find函数
注意:static const size_t npos = -1;//其实npos就是size_t的最大值
size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (const char* s, size_t pos, size_t n) const;
size_t find (char c, size_t pos = 0) const;
void test15()
{
string s1("http://www.cplusplus.com/reference/string/string/find/");
size_t pos1 = s1.find("com");//正向查找com字符串
cout << pos1 << endl;
string s2("http");
size_t pos2 = s1.find(s2);//正向查找与s2对象匹配的字符串
cout << pos2<< endl;
size_t pos3 = s1.find('p');//正向查找字符p
cout << pos3 << endl;
}
结果展示:
⑥rfind
size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos, size_t n) const;
size_t rfind (char c, size_t pos = npos) const;
void test16()
{
string s1("http://www.cplusplus.com/reference/string/string/find/");
size_t pos1 = s1.rfind("string");//反向查找是s1中第一次出现的string的首位置
cout << pos1 << endl;
string s2("cplusplus");
size_t pos2 = s1.rfind(s2);//反向查找s1中与s2对象匹配的首位置
cout << pos2 << endl;
size_t pos3 = s1.rfind('h'); //反向查找s1中h字符
cout << pos3 << endl;
}
结果展示:
⑦substr
string substr (size_t pos = 0, size_t len = npos) const;
void test17()
{
//获取file的后缀
string file("string.cpp");
size_t pos = file.rfind('.');//反向查找 . 点
string suffix(file.substr(pos,file.size()-pos));//从点开始,包括点的后面部分
cout << suffix << endl;
}
结果:
string& replace (size_t pos, size_t len, const string& str);
string& replace (size_t pos, size_t len, const string& str);
string& replace (size_t pos, size_t len, size_t n, char c);
⑧利用find取出网站的域名以及删除前缀
void test()
{
// 取出url中的域名
string url("http://www.cplusplus.com/reference/string/string/find/");
cout << url << endl;
size_t start = url.find("://");
if (start == string::npos)
{
cout << "invalid url" << endl;
return;
}
start += 3;
size_t finish = url.find('/', start);
string address = url.substr(start, finish - start);
cout << address << endl;
// 删除url的协议前缀
size_t pos = url.find("://");
url.erase(0, pos + 3);
cout << url << endl;
}
结果如下:
⑨利用replace和find替换字符
面试题:将这个字符串的空格替换为%%20.
`“hello world I love ereryday!”
方法1:找到空格后替换
int main()
{
string s1 = "hello world I love you!";
//我们提前算好我们的空格,然后提前开空间
size_t num = 0;
for (auto ch : s1)
{
if (ch == ' ')
{
++num;
}
}
//提前开空间,避免replace时扩容
s1.reserve(s1.size() + 3 * num);
//第二个参数是缺省值=0,就是从开始的地方找
size_t pos = s1.find(' ');
while (string::npos != pos)
{
s1.replace(pos, 1, "%%20");
//在pos之后第四个位置开始找
pos = s1.find(' ',pos+4);
}
cout << s1 << endl;
}
方法2:空间换时间,创建新的string对象,遍历原字符串,不是空格就尾插字符,是空格就尾插%%20
//第二种方法以空间换时间
int main()
{
string s1 = "hello world I love you!";
//我们提前算好我们的空格,然后提前开空间
string tmp;
for (auto ch : s1)
{
if (ch != ' ')
{
tmp += ch;
}
if (ch == ' ')
{
tmp += "%% 20";
}
}
s1 = tmp;
cout << s1 << endl;
}
⑩swap
在我们的string中我们也有一个交换函数。库里面也有一个swap。
我们使用string中swap效率更高。
因为在我们的string中可以直接交换指针,指向不同的字符串。
而库中的swap,我们还要用一个临时变量去交换。
int main()
{
string s1("abcd");
string s2("xxxx");
//用string中的交换
s1.swap(s2);
cout << s1 << endl << s2 << endl;
//用库中的交换
swap(s1, s2);
cout << s1 << endl << s2 << endl;
}
5、string类非成员函数
函数 | 功能 |
---|---|
operator+ | 尽量少用,因为是传值返回,导致拷贝效率低 |
operator>> | 流插入运算符重载 |
operator<< | 流提取运算符重载 |
getline | 获取一行字符串 |
relational operators | 大小比较 |
getline
输入输出问题:
void test18()
{
string s;
cin >> s;//输入hello world
cout << s << endl;//只会打印hello
}
我们知道cin标准输入,当从键盘上面读取到空格或者\n时,会停止读取,这时候就要用到getline函数了
istream& getline (istream& is, string& str, char delim);
//从is中读取字符串,储存在str对象中,直到遇到delim界限符或者\n停止(自己指定)
istream& getline (istream& is, string& str);
//从is中读取字符串,储存在str对象中,直到遇到\n停止
演示遇到指定字符就停止读取
void test18()
{
string s1;
getline(cin,s1,'e');//输入hello world,
cout << s1 << endl;//输出h
}
演示遇到\n停止
void test18
{
string s2;
getline(cin, s2);//输入hello world
cout << s2 << endl;//输出hello world
}
总结:
以后使用getline
string s1;
getline(cin,s1);
大小比较问题:
我们的string的大小比较就和stercmp一样,是比较字符的ASCII的大小
void test19()
{
string s1("hello");
string s2("string");
cout << (s1 > s2) << endl;//输出0
}
这里的有点不一样,比较的是第一个字符的ASCII码值的大小,若第一个相等则比较第二个,返回一个bool值,以此类推。
6、其他函数
做OJ题的时候比较常用
① stoi——字符串转为整型
void test20()
{
int val = stoi("12345");
cout << val << endl;
}
② to_string——其他类型转为字符串
void test21()
{
string str = to_string(3.14);
cout << str << endl;
}