资料来源:《C++ Primer》、《C++ Primer Plus》、博客随笔
1、标准库类型string
- string是什么??其实就是可以变长的字符序列,伸缩自如的string首先需要包含头文件<string>才能听令你的指挥
- string的使用过程中,由于它跟cin、cout一样存在与命名空间当中,所以必须std::string,或者在全局当中直接声明:using std::string;或者using namespace std;
- 在工作过程中我们要尽可能不去使用using namespace std;这种习惯,由于很多函数都在全局命名空间namespace中,我们写函数偶尔单词会跟里面的函数重复,这就导致发生一些冲突性的错误
(1)定义和初始化string对象:直接初始化和拷贝初始化
直接初始化简单来说是一个不含=的式子,而拷贝初始化就是将=的右侧的初始值拷贝到新建的对象去。
#include<iostream>
#include<string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string s1; //默认初始化,s1是一个空串
string s2(s1); //直接将s2初始化为s1的内容
string s3 = s1; //拷贝s1的内容赋给s3
string s4;
s4 = "hello world";
string s5("value"); //直接将s4初始化为value
//string s6(n, 'c'); //直接将s5初始化为连续的n个字符c
string s7(5, 'c'); //输出ccccc
string s8 = string(5, 'c');
//s8等价于创建了string temp(5'c');
//再把temp的内容拷贝给s8,这种方式没有什么意义
string s9(s4, 6); //将s4第六个字符后的内容赋给s9,空字符也算一个字符
string s10(s4,6,3); //将s4第六个字符后开始的内容最多为3个字符的内容赋给s10
string s11(s4.begin(), s4.begin() + 5); //将s4开始到s4第五个字符的内容赋给s11
cout << " s1 : "<<s1 << endl;
cout << " s2 : " << s2 << endl;
cout << " s3 : " << s3 << endl;
cout << " s4 : " << s4 << endl;
cout << " s5 : " << s5 << endl;
cout << " s7 : " << s7 << endl;
cout << " s8 : " << s8 << endl;
cout << " s9 : " << s9 << endl;
cout << " s10 : " << s10 << endl;
cout << " s11 : " << s11 << endl;
system("pause");
return 0;
}
运行结果:
(2)读写string对象
当用cin输入一串字符时,string对象会自动忽略开头的空白(空格符,换行符,制表符等),然后从第一个真正的字符开始读起,直到遇到下一处空白结束。
string str;
cin >> str;
cout << str << endl;
输入的内容为(双引号不为输入内容)“ 输入空格 空格 ”,很显然以下的结果忽视了开头空格与字符后的空格内容。
结果为:
解决方案1:使其不忽略所有字符(除了空白)
string str1,str2;
cin >> str1 >> str2;
cout << str1<<str2 << endl;
输出的结果依旧忽视的空白,但是将str1和str2输出
解决方案2:只设置一个str,反复读取,逐个输出字符串,每个字符串后面紧跟换行
string str;
while(cin >> str)
cout << str << endl;
输入内容后的结果依旧忽略了空白,但是输入的所有字符串能输出并且换行
解决方案3:使用getline()函数读取整一行
string str;
while (getline(cin, str))
cout << str << endl;
getling函数的一项功能就是从第一个字符开始读取,直至输入换行符,再将内容存入到string对象(对象不含有换行符),如果只输入换行符(回车)那么那个string对象输出为空
(3)string特性描述
bool empty() const; 根据string对象是否为空返回一个对应布尔值,为空返回true,反之为false
int size() const; 返回string对象的字符个数,可以用来限制字符串
int max_size() const; 返回string对象中可存放的最大字符串的长度
int capacity() const; 返回当前不必增加内存即可存放的元素个数
int length() const; 返回当前字符串的长度
void resize(int len, char c); 把字符串当前大小设置为len, 多去少补, 多出的字符c填充不足部分
#include<iostream>
#include<string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
string str("C++ ");
if(str.empty()) //为空返回1,反之返回0
{
cout << "str is NULL." << endl;
else
cout << "str is not NULL." << endl;
}
str += "数据结构";
cout << "str is " << str << endl;
cout << "str's size is " << str.size() << endl; //返回当前字符串大小
cout << "str's capacity is " << str.capacity() << endl; //返回当前不增加内存时的容量
cout << "str's max size is " << str.max_size() << endl; //返回最大字符串长度
cout << "str's length is " << str.length() << endl; //返回当前字符串长度
str.resize(20, 'n'); //把当前字符串设置大小为20,用n补充其余部分
cout << "str is " << str << endl;
str.resize(5); //大小设置为5
cout << "str is " << str << endl;
system("pause");
return 0;
}
输出其结果:我们知道,字母跟英文符号占1个字节,中文字符占2个字节,最后一个打印的大小是5,但是C++ 后面多了空格含4个字节,与中文字符算下去6个字节,所以中文的数不会打印出来,但是那消失的2个字节去哪了,我也太懂这原理
(3)字面值与string对象相加,两个string对象相加
string相加的时候注意加号两侧必须要有一个对象是string,不然为错误的无效string相加
string s1 = "hi, "; //逗号后面添加了空格
string s2 = "badby.";
string s3 = s1 + s2;
cout<<s3<<endl; //输出结果为hi, badbye.
string s4 = "hello" + ","; //错误,加号左右没有string对象
string s5 = "hello" + s1; //正确
string s6 = "hello" + "," + s1; //错误,第一个加号左右没有string对象,字面值不可以直接相加
还有一个注意的点,在第一个加号左右有string的前提下,第二个加号为什么不需要添加string对象。其实里面有一个工作原理,编译器会先创建temp临时量,先将s1与"hi"临时存放在temp当中,再让temp与"badbye"相加,当且仅当第一个加号左右有一个string对象时,才允许这种操作出现
string s7 = s1 + "hi" + "badbye"; //正确,这个式子的工作原理如下
string s7 = (s1 + "hi") + "badbye";
string temp = s1 + "hi"; //编译器先临时创建temp临时量
s7 = temp + "baybye"; //再将temp与后面的字符串相加
还有一种字符串相加的方法:append()函数
string s8("hi, ");
s8.append("boy next door");
cout << s8 <<endl;
//输出hi, boy next door
(4)string对象比较
1、比较操作符:>,>=,<=,<,==,!=
操作符按照字典顺序靠前的字符小,比较顺序由前到后,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小
2、成员函数compare()
支持多参数处理,支持用索引值和长度定位子串进行比较,前面减去后面的ASCII码
- 将对象1与对象2对比时还:对象1.compare(对象2),从[0,size)一个一个字符进行对比
- 同时还支持区间内字符的对比A.compare(2, 2 ,B),从A的[2, 2)与B字符串进行对比
- A.compare(2, 2, B, 2, 2),从A的[2, 2)与B的[2, 2)进行对比
string A("123AaAa");
string B("123BbBb");
string C("123CcCc");
string D("123DdDd");
cout << "A.compare(B):" << A.compare(B) << endl; //"123AaAa"和"123BbBb"比较,A<B ,-1
cout << "A.compare(2, 2, B):" << A.compare(2, 2, B) << endl; //"3A"和“123BbBb"比较,3>1 ,1
cout << "A.compare(2, 2, B, 2, 2):" << A.compare(2, 2, B, 2, 2) << endl; //"3A"和“3B"比较,A<B ,-1
cout << "C.compare(2, 4, D, 2, 4):" << C.compare(2, 4, D, 2, 4) << endl; //"3CcC"和"3DdDd"比较,C<B ,-1
2、额外的string操作
(1)构造string的其他方法
//n、len2和pos2都是无符号值
char *p[] = "hi!";
string s(cp, n); //s是cp指向的数组中前n个字符的拷贝、此数组至少包含n个字符
string s(s2, pos2); //s是string s2从下标pos2开始的字符的拷贝。如果pos2的大小大于s2当前大小,则构造函数的行为未定义,可运行成功其他的结果,但是此项显示为空
string s(s2, pos2, len2); //从s2拷贝下标为pos开始len2个字符给s
#include<iostream>
#include<string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main()
{
const char *p = "boy next door!!";
string s1(p, 2); //拷贝p指向的数组中的前个字符
cout << s1<< endl;
string s2("123456789");
string s3(s2,1); //拷贝s2当中从下标为1开始的字符。当设置大小大于9,会打印空并且报错
cout << s3 << endl;
string s4(s2, 3, 2); //拷贝s2从下标为3开始的2个字符
cout << s4 << endl;
system("pause");
return 0;
}
提示:下标从0开始
运行结果:
当char数组存放的内容为字符时,必须以'\0'结尾,全为数字时,不需要'\0'结尾,不然计算机会显示多出来的内容。举个例子,图书馆当中一列柜子有100个格子,里面未必放满,可能存放10本书、20本,所以我们不能用格数算字符串长度(sizeof()),实际存放书的输了是动态变化的,但是管理员告诉我们当我们用'\0'标记最后一位时,就明确了到此为止不会存放任何书了,编译器就懂得只打印'\0'前的内容了。
C标准库中的字符串处理程序,是只认'\0'的,只要没找到'\0',它就认为字符串没有结束,拼命地往后找,这个寻找的过程不理会可能已经超过书柜的格数了(计算机其实很蠢);同样,也可能你在一排书中的中间抽走一本,在那个位置上写上'\0',那么愚蠢的计算机也会认为书到这里为止,它不理会后面其实还有(这是某种截断字符串的技巧)。
char i[] = {'boy','next','door'};
string s(i); //错误,如果char数组不以\0结尾,拷贝进s除了会出现i的内容外,还会出现一些乱码
string s(i,2); //拷贝i的2个字符给s
char i[] = { 'b','n','d' };
string s5(i);
cout << s5 << endl; //打印结果:bnd烫烫蘇m箏4齇,每次打印结果时,出了bnd相同,后面的乱码是会改变的,也说明了char的动态变化
string s6(i, 2);
cout << s6 << endl; //打印结果:bo
char i[]={ 'b','n','d', '\0' };
string s5(i);
cout << s5 << endl; //打印结果:bnd
我在网上找到了类型相关'\0'结尾的总结:
- const char* 字符串 以 “\0”结尾。
- char[] 字符串 以 “\0”结尾。
- string 字符串 不以 “\0”结尾。
- char[n] = "string", 当string 长度+“\0”>n时,会因空间不足出错。
- string.c_str() 转 const char* 时, 会在字符串末尾 自动补“\0”
- char* 转string 时, 会自动把末尾的 “\0” 去掉。
- strlen()是取字符串除去结尾符 “\0” 的长度。
(2)substr操作
substr会返回一个string,它是原始string的一部分或全部拷贝。
string s1("boy next door"); //总共14个字符
string s2 = s1.substr(0,13); //s2 = boy next door,拷贝从下标0开始到13的值
string s3 = s1.substr(6); //s3 = xt door ,拷贝从下标6到结束的值
string s4 = s1.substr(14) //会抛出一个out_of_range的异常
(3)改变string的其他方法
string类型支持顺序容器的赋值预算符以及assign、insert和erase操作。append和replace函数还有额外操作。
s.insert(pos,args) 在s的pos(下标或迭代器)之前插入args指定的字符。下标:返回指向s的引用。迭代器:返回一个指向第一个插入字符的迭代器。
string str("boy next door!!!");
str.insert(str.size(), "???"); //str == boy next door!!!???,从str最后的字符增加???
str.insert(16, "~~~"); //boy next door!!!~~~???,从第16个字符开始添加~~~
s.append(args) 在s后追加args指向的内容,或者字符串。返回一个指向s的引用。
string s("c++"); //初始化s
s.append("Primer"); //s == c++Primer
string str("sstr");
s.append(str); //s == c++sstr
const char *c = { "ccc" };
s.append(c); //s == c++ccc
s.erase(pos,len) 删除从pos开始的len个字符。如果len被省略直接删除s当中全部字符。返回一个指向s的引用。
const char *cp = "boy next door!!!";
s.insert(s.size(), 5, '!'); //在末尾插入5个感叹号
s.assign(cp,8); //s == boy next,从cp中将前八个字符赋给s
s.insert(s.size(),cp+8); //s == boy next door!!!,将cp第八个开始到结束的内容加到s中
s.erase(s.size()-5, 5); //s == boy next do,从s删除最后5个字符
s. assign(args) 将s中的字符替换为args指定的字符。返回一个指向s的引用。
const char *cp = "boy next door!!!";
s.assign(cp,8); //s == boy next
s.replace(range,args) 删除s中范围range内的字符,替换为 args指定的字符。range可以是下标,或是一堆指向s的迭代器。返回一个指向s的引用。
replace操作是调用erase和insert的一种简写。
string s("c/c++");
s.erase(2,3); //s == c/,从第二个字符开始删除3个字符
s.insert(2,"指针"); //s == c/指针,从第二个字符开始加入内容
//只用replace就能完成以上操作
s.replace(2,3,"指针");
(4)string的搜索
- string类提供了6个不同的搜索函数,每个函数都有4个重载版本。
- 每个搜索操作都返回一个string:size_type值,表示匹配发生位置的下标。如果搜索失败返回一个名为string::npos的static成员(npos是一个无符号类型,可能会等于string最大的可能大小)。
s.find(args) 查找s中args第一次出现的位置
s.rfind(args) 查找s中args最后一次出现的位置
s.find_first_of(args) 在s中查找args中任何一个字符第一次出现的位置
s.find_last_of(args) 在s中查找args中任何一个字符最后一次出现的位置
s.find_first_not_of(args) 在s中查找第一个不在args中的字符
s.find_last_not_of(args) 在s中查找从最后一个数起不在args中的字符
string s("C++ Primer");
auto pos1 = s.find("+"); //查找第一次出现
cout << "+在s中的下标为:"<<pos1 << endl;
pos1 = s.rfind("+"); //查找最后一次出现
cout << "+在s中的下标为:" << pos1 << endl;
pos1 = s.find_first_of("or"); //定位r第一次出现
cout << "r在s中第一次出现的下标为:" << pos1 << endl;
pos1 = s.find_first_of("R"); //查找明确区分大小写,不然输出string的最大内存
cout << "R在s中第一次出现的下标为:" << pos1 << endl;
pos1 = s.find_last_of("lr"); //定位r最后一次出现
cout << "r在s中最后一次出现的下标为:" << pos1 << endl;
pos1 = s.find_first_not_of("C"); //定位括号内在s中没有的内容
cout << "括号内在s第一次不存在的内容下标为:" << pos1 << endl;
pos1 = s.find_last_not_of("r"); //定位括号内在最后一次s中没有的内容
cout << "括号内在s最后一次不存在的内容下标为:" << pos1 << endl;
运行结果如下: