? ? ? ? 标准库类型string 表示可变长的字符序列,使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。
? ? ? ? 即
#include<string>
using std::string;
? ? ? ? 如何初始化类的对象是由类本身决定的。
????????例
string s1;????????????????????//默认初始化,s1是一个空字符串
string s2=s1;??????????????//s2是s1的副本
string s3="hiya";????????//s3是该字符串字面值得副本
string s4(10,'c');????????//s4的内容是cccccccccc
????????等号(=)右边等于左边称之为拷贝初始化;反之则成为直接初始化;
string s5="hiya";? ? ? ? ?//拷贝初始化
string s6("hiya");? ? ? ? //直接初始化
string s7(10,'c');? ? ? ? ?//直接初始化,s7=cccccccccc;
????????拷贝初始化和直接初始化区别
????????拷贝初始化 :? =? ? ?比直接初始化多了个步骤,效率相对较低
????????直接初始化:()
类似于a和c之间利用b进行交换
b=a;
a=c;
c=b;
string s8 =string(10,'c');
?string(10,'c');先提出一个临时string对象 然后在把临时srting对象赋值给s8。?上下两个函数等价。
string temp(10,'c');
string s8=temp;
#include<iostream>
#include<string>
using std::cout;
using std::cin;
using std::endl;
using std::string;
int main()
{
string s;
cin>>s;
cout<<s<<endl;
return 0;
}
????????
????????在读取操作时,string对象会自动忽略开头的空白(即空格符,换行符,制表符等),并从第一个真正的字符开始读起,直到遇见下一处空白为止。
#include<iostream>
using namespace std;
int main()
{
string word;
while(cin>>word) //使用while循环,反复读取,直到文件末尾
cout<<word<<endl; //逐个输出单词,每个单词后面紧跟一个换行(endl;)
return 0;
}
????????一直反复读取,直到文件末尾。输入空格,回车都没有用,因为在它的思维里面 空格 和 回车 也是一个字符,所以不会停止。
????????输入^D(等效于? ENF),则可以停止。
? ? ? ? 因为ENF就是文件末尾。
? ? ? ? 有时我们需要得到我们在字符串中保留输入时的空白字符,此时我们可以使用?geline函数? 来代替? ?>>运算符。getline函数的参数是一个输入流和一个string对象,函数从给定的输入流读入内容,直到遇到换行符为止(Tips:最后的换行符也被读进来了),然后把所读得内容存入到string对象中去(Tips:此时不会存入换行符)。
? ? ? ? getline函数只要遇到换行符就会结束读取操作并进行存储,如果没有内容,直接输入回车键,此时的string对象就是一个空的string。
? ? ? ? 和输入运算符一样,getline也会返回它的流参数。因此输入运算符能作为判断条件,getline函数的结果同样能作为条件。
? ? ? ? 例:可以通过改写之前的程序,我们能实现一次输出一整行,而不再是每次输出一行一个词了。
? ? ? ? 我们拷贝一下刚刚的primer0324.cpp
cp priemr0324.cpp priemr0325.cpp
? ? ? ? ?这样我们就得到了和primer0324.cpp一样的文件primer0325.cpp,再通过nano就可以直接打开了。
? ? ? ?
include<iostream>
using namespace std;
int main()
{
string line;
while(getline(cin,line))
cout<<line<<endl;
return 0;
}
????????Tips:此时getline函数返回的那个换行符实际上已经被丢弃了,因为string对象不会保存换行符,所以我们在结尾加了个endl;
? ? ? ? empty函数:根据string对象是否为空返回一个对应的bool值。
? ? ? ? empty是string的一个成员函数。调用该函数只要使用一下点函数指明哪个对象需要执行empty函数就可以了。
????????例:
while(getline(cin,line))
{
if(!line.empty())
{
cout<<line<<endl;
}
}
????????line就是string对象,所以使用line.empty()
? ? ? ? 意思是:每次读取一整行,遇到空行直接跳过输出。
? ? ? ? 其中line非空时,line.empty返回false值(0),反之返回true值(1)
? ? ? ? 使用if(!line.empty())的意思就是
line非空时line.empty返回0,!a为真,if条件成立,输出line。
line为空时line.empty返回1,!a为假,if条件不成立,不输出line。
? ? ? ? size函数:返回string对象的长度(即string对象中字符的个数)。
例:
#include<iostream>
using namespace std;
int mian()
{
while(getline(cin,line))
{
if(line.size()>100)
cout<<line<<endl;
}
return 0;
}
?每次输出一整行,但是只输出超过100个字符的行。
string::size_type类型
? ? ? ? 对于size函数来说,返回的是一个string::size_type类型的值。string类及其它大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型与机器无关的特性。即使我们不知道srting::size_type类型的细节,但是我们知道:它是一个无符号类型的值,并且能够存放下任何一个string对象的大小。其中所有用于存放string类型的返回变量都是string::size_type类型的。
? ? ? ? 在C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型:
auto len = line.size();
? ? ? ? ?len的类型是string::size_type
? ? ? ? 由于size函数返回的是一个无符号整形数,所以在表达式中混用带符号数和无符号数可能会产生意想不到的结果。
? ? ? ? 例:
? ? ? ? 设n是一个具有负值的int,这表达式s.size()<n的判断结果几乎是true。
因为负值n会自动转换成一个比较大的无符号值。
? ? ? ? string对象中的字符,对大小写敏感,所以在比较同一个字母的大写和小写是不同的。
? ? ? ? 相等运算符(==和!=)分别检验两个string对象相等或不相等,string对象相等意味着它们的长度相同并且包含的字符也全部相同。关系运算符<,<=,>,>=分别检验一个string对象是否小于,小于等于,大于,大于等于另一个string对象。并且这些关系运算符都遵守字典顺序:
? ? ? ? 1.如果两个string对象的长度不同,并且短的string对象的每个字符都与较长的string对象对应位置上的字符相同,也就是说较短的string对象小于较长的string对象。
? ? ? ? 2.如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实是string对象中第一个对应相异的字符的比较的结果。
? ? ? ? 例
? ? ? ? string str = "Hello";
? ? ? ? string phrase = "Hello World";
? ? ? ? string slang = "Hiya";
根据 1 可以判断,对象str小于对象phrase;
根据 2 可以判断,对象slang即大于str也phrase。
? ? ? ? 同前拷贝初始化和直接初始化相同
string st1(10,'c'),st2;
//st1的内容:ccccccccccc;st2是一个空字符串。
st1=st2;
//赋值:用st2的副本替换st1的内容
//最后得到st1和st2都是控制符串。
? ? ? ? 两个string对象相加
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1="hello",s2="world";
string s3=s1+s2;
cout<<s1<<endl<<s2<<endl<<s3<<endl;
s1+=s2;
cout<<s1<<endl;
return 0;
}
? ? ? ? s1+=s2;等效 s1=s1+s2;
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1="hello",s2="world";
string s3=s1+","+s2+'\n';
cout<<s1<<endl<<s2<<endl<<s3<<endl;
return 0;
}
?
当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string;
? ? ? ? string s4 = s1 + “,”;? ? ? ? ? ? ? ? ? //正确:把一个string对象和一个字面值相加;
? ? ? ? string s5 =? "hello" + "," ;? ? ? ? ?//正确:两个运算对象都不是string;
? ? ? ? string s6 = s1 +“,”+“world”;//正确:每个加法运算符都有一个运算对象是string;
? ? ? ? string s7 =? “hello” + “,”+s2;//错误:不能把字面值直接相加;
?“字符串” 其实代表的是字符串的地址
#include<iostream>
using namespace std;
int main()
{
string s="Hello world";
cout<<"Hello world"<<endl<<*"Hello world"<<endl;
cout<<s<<endl<<&s<<endl;
return 0;
}
?
?“*”为寻址运算符,说明后面那串一定是地址,提取了该字符串的首地址,也就是“H”。
这不仅证明了"字符串"是字符串的地址,也证明了为什么加法运算符两侧至少要有一个string。
? ? ? ? 如何的单独处理string对象中的字符,比如检查一个string是否包含空白,string对象中的字母改小写,某个字符是否出现等。
? ? ? ? 处理string的关键问题是如何获取字符本身。
? ? ? ? 第一种方法就是去了解cctype头文件,其中的函数可以让我们来判断某个字符。
? ? ? ? 第二种方法使用基于范围的for语句(范围for语句)
for(declaration:expression)
statement
? ? ? ? ?expression:一个对象,用于表示一个序列。
? ? ? ? declaration:部分负责定义一个变量,改变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量都会被初始化为expression部分的下一个元素值。
? ? ? ? statement:说明,你要做什么,让电脑执行什么。比如下面这个示例,就是执行输出abab。
? ? ? ? 范围for语句示例:
string str("some string");
for(auto abab : str)
cout << abab <<endl;
? ? ? ? ?定义了一个str内容是some string,使用范围for语句,其中? : 右侧 str 字符序列。auto 取出str的类型,取出了什么类型abab就是什么类型,而str的类型都是char型,所以abab就是char型。
#include<iostream>
#include<cctype>
using namespace std;
int main()
{
string s("Hello world!!!");
decltype(s.size()) punct_cnt=0;
for (auto c:s)
if(ispunct(c))
++punct_cnt;
cout<<punct_cnt
<<"punctuation characters in "<<s<<endl;
return 0;
}
? ? ? ? ?先创建一个名为s内容为Hello worlad!!!的string。
? ? ? ? 然后确定punct_cnt的类型并让punct_cnt等于0(在上文我们提到过,在C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型)所以这里punct_cnt的类型和s.size的返回类型一样。punct_cnt是用来统计s中的标点符号的数量。
? ? ? ? for(auto:s)用来面向s中的每个字符
? ? ? ? if(ispunct(c))是c标准库<cctype>中的ispunct(),用于检查所传的字符是否是标点符号字符。
? ? ? ? 然后if(ispunct(c))正确则进行++punct_cntl;将标点符号的计数值加1。
? ? ? ? 使用范围for语句改变字符串中的字符
? ? ? ? 如果想改变string对象中字符的值,必须把循环变量定义成引用类型。示例,把整个string对象转换成大写。
#include<iostream>
using namespace std;
int main()
{
string s("Hello world!!!");
for (auto &c:s)
c=toupper(c);
cout<<s<<endl;
return 0;
}
????????
?????????? c就是每次遍历对于这个字符的引用。
? ? ? ? ? ?toupper()把小写字母转换为大写字母。
? ? ? ? ? ?每次迭代时,变量c引用string对象s的下一个字符,实际上就是改变了当前s的一个字符,当整个循环结束后,s就全部为大写了。
????????一种方式使用下标运算符[],另一种是使用迭代器。
? ? ? ? string的下标同数组一样 从s[0]开始一直到s[s.size()]-1。在访问制定字符之前,必须检测字符是否为空,检查过后再用下标,可以减少错误。
更详细,更全面的介绍,垂阅此文章(该文章篇幅较长,推荐使用电脑阅读)。