C++ STL String
创建
C++的基本数据类型有
int
,bool
,float
,double
,char
,void
,所以string
不是数据类型,而是类
int main(){
string str; //字符串类对象str,无初始化
string s("123456789");
string str(s); //拷贝
string str(s,2); //s[2...end]初始化str,str="3456789"
string str(s,2,5); //s[2]开始,长度为5的字符串初始化str,str="34567"
string str(5,'c'); //用5个字符'c'初始化str
string str(s.begin(),s.end()); //使用迭代器初始化str,str="123456789"
字符串迭代器
定义
string::iterator itr; // itr可以读/写string对象的字符
string::const_iterator citr; // citr只能读取string对象的字符
赋值
一般拥有迭代器的类都会同时拥有返回迭代器的成员函数。
常用的成员函数有begin()
,end()
,前者返回指向第一个元素的迭代器,后者返回指向最后一个元素的下一个位置的迭代器,所以后者的意义主要是作为一个结束的标志,表示已经将所有的元素处理完
与上面两个相反的还有rbegin()
,rend()
,这两个成员函数则在逻辑上从后往前看待容器,前者返回指向最后一个元素的迭代器,后者返回指向第一个元素前一个位置的迭代器
#include <iostream>
#include <string>
using namespace std;
int main(){
string s("1,2,3,4,5");
cout<<string(s.begin(),s.end())<<endl;
cout<<string(s.rbegin(),s.rend())<<endl;
string::iterator i;
for(i = s.begin(); i!=s.end(); ++i)
cout<<*i;
cout<<endl;
for(i = s.end()-1; i!=s.begin(); --i) // A
cout<<*i;
cout<<*s.begin()<<endl; // C
string::reverse_iterator ri ;
for(ri = s.rbegin();ri!=s.rend(); ++ri) // B
cout<<*ri;
}
1,2,3,4,5
5,4,3,2,1
1,2,3,4,5
5,4,3,2,1
5,4,3,2,1
- 迭代器通过
*
访问指向的地址中的内容,和指针相似 - 反向迭代器
string::reverse_iterator ri
,进行++
或--
,行进的方向和正向迭代器相反 - 反向迭代器通常和
rbegin()
,rend()
匹配使用 A
行和B
行的输出结果是一样的,但A
行输出却需要补充一个C
行A
行一开始还忘记-1
了
访问字符串
string str("12345");
char c;
c = str[str.length()]; // 返回 '/0'
c = str[50]; // 越界,处罚断言
c = str.at(str.length()]; // 触发异常
修改字符串
修改assign()
string s1("12345abcd");
string s2;
s2.assign(s1); // s2 = "12345abcd"
s2.assign(s1,4,10); // s2 = "abcd",从下标4开始,往后取10个字符,s1长度不够则到末尾即可
s2.assign(s1,4,s1.npos); // s1.npos 静态常量,指向字符串的结尾处
s2.assign(5,'X'); // s2 = "XXXXX"
s2.assign(s1.begin(),s1.end());
s2.assign(s1.begin()+2, s1.end()-2); // s2 = "345ab" 这里是减掉两个誒!!!!!
大部分都和最上边一样
但要注意最后一行,不是说end()
,指向最后一个元素的下一个位置吗?
应该是自己对s2.assign(s1,4,10)
的印象太过深刻,往后取字符,不够则取到末尾即可,以为迭代器也是一样的操作,这里应该是取不到迭代器指向的那个字符,所以才会有最后一行的情况
总结
如果第一个参数是字符串
s1
,那调用函数的s2
将会被整体修改
s2.assign(s1,2,3)
s2.assign(s1,0,10)
两次修改,都相当于被整体替换
如果第一个参数是数字,那第二个参数一定是字符
s2.assign(5,'X')
s2 == "XXXXX"
参数还可能是两个迭代器
追加append()
string s1("1234");
string s2="abcd";
s1.append(s2);
s1.append(s2,2,10); // s1 = 1234cd, 注意第三个参数
s1.append(s2,2); // s1 = 1234ab
s1.append(5,'X'); // s1 = 1234XXXXX
s1 += "Y"; //追加单个字符,这里用的是 字符串 "Y"
s1.append(1,'Y');
s1.push_back('Y');
这上面的例子,每一次append()
操作后,都会改变调用函数的字符串,所以注释的s1
只是为了方便查看功能
插入和删除
string s1("0123456789");
string s2 = "abcd";
s1.erase(7); // s1 = 01234567 删除索引7以后的子串
s1.erase(3,2); // s1 = 012567
s2.insert(2,s1); // s2 = ab012567cd 在s2的索引2开始插入s1
s2.insert(3,"广外大学"); // s2 = ab0广外大学12567cd
cout<<s2.size(); // 18
s2.insert(4,'Y'); // 这里乱码 s2 = ab0 *** 2567cd
s2.insert(5,'Y'); // s2 = ab0广Y外大学12567cd
这里是因为一个中文字符占两个字节,而英文字符占一个字节,当在索引4插入一个英文字符,会打乱"广"这个字符的ASCII码,即"广"的前一个字节和Y这个字符的字节组合成一个新的两字节字符,所以出现了乱码
所以s2的索引,4个中文字符却占了8个索引,4和5
,6和7
,8和9
,10和11
分别是"广"
,"外"
,"大"
,"学"
的索引
string s("广东外语外贸大学");
for(char i:s) cout<<i;
cout<<endl;
for(int i=0;i<s.length();++i) cout<<s[i];
程序结果
骞夸笢澶栬澶栬锤澶у
骞夸笢澶栬澶栬锤澶у
替换与交换
string s1 = "0123456";
string s2("abcdef");
s1.replace(2,3,s2); // s1 = 01abcdef56
s1.replace(2,6,s2,2,3); // s1 = 01cde56
s1.replace(0,5,s1,1,3); // s1 = 1cd56
s1.replace(1,2,5,'X'); // s1 = 1XXXXX56
string::iterator ib = s1.begin();
string::iterator ie = s1.end();
s1.replace(ib+1,ie-4,s2); // s1 = 1abcdefXX56
s1.swap(s2);
cout<<s1<<" "<<s2; // s1 = abcdef s2 = 1abcdefXX56
小小总结一下
参数如果是数字的话
一般第一个数字是要替换的字符子串的首字符下标
,第二个数字是要替换的字符子串长度
参数如果是迭代器的话
第一个迭代器是要替换的子串的首字符
,第二个迭代器是要替换的子串的尾字符的下一位置
也就是说替换不了第二个迭代器指向的字符
而参数如果还有一个字符串s2
的话
那在s2
前的数字或迭代器,操作的对象是调用该函数的字符串
在s2
后边的数字或迭代器,操作的对象就是s2
字符串对象上的参照
- 字符查找
find()
参数有两个,第一个是需要查找的目标子串或字符,第二个是查找的初始位置;
若找到,返回子串第一次出现的下标,否则返回npos(串尾)
其实返回的就是整型数据,要么是某个下标要么是该字符串的length()
别忘了auto
这个类型
- 取子串
substr()
两个参数,第一个是要截取子串的首地址,第二个是要截取的长度