C++:string 类
目录
一、STL
1.1、STL的基本概念
1.2、STL的六大组件
1.2.1、容器
1.2.2、算法
1.2.3、迭代器
1.2.4、仿函数
1.2.5、适配器
1.2.6、分配器
二、string 类的简单介绍
三、string类的常用操作详解
3.1、构造和初始化
3.1.1、string()
3.1.2、string(const string& str)
3.1.3、string(const string& str, size_t pos, size_t len = npos)
3.1.4、string (const char* s)
3.1.5、string(const char* s, size_t n)
3.1.6、string(size_t n, char c)
3.2、string 的析构函数
3.3、赋值符重载
3.4、string类的迭代器
3.4.1、begin
3.4.2、end
3.4.3、rbegin 和 rend
3.4.4、cbegin ,cend, crbegin, crend
3.5、stirng 类的容器
3.5.1、size 和 length
3.5.2、max_size
3.5.3、resize 和 capacity
3.5.4、reserve
3.5.5、clear
3.5.6、empty
3.5.7、shrink_to_fit
3.6、string类的元素访问
3.6.1、operator[ ]
3.6.2、at
3.6.3、back
3.6.4、front
3.7、string类的修改器
3.7.1、append
(1)string& append(const string& str)
(2)string& append(const string& str, size_t subpos, size_t sublen)
(3)string& append(const char* s)
(4)string& append(const char* s , size_t n)
(5)string& append(size_t n , char c)
3.7.3、operator+=
3.7.5、insert
3.7.6、erase
3.7.7、replace
3.7.8、swap
3.7.9、pop_back
3.8.1、c_str 和 data
3.8.2、copy
3.8.3、find
3.8.4、rfind
3.9、npos
3.10、getline
四、练习与巩固
4.1、字符串相加
4.2、翻转字符串中的单词
一、STL
1.1、STL的基本概念
STL ,全名标准模板库,不仅是一个可以重复使用的组件库,而且是一个包罗数据结构与算法的软件框架。
说人话,STL 就是 C++自己带的一个工具箱,里面提供了已经写好的数据结构和算法
1.2、STL的六大组件
STL 的六大组件(六大组成部分)为:容器,算法,迭代器,仿函数,适配器,分配器。
1.2.1、容器
作用:负责数据的存储和管理,封装了底层数据结构。
说人话,容器就是STL提供的多种箱子,用来存放不同的数据。每种箱子都有自己的特性和适用场景。
1.2.2、算法
作用:对容器中的数据进行各种操作和处理
特点:算法通过迭代器与容器交互,不依赖容器具体类型
1.2.3、迭代器
作用:提供一种统一的方法来遍历和访问容器中的元素
通俗一点,迭代器就像一个导游,带领算法访问容器中的元素
重要性:实现了容器与算法的分离,是泛型编程的关键
1.2.4、仿函数
作用:让对象具有函数的行为,增加算法的灵活性
1.2.5、适配器
作用:改变组件的接口,使其适应不同的场景
1.2.6、分配器
作用:封装内存管理的细节,为容器分配和释放内存
二、string 类的简单介绍
string类用来管理程序中的字符串,出现的时间比STL要早,SLT很多内容都是参考的string,后面STL成熟了,string也有些内容是参考STL修改的。
在C语言中,用数组存放字符串,我们需要担心是否越界,需要手动扩容和释放。但在C++中,我们有string类,它可以帮助我们完成这些事情,节省程序员的精力。
使用string类是需要头文件的: <string> ,同时也是有命名空间的 :std
三、string类的常用操作详解
3.1、构造和初始化
string的构造初始化一共有以下几种:
3.1.1、string()
这就是最原始的构造函数。举个栗子:
string s1;
cout << s1 << endl;
这里的 s1 放的是一个空字符 \0 ,打印出来就是什么也没有。其次,string类是有对 << 重载的,因此可以直接用 cout 打印
3.1.2、string(const string& str)
这是拷贝构造函数,举个例子:
string s2(s1);
string s3 = s1;
这两者都是拷贝构造调用的形式。
3.1.3、string(const string& str, size_t pos, size_t len = npos)
对str这个字符串,从位置pos开始到npos结束的这段字符复制,然后拷贝到新的字符串。例如:
string s1("Hello World!");
string s2(s1, 2, 5);
cout << s1 << endl;
cout << s2 << endl;
结果为:
注意:可以看到,从位置 2 到 5为止 拷贝,最后实际执行的是下标为 2 到下标为 6 的地方!(这与空白字符无关,实际上将空白字符用别的字符代替,最后还是会打印到w)
3.1.4、string (const char* s)
复制由字符指针指向的以空字符结尾的字符串序列。说人话,就是把char* 指向的字符串拷贝到新字符串中。举例:
char ch1[] = "Hello World!";
string s2(ch1);
cout << ch1 << endl;
cout << s2 << endl;
3.1.5、string(const char* s, size_t n)
就是把 s 指向的字符串,赋值前 n 个字符。举例:
char ch1[] = "Hello World!";
string s2(ch1, 7);
cout << ch1 << endl;
cout << s2 << endl;
3.1.6、string(size_t n, char c)
用字符 c 填充新字符串,共填充 n 次。举例:
string s1(10, '#');
cout << s1 << endl;
3.2、string 的析构函数
~string();
这个就是析构函数,没什么好说的,跳过。
3.3、赋值符重载
非常简单,直接举例:
string s1("Hello World!");
string s2;
s2 = s1;
cout << s2 << endl;
char ch1 = '%';
s2 = ch1;
cout << s2 << endl;
char ch2[] = "Hello CPlusPlus";
s2 = ch2;
cout << s2 << endl;
3.4、string类的迭代器
如图是string的迭代器大全:
迭代器是引领算法访问容量的,相当于一个指针。
3.4.1、begin
作用:返回指向字符串的第一个字符的迭代器。
例如:我现在要访问字符串的第一个字符,就可以这样:
string str = "Hello World!";
string::iterator it = str.begin();
cout << *it << endl;
注意:iterator 不是C++的关键字,而是定义在容器内部的一个指针类型别名。它的中文意思就是迭代器。iterator 在string类的内部实际上是一个 char* 类型的别名。
3.4.2、end
作用:返回字符串的最后一个字符的下一个位置,也就是 /0。因此对该迭代器进行解引用是十分危险的。大部分使用这个迭代器是为了划定字符串的边界,例如:
string s1 = "Hello World!";
string::iterator it2 = s1.end();
string::iterator it1 = s1.begin():
while(it1 != it2)
{cout << "hehe" << endl;
}
3.4.3、rbegin 和 rend
这两个迭代器的作用是从后往前遍历字符串,取第一个字符的地址和最后一个字符的地址。从原型中可以看出,这两个迭代器的类型为:reverse_iterator 。下面举个例子:
string s1 = "abcedefg";
string::reverse_iterator rbegin1 = s1.rbegin();
string::reverse_iterator rend1 = s1.rend();
cout << *rbegin1 << endl;
//cout << *rend1 << endl;
rend 指向的是字符串第一个字符的前一个位置,通常被记为 \0 ,因此不能解引用。此外,reverse_iterator 也是一种 char* 类型的别名。
3.4.4、cbegin ,cend, crbegin, crend
这几个都是前面讲的const版本(前面加了一个c),返回的类型该是iterator 还是 reverse iterator没有任何变化,对于返回的迭代器,也可以进行正常的加减操作,但是,迭代器所指向的内容是不可以被更改的!
3.5、stirng 类的容器
3.5.1、size 和 length
size 和 length都是返回字符串长度的(不带最后的斜杠0),有两个名字 size 和 length 纯粹是有些结构可以用大小来说,有些结构只能用长度来说。举例:
string s1 = "Hello World!";
cout << s1.size() << endl;
最后结果为12。
3.5.2、max_size
功能:返回字符串所能达到的最大长度。(注意是能达到的最大长度,而不是字符串现在的最大长度)
这个在实际中不怎么使用。
3.5.3、resize 和 capacity
resize:将字符串的字符个数改为 n 个(不包含斜杠0),多出来的空间用字符 c 填充。但该操作不会影响字符串的容量。例如:
string s1 = "Hello World!";
cout << s1 << endl;
s1.resize(20, '#');
cout << s1 << endl;
那如果你用resize设置的字符个数比原本的就小呢?那字符串的个数就缩小到你设置的个数(不包含斜杠0)。
capacity:返回已分配空间的大小。注意,是已分配空间的大小,而不是字符串的大小!例如:
string s1 = "a";
cout << "s1.capacity:" << s1.capacity() << endl;
string s2 = "Hello World";
cout << "s2.capacity:" << s2.capacity() << endl;
string s3 = "Hello World! Hello World!";
cout << "s3.capacity:" << s3.capacity() << endl;
结果为:15 ,15, 31
3.5.4、reserve
给字符串扩容,扩容后的字符串的容量至少为 n ,例如:
string s1 = "Hello World!";
cout << "扩容前:" << s1.capacity() << endl;
s1.reserve(20);
cout << "扩容后:" << s1.capacity() << endl;
答案为:15 , 31
那如果扩容给的 n 小于当前字符串的容量呢?这个不确定,根据编译器而定,不过一般都不会缩容的,而是保持现有容量不变。
3.5.5、clear
作用:清空一个字符串,使其变为空字符串。但是字符串的容量不变。
3.5.6、empty
作用:判断字符串是否为空,字符串为空,返回true,否则返回false
3.5.7、shrink_to_fit
作用:就是给字符串缩容。只会将字符串的容量缩小至目前所需的最小容量。例如,存放10个字符的字符串,但是容量为31,使用该函数后,容量只会缩小至15!
3.6、string类的元素访问
3.6.1、operator[ ]
有非const和const两个版本。
就是像访问数组的元素那样去访问字符串里的字符,同样,第一个字符的下标为0。例如:
string s1 = "abcdefgh";
cout << s1 << endl;
for (int i = 0; i < s1.size(); i++)
{s1[i] += 1;
}
cout << s1 << endl;
3.6.2、at
作用:返回字符串当前位置的的字符。例如:
string s1 = "abcdefgh";
cout << s1.at(2) << endl;
答案为:c
3.6.3、back
作用:范回字符串的最后一个字符的引用。例如:
string s1 = "abcdefgh";
cout << s1.back() << endl;
最后结果为: h
如果对空字符串使用back会报错!
3.6.4、front
作用:返回字符串第一个字符的引用。例如:
string s1 = "Hello World!";
s1.front() += 1;
cout << s1 << endl;
结果为:Iello World!
如果对空字符串使用会报错!
3.7、string类的修改器
3.7.1、append
(1)string& append(const string& str)
作用:在字符串后面再添加一个字符串。例如:
string s1 = "Hello World!";
string s2 = "Hello Cplusplus";
s1.append(s2);
cout << s1 << endl;
(2)string& append(const string& str, size_t subpos, size_t sublen)
作用:将第二个字符串的指定区间添加到第一个字符串的末尾。例如:
string s1 = "Hello World!";
string s2 = "Hello Cplusplus";
s1.append(s2 ,2,4);
cout << s1 << endl;
(3)string& append(const char* s)
就是把 s 指向的字符串添加到源字符串末尾。与第一个一样,只不过一个是string类型,一个是char*类型。
(4)string& append(const char* s , size_t n)
把 s 指向的字符串的前 n 个字符放到源字符串的末尾。例如:
string s1 = "Hello World!";
char ch1[] = "abcdef";
s1.append(ch1, 3);
cout << s1 << endl;
(5)string& append(size_t n , char c)
作用:在原字符串末尾添加 n 个字符 c。例如:
string s1 = "Hello World!";
s1.append(3 ,'#');
cout << s1 << endl;
3.7.2、push_back
作用:将字符 c 添加到字符串末尾。例如:
string s1 = "Hello World!";
s1.push_back('$');
cout << s1 << endl;
3.7.3、operator+=
作用:在字符串末尾添加字符,字符串。
在实际编程中,有了+=重载,append用的就非常少了,因为+=实在是太方便了。
string s1 = "Hello World!";
s1 += "abcdef";
cout << s1 << endl;
3.7.4、assign
作用:原字符串替换成参数里的字符串。例如:
string s1 = "Hello World!";
string s2 = "abcdefg";
cout << s1 << endl;
s1.assign(s2);
cout << s1 << endl;
3.7.5、insert
作用:在原字符串的指定位置添加字符串。例如:
string s1 = "Hello World!";
cout << s1 << endl;
s1.insert(2, "#");
cout << s1 << endl;
3.7.6、erase
作用:从字符串中删除某些字符。例如:
string s1 = "Hello World!";
cout << s1 << endl;
s1.erase(2, 5);
cout << s1 << endl;
3.7.7、replace
作用:将字符串从pos开始,len 个字符替换为新的字符串。例如:
string s1 = "Hello World!";
string s2 = "hahahehe";
s1.replace(2, 5, s2);
cout << s1 << endl;
3.7.8、swap
作用:将容器中的内容替换为该字符串中的内容。包括容量。
string s1 = "Hello World!";
string s2 = "hahahehe";
s2.reserve(30);
cout << s1 << " " << s1.size() << " " << s1.capacity() << endl;
s1.swap(s2);
cout << s1 << " " << s1.size() << " " << s1.capacity() << endl;
3.7.9、pop_back
作用:删除字符串的最后一个字符。不能对空字符串使用!例如:
string s1 = "Hello World!";
cout << s1 << endl;
s1.pop_back();
cout << s1 << endl;
3.8、string类的字符串操作
3.8.1、c_str 和 data
作用:返回一个C字风格类型的字符串。是将string类型转变为char*类型的桥梁。唯一不同的是c_str 转换后末尾一定是有斜杠0的,而data就不一定了。但是注意,转变成char*以后得字符串只可读不可写!举例:
string s1 = "Hello World!";
const char* ch1 = s1.c_str();
cout << ch1 << endl;
多用于使用C语言中的某些函数传参时必须使用 char* 类型的情况。
string filename = "example.txt";
// 使用C库函数需要C风格字符串
FILE* file = fopen(filename.c_str(), "r");
3.8.2、copy
作用:将源字符串从pos开始到len个字符复制到 s 指向的字符串中。注意,第一个参数为接收被复制字符的对象,第二个参数为复制字符的个数,第三个参数为被复制字符在原字符串的起点。例如:
char ch1[20] = { 0 };
string s1 = "123456789";
s1.copy(ch1, 3, 1);
cout << ch1 << endl;
cout << s1 << endl;
3.8.3、find
作用:在字符串中查找其参数指定的序列首次出现的位置。第一个参数为要查找的字符串,第二个参数为被查找字符串的查找起点。例如:
string s1 = "hello world!+hello world!";
string s2 = "hello world!";
cout << s1.find(s2) << endl;
cout << s1.find(s2 , 5) << endl;
答案为: 0 , 13
3.8.4、rfind
作用:在字符串中查找参数指定序列最后一次出现的位置。第一个参数为被查找序列,第二个参数为查找该位置之前的字符串。该函数与find刚好相反,find从前往后查找第一出现的,rfind从后往前查找,找最后一次出现的。
3.9、npos
npos 是string类的一个静态成员变量,类型为: size_t 。
size_t 类型为非负数的类型,但是 npos的赋值为 -1 ,根据数据溢出原则,npos 将会是这个类型能放下的最大正数。所以,npos 在string类代表着一个很大的数字,含义是:直至字符串末尾。
3.10、getline
对于输入一串含有空格字符的字符串,使用传统的 cin 会在遇见空格字符就收手。但是,getline只会遇见回车符才会收手,完美的解决了含有空格字符的输入问题。
此外,getline 还可以只能收手的字符。
string s1;
getline(std::cin , s1);
cout << s1 << endl;
四、练习与巩固
4.1、字符串相加
415. 字符串相加 - 力扣(LeetCode)
在处理很大的数据的时候,int,long long 类型就不够看了,这两个能够存放的整型位数不过 9 位和18位,很容易就溢出,但是采用字符串就不一样了,没有上限。这道题就是用两个字符串来表示两个整型的加法的。其细节内容和我们小学的列竖式一样。
string addStrings(string num1, string num2) {int end1 = num1.size()-1;int end2 = num2.size()-1;int carry = 0;string returnret;while( end1 >= 0 || end2 >= 0){int value1 = end1 >= 0? num1[end1] - '0' : 0;int value2 = end2 >= 0? num2[end2] - '0' : 0;end1--;end2--;int ret = value1 + value2 + carry;carry = ret/10;ret = ret % 10;//头插returnret.insert(0 ,1, ret+'0');}if(carry != 0){returnret.insert(0 ,1, carry+'0');}return returnret;}
4.2、翻转字符串中的单词
https://leetcode.cn/problems/reverse-words-in-a-string-iii/
思路:先写一个函数,用于交换字符串特定范围内的逆置。然后在寻找空格字符,每找到一个空格字符就进行一次范围逆置。最后不要忘了把最后一部分的单词单独逆置。
//翻转函数void reversestr(string& s ,int left ,int right){while(left <= right){swap(s[left] ,s[right]);left++;right--;}}string reverseWords(string& s) {//寻找空字符size_t left = 0, right = 0;right = s.find(" ");while(right < s.size()){reversestr(s,left ,right-1);left = right+1;right = s.find(" " , left);}reversestr(s,left ,s.size()-1);return s;}