1、 C语言中的字符串
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,
但是这些库函数与字符串是分离开的,不太符合程序设计的思想,而且底层空间需要用户自己管理,稍不留神可
能还会越界访问
2. 标准库中的string类
(注意:本文是在VS2019下操作学习,不同环境下一些结论可能不成立)
2.1 string类
几个注意点:
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作
3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
string;
4. 不能操作多字节或者变长字符的序列。
使用string类时,必须包含#include头文件以及using namespace std;
注意没有 .h 为 include<string>
2.2 string类的常用接口说明
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>
using namespace std;
#include<string>
int main()
{
string s1;
string s2("hello");
string s3(s2);//拷贝构造
string s4(10, 'A');
//十个A
cout << s2 << endl;
cout << s2 << endl;
cout << s4 << endl;
return 0;
}
2. string类对象的容量操作
函数名称 | 功能说明 |
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间** |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
代码举例:
size\length\capacity\empty
void TestString2()
{
string s("hello");
cout << s.size() << endl;
cout << s.length() << endl;
cout << s.capacity() << endl;
if (!s.empty())
{
cout << "s is not empty():"<<s << endl;
}
else
{
cout << "s is empty()"<<endl;
}
//将s中有效字符清空
s.clear();
cout << s.size() << endl;
}
int main()
{
TestString2();
return 0;
}
reserve
void TestString3()
{
string s("hello");
cout << s.size() << endl;
cout << s.capacity() << endl;
s.reserve(20);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.reserve(100);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.reserve(80);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.reserve(15);
cout << s.size() << endl;
cout << s.capacity() << endl;
}
通过reserve将string底层空间扩大,假设string底层旧空间大小为oldcapacity
如果newcapacity大于oldcapacity,空间确实会变大。
string类内部会按照一定机制跟进用户所传递的容量去计算新的容量
扩容机制:开辟新空间,拷贝元素,释放旧空间
但是:如果new<old && new>=16时,reserve会忽略本次操作,不会扩容缩小
当new<16:reserve会将底层空间大小缩为15
问题:为什么new<16时,reserve才会真正将空间缩小?
因为在string对象内部包含了一个固定大小数组--》char buff[16]
原因:绝大多数情况下的字符串中的有效字符个数都是小于16 如人名等
直接使用固定大小的数组,不从堆上开辟空间可以让string类效率更高,
当有效元素超过16时,string类才真正从堆上开辟空间,从堆上开辟空间是有时间消耗,所以string类从堆上申请成功之后一般不会将空间缩小
resize
resize(size_t newsize, char ch='\0'):将有效元素个数修改到newsize,如果newsize大于string对象中原有的字符个数多出的字符就用ch填充
void TestString3()
{
string s("hello");
s.resize(10, 'A');
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(20, 'B');
s.resize(40, 'C');
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(20);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(17);
s.resize(16);
cout << s.size() << endl;
cout << s.capacity() << endl;
s.resize(15);
s.resize(5);
cout << s.size() << endl;
cout << s.capacity() << endl;
}
当newsize小于string对象中有效元素个数时,将有效元素个数缩小到newsize个,缩小时不会改变容量
其他注意:
1. size()与length()方法底层实现原理完全相同,引入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不会改变容量大小
3. string类对象的访问及遍历操作
operator[]
越界检测操作
at也是访问任意位置元素的,处理越界方式不一样
begin+ end
begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
4. string类对象的修改操作
push_back (char ch):往string尾部插入ch
append 和operator+= :在string中拼接单个字符,C格式的字符串,string对象
void TestString3()
{
string s("hello");
s.push_back(' ');//等价于s.append(1,' ');
//拼接C格式字符串
s += "world";//等价于 s.append("world");
//拼接string对象
string ss("zx");
s += ss;//等价于 s.append(ss);
s += 'A';
cout << s << endl;
}
注意:
1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般
情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
insert
void TestString3()
{
string s("hello");
//在0号位置插入3个A
s.insert(0, 3, 'A');
cout << s << endl;
s.insert(1, "bite");
cout << s << endl;
}
erase删除
void TestString3()
{
string s("hello world");
//0号位置删6个
s.erase(0, 6);
cout << s << endl;
//begin此时在w位置,+2在r位置,删掉
s.erase(s.begin() + 2);
cout << s << endl;
s.erase(s.begin(), s.end());//等价于全部删除了
cout << s << endl;
}
此处所能用的各种功能函数还有很多,不再一一举例,使用时搜索即可。
一些操作
void TestString3()
{
string s("12435234");
cout << s << endl;
//逆置s
reverse(s.begin(), s.end());
cout << s << endl;
//排序
sort(s.begin(), s.end());
cout << s << endl;
}