目录
string,也就是串或者字符数组,可以扩容,可以增删查改
接下来我们通过对Cplusplus的string的文档阅读来了解string的常见接口~
string类对象的默认成员函数
string类对象的构造(包括拷贝构造)
其中,常见的函数和功能如下表:
函数名称 | 功能说明 |
string() | 构造空的string类对象,即空字符串 |
string(const string&str) | 拷贝构造函数 |
string(const char* s) | 用C-string来构造string类对象 |
void test_string()
{
string s1;
string s2("hello world");
string s3(s2);
}
string类对象的赋值运算符重载
string类对象的容量操作
函数名称 | 功能说明 |
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串释放为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve (重要) | 为字符串预留空间** |
resize (重要) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
void test_string()
{
string s1("hello world hello C++");
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << s1.max_size() << endl;
}
resize(影响size和capacity)
void test_string()
{
string s1;
//s1.resize(5, '0');
s1.resize(5);
s1[4] = '3';
s1[3] = '4';
s1[2] = '5';
s1[1] = '6';
s1[0] = '7';
// 76543
// 插入(空间不够会扩容)
string s2("hello world");
s2.resize(20, 'x');
// 删除
s2.resize(5);
}
reserve(影响capacity)
void test_string()
{
string s1("111111111");
cout << s1.capacity() << endl;
s1.reserve(100);
cout << s1.capacity() << endl;
s1.reserve(20);
cout << s1.capacity() << endl;
}
注意:
1. size()与length()方法底层实现原理完全相同,length的实现是C++发展的历史原因其他容器的接口的我们都是用size()的,一般情况下基本都是用size()
2. clear()只是将string中有效字符清空,不改变底层空间大小3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变
4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小
string中元素访问及遍历
函数名称 | 功能说明 |
operator[] | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭 代器 |
rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭 代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
遍历方式1:下标+[]
void test_string()
{
string s1("hello world");
cout << s1.size() << endl;
for (size_t i = 0; i < s1.size(); i++)
{
//cout << s1.operator[](i) << " ";
cout << s1[i] << " ";
}
cout << endl;
}
void test_string()
{
const string s("hello world");
// 不可修改
//s[0] = 'x';
}
遍历方式2: 迭代器
(正向迭代器和反向迭代器,各两种)
正向迭代器
void test_string()
{
string s1("hello world");
//string::iterator it1 = s1.begin();
auto it1 = s1.begin();
while (it1 != s1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
}
void test_string()
{
const string s1("hello world");
//string::const_iterator it1 = s1.begin();
auto it1 = s1.begin();
while (it1 != s1.end())
{
// 不能修改
//*it1 += 3;
cout << *it1 << " ";
++it1;
}
cout << endl;
}
反向迭代器(反过来遍历):
void test_string()
{
string s2("hello world");
//string::reverse_iterator it2 = s2.rbegin();
auto it2 = s2.rbegin();
while (it2 != s2.rend())
{
cout << *it2 << " ";
++it2;
}
cout << endl;
}
void test_string()
{
const string s1("hello world");
//string::const_reverse_iterator cit1 = s1.rbegin();
auto cit1 = s1.rbegin();
while (cit1 != s1.rend())
{
// 不能修改
//*cit1 += 3;
cout << *cit1 << " ";
++cit1;
}
cout << endl;
}
遍历方式3: 范围for
void test_string()
//底层角度,就是迭代器
{
for (auto& e : s1)
{
cout << e << " ";
}
cout << endl;
}
string类对象的修改操作
string中插入和查找等
函数名称 | 功能说明 |
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= | 在字符串后追加字符串str |
c_str | 返回C格式字符串 |
find + npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
void test_string()
{
string s1("hello world");
cout << s1 << endl;
s1.push_back('x');
cout << s1 << endl;
s1.append(" yyyyyy!!");
cout << s1 << endl;
string s2("111111");
s1 += 'y';
s1 += "zzzzzzzz";
s1 += s2;
cout << s1 << endl;
}
接下来看一下insert和erase
void test_string()
{
string s1("hello world");
cout << s1 << endl;
// 慎用,因为效率不高 -> O(N)
// 实践中需求也不高
string s2("hello world");
s2.insert(0, "xxxx");
cout << s2 << endl;
s2.insert(0, 1,'y');
cout << s2 << endl;
s2.insert(s2.begin(), 'y');
cout << s2 << endl;
s2.insert(s2.begin(), s1.begin(), s1.end());
cout << s2 << endl;
}
void test_string()
{
string s1("hello world");
cout << s1 << endl;
// erase效率不高,慎用,和insert类似,要挪动数据
s1.erase(0, 1);
cout << s1 << endl;
//s1.erase(5);//不传
s1.erase(5, 100);//传个很大的
cout << s1 << endl;
}
下面看一下replace:
void test_string()
{
// replace效率不高,慎用,和insert类似,要挪动数据
string s2("hello world");
s2.replace(5, 1, "%20");
cout << s2 << endl;
//功能实现:把所有空格都替换成 "%20"
//方法1
string s3("hello world hello C++");
for (size_t i = 0; i < s3.size();)
{
if (s3[i] == ' ')
{
s3.replace(i, 1, "%20");
i += 3;
}
else
{
i++;
}
}
cout << s3 << endl;
//方法二
string s4("hello world hello C++");
string s5;
for (auto ch : s4)
{
if (ch != ' ')
{
s5 += ch;
}
else
{
s5 += "%20";
}
}
}
int main()
{
string url("https://legacy.cplusplus.com/reference/string/string/substr/");
size_t pos1 = url.find(':');
string url1 = url.substr(0, pos1 - 1);
cout << url1 << endl;
size_t pos2 = url.find('/', pos1 + 3);
string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));
cout << url2 << endl;
string url3 = url.substr(pos2 + 1);
cout << url3 << endl;
return 0;
}
string类非成员函数
函数 | 功能说明 |
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
int main()
{
string s1 = "hello";
string s2 = "world";
string ret = s1 + s2;
cout << ret << endl;
string ret2 = s1 + "xxxxx";
cout << ret2 << endl;
//第三种用法就是为什么要将operator+ 设置成非成员函数的原因
string ret3 = "xxxxx" + s2;
cout << ret3<< endl;
return 0;
}
最后我们来看一下getline
这个函数我们要搭配一道题来看:
这道题,我们可能会这么写:
int main()
{
string str;
cin>>str;
size_t pos = str.rfind(' ');
cout << str.size() - (pos+1) << endl;
return 0;
}
但是,这样写的结果是:
为什么?因为它遇到空格就结束了,cin或者scanf默认规定空格或者换行是多个值的分割
那么,如何才能完整的提取呢 ?
接下来我们看一下getline
它是默认遇到换行才结束,当然,这个结束符也可以由我们来 自定义
知道了getline,我们就可以将上面的代码进行修改
int main()
{
string str;
getline(cin, str);
size_t pos = str.rfind(' ');
cout << str.size() - (pos+1) << endl;
return 0;
}
总结
对于string类的常用接口使用我们就先说到这里,另外,值得在这里强调对于一些接口的使用我们可以去查文档进行理解使用。