1.初识string类
1)为什么c++不使用c语言中的字符串?
C语言中字符串是以’\0’结尾的一些字符的集合,对这些字符串的处理C语言使用strcpy等函数,但是这些函数与字符串是分离开的,不太符合面向对象的思想,而且底层空间需要用户自己管理,也可能用户使用时造成越界访问。
2)string类基本了解
- string类是一个字符串的类。
- string类的接口与其他容器的接口基本相同,并添加了一些专门用来操作string类的接口。
- string在底层实际是:
basic_string
模板类的别名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字节或者变长字符的序列
- 使用时必须包含
#include<string>
与using namecpace std;
2.string类的常用接口
1)string类对象的常见构造
a.常用构造了解
函数名称 | 功能 |
---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n,char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
string(const string&s,size_t n) | 用s中的前n个字符构造新的string类对象 |
b.常用构造测试
void test1()
{
string s1;//构造空的string类对象
string s2("hello");//用C-string来构造string类对象
string s3 = "world";//explicit 隐式转换
string s4(s2);//拷贝构造
string s5(10, 'a');// 用10个字符'a'构造string类对象s35
string s6(s5, 5);//用s5的前5个字符构成string对象s6
cout << s1 << endl;//输出空格
cout << s2 << endl;//hello
cout << s3 << endl;//world
cout << s4 << endl;//hello
cout << s5 << endl;//aaaaaaaaaa
cout << s6 << endl;//aaaaa
}
2)string类对象的容量操作
a.容量操作了解
函数名称 | 功能 |
---|
size_t size() const | 返回字符串有效字符长度 |
bool empty ( ) const | 检测字符串释放为空串,是返回true,否则返回false |
size_t length() const | 返回字符串有效字符长度 |
void clear() | 清空有效字符 |
size_t capacity ( ) const | 返回空间总大小 |
void resize ( size_t n, char c ) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
| |
void resize ( size_t n ) | 将有效字符的个数改成n个,多出的空间用0填充 |
| |
void reserve ( size_t res_arg=0 ) | 为字符串预留空间 |
| |
b.容量操作测试
void resize()
{
// 注意:string类对象支持直接用cin和cout进行输入和输出
string s("hello");
cout << s << endl;//hello
cout << s.length() << endl;//5
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//15 实质上是16个char的空间因为'\0'的存在
s.clear();//不改变容量的大小
cout << s.size() << endl;//0
cout << s.capacity() << endl << endl;//15
s.resize(12,'b');
cout << s << endl;//bbbbbbbbbbbb
cout << s.size() << endl;//12
cout << s.capacity() << endl<<endl;//15
s.resize(18);
cout << s.size() << endl;//18
cout << s.capacity() << endl << endl;//31
s.resize(10);
cout << s << endl;//bbbbbbbbbb
cout << s.size() << endl;//10
cout << s.capacity() << endl << endl;//31
}
void reserve()
{
string s("hello");
cout << s << endl;//hello
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//15
// 测试reserve是否会改变string中有效元素个数
s.reserve(200);
cout << s.size() << endl;//5 不算“\0” 不会改变
cout << s.capacity() << endl << endl;//207
// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
s.reserve(100);
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//207 不会缩小
s.reserve(15);
cout << s.size() << endl;//5 不算“\0”
cout << s.capacity() << endl << endl;//15 因为原始空间为15
}
c.注意事项
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不会改变容量大小。
d.可以使用reserve提高插入数据时的效率,避免增容的开销。
void test1()
{
//利用reserve提高插入数据的效率,避免增容带来的开销
//事先声明好reserve
string s;
s.reserve(1);
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s += 'c';
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
cout << s << endl;
}
运行结果如下,上述代码每次都需要增容。
可修改为如下:
void test2()
{
//利用reserve提高插入数据的效率,避免增容带来的开销
//事先声明好reserve
string s;
s.reserve(100);
size_t sz = s.capacity();
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s += 'c';
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
cout << s << endl;
}
3)string类对象的访问操作
a.访问接口了解
函数名称 | 功能 |
---|
char& operator[](size_t pos) | 返回pos位置的字符,const string类对象调用 |
const char& operator[](size_t pos)const | 返回pos位置的字符,非const string类对象调用 |
b.测试
void test6()
{
string s1("hello bit");
cout << s1 << endl;//hello bit
s1[0] = 'H';
s1[6] = 'B';
cout << s1 << endl;//Hello Bit
const string s2("Hello Bit");
// s2[0] = 'h'; // 代码编译失败,因为const类型对象不能修改
}
4)string类对象的修改操作
a.接口了解
函数名 | 功能 |
---|
void push_back(char c) | 尾插字符c |
| |
string& append (const char* s); | 在字符串后追加一个字符串 |
| |
string& operator+=(const string& str) | 在字符串后追加字符串str |
| |
string& operator+=(const char* s) | 在字符串后追加C个数字符串 |
| |
string& operator+=(char c) | 在字符串后追加字符c |
| |
const char* c_str( )const | 返回C格式字符串 |
| |
size_t find (char c, size_t pos = 0)const | 从pos往后找c,返回c的位置 |
size_t rfind(char c, size_t pos = npos) | 从pos往前找c,返回c的位置 |
string substr(size_t pos = 0, size_t n = npos)const | 从pos位置截取n个位置子串并返回 |
b.接口测试
void test1()
{
//1
string s1("HELLO");
s1.push_back(' ');
s1.append( "world");
cout << s1 << endl;//HELLO world
//2
string s2("HELLO");
s2 += ' ';
s2 += "world";
string s3("!!!");
s2 += s3;
cout << s2 << endl;//HElLO world!!!
cout << s2.c_str() << endl;//HElLO world!!! 作用:当需要用c的接口实现string时
//3
string num("111");
int valuse = atoi(num.c_str());
cout << valuse << endl;//111
//4
string s2("HELLO");
s2 += ' ';
s2 += "world";
string s3("!!!");
s2 += s3;
s2.insert(s2.begin(), 'x');
s2.insert(0, "bit");
cout << s2 << endl;//bitxHELLO world!!!
}
void test2()
{
//获取后缀
string file("test.cpp");
size_t pos = file.rfind(".");
// npos是string里面的一个静态成员变量
// static const size_t npos = -1;
if (pos == string::npos)
{
cout << "文件没有后缀" << endl;
}
else
{
cout << file.substr(pos, file.size() - pos) << endl;//.cpp
}
//取出url的域名
string url("https://hao.360.cn/");
size_t start = url.find("://");
if (start == string::npos)
{
cout << "url invalid" << endl;
return;
}
start += 3;
size_t finish = url.find('/', start);//size_t 因为值可能大,find找不到就返回-1
cout << url.substr(start, finish - start) << endl;//hao.360.cn
//删除url的前缀
pos = url.find("://");
url.erase(0, pos + 3);
cout << url << endl;//hao.360.cn/
}
c.注意事项
- 在string尾部追加字符时,s.push_back(c)/ s.append(1, c) / s += 'c’三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
5)string类的一些非成员函数
函数 | 功能 |
---|
operator+ | 一个字符串加一个字符串需要另一个字符串接受并返回(不建议使用,因为效率低) |
operator>> | 输入运算符重载 |
| |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operators | 大小比较 |
6)string类对象的三种访问方式
a.迭代器的方式
//迭代器有普通版本 反向版本 const版本
//迭代器给出了统一的方式去访问容器,屏蔽了底层复杂的细节。
//迭代器底层类似于指针,是string的内部类型。
//string::iterator是类型 it是对象。
//begin是指向第一个数据位置的迭代器 "1"。
//end是指向最后一个数据的下一个位置的迭代器 “\0"。
//左闭右开
//普通版本
void test2()
{
string num("1234");
string::iterator it = num.begin();
int value = 0;
while (it != num.end())
{
value *= 10;
value += *it - '0';
++it;
}
cout << value << endl;
}
// const迭代器
int StrToNum1(const string& str)
{
int value = 0;
string::const_iterator it = str.begin();
while (it != str.end())
{
//读数据
//cout << *it << " ";
//++it;
value *= 10;
value += *it - '0';
++it;
}
return value;
}
//反向迭代器
void test3()
{
string num = "1234";
string::reverse_iterator it = num.rbegin();
while (it != num.rend())
{
//读数据
cout << *it << " ";
++it;
//修改数据
//(*it) += 1;
//++it;
}
cout << endl;
cout << StrToNum3(num) << endl;
}
b.for+下标
void test()
{
string s("hello");
for (size_t i = 0; i < str.size(); i++)
{
cout<<s1[i]<" ";
}
cout<<endl;
}
c.auto形式
void test()
{
string s("hello");
int value = 0;
for (auto e : s)
{
value *= 10;
value += (e - '0');
}
cout << value<<endl;
}