在C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要自己管理,稍有不慎可能还会越界访问。为了让字符串操作符合面向对象的思想,C++标准模板库定义了string类。
标准库中的string类
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似标准字符容器的接口,但添加了专门用于操作单字节字符串的设计特性
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型)
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数
- 这个类独立于所使用的编码来处理字节
PS:使用string类要
using namespace std
总结:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,
typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字节或者边长字符的序列
string类的常用接口说明
1. string类对象的常见构造
函数名称 | 功能说明 |
---|---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用c语言的字符串来构造string类对象 |
string(size_t n, char c ) | string类对象中包含n个字符串c |
string(const string& s) | 拷贝构造函数 |
1 #include<iostream>
2 #include<string>
3 using namespace std;
4 int main(){
5
6 string str1;
7 string str2("asdfasdf");
8 string str3(10, 'c');
9 string str4(str2);
10 std::cout << str1 << std::endl;
11 std::cout << str2 << std::endl;
12 std::cout << str3 << std::endl;
13 std::cout << str4 << std::endl;
14 return 0;
15 }
2. string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串是否为空字符串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 为字符串预留空间 |
resize | 将有效字符的个数改成n个,多出的空间用字符c填充 |
1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 int main(){
6
7 string str("hello world");
8 cout <<"str的有效字符个数:" << str.size() << endl;
9 cout << "str的有效字符个数:" << str.length() << endl;
10 cout << "str的总空间大小:" << str.capacity() << endl;
11
12 // string类对象支持直接使用cin和cout进行输入和输出
13 cout << str << endl;
14
15 // 将str中有效字符串清空,注意清空时只是将size清0,不改变底层空间的大小
16 str.clear();
17 cout <<"使用clear函数之后的size:" << str.size() << endl;
18 cout << "使用clear函数之后的总空间大小 " << str.capacity() << endl;
19
20 // 将str中有效字符个数增加到15个,多出位置用't'进行填充
21 str.resize(10, 't');
22 cout <<"使用resize(10, \'t\')的size:" << str.size() << endl;
23 cout << str.capacity() << endl;
24 cout << str << endl;
25
26 // 将str中有效字符个数增加到15个,多出的位置用缺省值'\0'进行填充
27 str.resize(15);
28 cout << "resize(15)" << endl;
29 cout << str.size() << endl;
30 cout << str.capacity() << endl;
31 cout << str << endl;
32
33 // 将str中有效字符个数缩小到5个
34 str.resize(5);
35 cout << "resize(5)" << endl;
36 cout << str.capacity() << endl;
37 cout << str << endl;
38 return 0;
39 }
看到这里,或许会有小伙伴会问,如果使用reserve函数,是否会改变string中有效元素个数呢?reserve参数小于string的底层空间大小时,是否会将空间缩小呢?在不同的编译器有不同结果
1 #include<iostream>
2 #include<string>
3 using namespace std;
4 int main(){
5
6 string str;
7 // 测试reserve是否会改变string中有效元素个数
8 str.reserve(100);
9 cout << str.size() <<endl;
10 cout << str.capacity() <<endl;
11
12 // 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
13 str.reserve(50);
14 cout << "参数小于底层空间大小" << endl;
15 cout << str.size() << endl;
16 cout << str.capacity() << endl;
17 return 0;
18 }
- size()与length()方法底层实现原理完全相同,引入size()的原因时是为了与其他容器的接口保持一致,一般情况下都是用size()
- clear()只是将string中有效字符串清空,不改变底层空间大小
reserve(size_t n)
与resize(size_t n, char c)
都是字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)
用0来填充多出的元素空间,resize(size_t n, char c)
用字符串c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果元素个数减少,底层空间总大小不变reserve(size_t rcs_arg = 0)
:为string预留空间,不改变有效元素个数,当reserve的参数小于string底层空间总大小时,在vs中不会改变容量大小,在g++里面会改变容量大小
3. string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[] | 返回pos位置的字符,const string类对象调用 |
begin + end | begin获取字符串开头的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | rbegin指向字符串开始的反向迭代器 + 指向字符串反向末端的方向迭代器 |
范围for | c++11支撑更加简洁的范围for的新遍历方式 |
1 #include<iostream>
2 #include<string>
3 using namespace std;
4 int main(){
5 string str("helloc world");
6 const string s2("helloc ybb");
7 cout << s1 << " " << s2 << endl;
8 cout << s1[0] << " " << s2[0] << endl;
9 s1[0] = 'T';
10 cout << s1 << endl;
11 // s2[0]='t'; 代码编译失败,因为const类型对象不能修改
12 return 0;
13 }
string类的遍历方式有三种,一种就是用string重载的[]
来进行下标访问,用for循环遍历。第二种就是使用迭代器来遍历。第三种,是使用范围for来遍历。
1 #include<iostream>
2 using namespace std;
3 int main()
4 {
5
6 string str("hello ybb!!");
7 // 使用重载的[],通过访问下标来遍历string
8 for(size_t i = 0; i < str.size();++i){
9 cout << s[i] << endl;
10 }
11
12 // 使用迭代器来遍历string
13 string::iterator it = str.begin();
14 while(it != str.end()){
15 cout << *it << end;
16 ++it;
17 }
18
19 // 使用迭代器反向遍历string
20 string::reverse_iterator rit = str.rbegin();
21 while(rit != str.rend())
22 {
23 cout << *rit <<endl;
24 ++rit;
25 }
26
27 // 使用范围for来遍历字符串
28 for(auto e : s){
29 cout << ch << endl;
30 }
31 return 0;
32 }
4. string类对象的修改操作
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符 |
append | 在字符串后追加一个字符串 |
operator+= | 在字符串后 追加字符串 |
c_str | 返回C格式的字符串 |
find + npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
1 #include<iostream>
2 using namespace std;
3
4 int main(){
5
6 string str("hello");
7 str.push_back('T'); // 在str后插入一个字符T
8 str.append(" ybb"); // 在str后追加一个字符串“ ybb”
9 str += 'b'; // 在str后追加一个字符'b'
10 str += " tz"; // 在str后追加一个字符串" tz"
11 cout << str << endl;
12 cout << str.c_str() << endl; // 以c语言的方式打印字符串
13
14 // 获取file的后缀
15 string file("test.txt");
16 size_t pos = file.rfind('.'); // 获取'.'的位置
17 string suffix(file.substr(pos, file.size()-pos)); // 用从pos开始,
截取file.size() - pos 个字符的子串
18 cout << suffix << endl;
19
20 // npos是string里面的一个静态成员变量
21 // static const size_t npos = -1;
22
23
24 // 做一个小测试,取出url中域名
25 string url("https://cplusplus.com/reference/string/string/");
26 cout << url << endl;
27 size_t start = url.find("://");
28 if(start == string::npos)
29 {
30 cout << "invalid url" <<endl;
31 return 0;
32 }
33
34 start += 3;
35 size_t finish = url.find('/',start);
36
37 string addr = url.substr(start, finish - start);
38
39 cout << addr << endl;
40
41 // 删除协议前缀
42 url.erase(0, start);
43 cout << url << endl;
44
45 return 0;
46 }
47
注意:
- 在string尾部追加字符时,
str.push_back('c')
和str.append(1,'c')
和str+='c'
三种方式差不多,但一般情况下string类使用+=
操作比较多,+=
不仅可以连接单个字符,还可以连接字符串- 对string操作时,如果能够大概预估到方多少字符,可以先通过reserve把空间预留好
5 return 0;
46 }
47
``