首页 > 代码库 > string 对象及其操作

string 对象及其操作

标准库类型string

标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。接下来的示例都假定了已包含了下述代码:

#include <string>

using std::string;  

本节描述最常用的string操作。

定义和初始化string对象

如何初始化类的对象是由类本身决定的。一个类可以定义很多种初始化对象的方式,只不过这些方式之间必须有所区别:或者是初始值的数量有所区别,或者是初始值的类型不同。

string s1;  //默认初始化,s1是一个空字符串

string s2=s1;  //s2是s1的副本

string s3=“hiya”;  //s3是该字符串字面值的副本

string s4(10,‘c‘);   //s4的内容是cccccccccc

可以通过默认的方式初始化一个string对象,这样就会得到一个空的string,也就是说,改string对象这没有任何字符。如果提高了一个字符串的字面值,则该字面值这除了最后那个空字符外其他所有的字符都被拷贝到所创建的string对象中去了。如果提供的是一个数字和一个字符,则string对象的内容是给定字符连续重复若干次后得到的序列。

表 初始化string对象的方式  

string s1              默认初始化,s1是一个空串

string s2(s1)                  s2是s1的副本

string s2=s1              等价于s2(s1),s2是s1的副本

string s3(“value”)          s3是字面值“value”的副本,除了字面值最后的那个空字符外

string s3=“value”          等价于s3(“value”),s3是字面值“value”的副本

string s4(n,‘c’)          把s4初始化为由连续n个字符c组成的串

直接初始化和拷贝初始化

C++语言有几种不同的初始化方式,通过string我们可以清楚地看到在这些初始化方式之间到底有什么区别和联系。如果使用符号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化

当初始值只有一个时,使用直接初始化或拷贝初始化都行。如果像上面的s4那样初始化要用到的值有多个,一般来说只能使用直接初始化的方式。

string s5=”hiya“;  //拷贝初始化

string s6(”hiya“);  //直接初始化

string s7(10,‘c‘);  //直接初始化,s7的内容是cccccccccc

对于多个值进行初始化的情况,非要用拷贝初始化的方式来处理也不是不可以,不过需要显式地创建一个(临时)对象用于拷贝:

string s8=string(10,‘c‘);//拷贝初始化,s8的内容是cccccccccc

s8的初始值是string(10,’c‘),实际上使用数字10和字符c两个参数创建出来的一个string对象,然后这个string对象又拷贝给了s8.这条语句本质上等价于下面的两天语句:

string temp(10,‘c’);  //temp的内容是cccccccccc

string s8=temp;  //将temp拷贝到s8

其实我们可以看到,尽管初始化s8的语句合法,但和初始化s7 的方式比较起来可读性较差,也没用任何补偿优势。

string对象上的操作

一个类除了要规定初始化其对象的方式外,还有定义对象上所能执行的操作。其中,类既能定义通过函数名调用的操作,就像Sales_item类的isbn函数那样,也能定义<<、+等各种运算符在该类对象上的新含义

string 的操作

os<<s      将s写到输出流os当中,返回os

is>>s       从is中读取字符赋给s,字符串以空白分割

getline(is,s)    从is这读取一行赋给,返回is

s.empty()    s为空返回true,否则返回false

s.size()     返回s中字符的个数

s[n]        返回s这第n个字符的引用,位置n从0开始

s1+s2      返回s1和s2连接后的结果

s1=s2      用s2的副本代替s1中原来的字符

s1!=s2     等性判断对字母的大小写敏感

<,<=,>,>=     利用字符在字典中的顺序进行比较,且对字母的大小写敏感

读写string对象

使用iostream来读写int、double等内置类型的值。同意,也可以使用io操作符读写string对象

cin输入操作:在执行读取操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇到下一个空白为止。只能读取两个空白间的字符串。

例如,如果输入的是“    Hello   World  ”,则只能输入Hello,输出的结果中没有任何空格。

和内置类型的输入输出操作一样,string对象的此类操作也是返回运算符左侧的运算对象作为其结果。因此,多个输入或者多个输出可以连写在一起:

string s1,s2;

cin>>s1>>s2;    //把第一个输入读到s1中,第二个输入到s2中

cout<<s1<<s2<<endl;  //输出两个string对象

假设给上面这段程序输入与之前一样的内容“   Hello World     ”,输出将是“HelloWorld”

 

使用getline读取一整行

有时候我们希望能在最终得到的字符串中保留输入时的空白,这时应该使用getline函数代替原来的>>运算符。getline函数的参数是一个输入流和一个string对象,函数从给定的输入流这读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。getline只是一遇到换行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符也是如此。如果输入真的一开始就是换行符,那么所得得结果是个空string。

和输入运算符一样,getline也会返回它的流参数。因此既然输入运算符能作为判断的条件,我们也能用getline的结果作为条件。

注意:触发getline函数返回的那个换行符实际上被丢弃掉了,得到的string对象中并不包含该换行符。

string的empty和size操作

顾名思义,empty函数根据string对象是否为空返回一个对应的布尔值。

size函数返回string对象的长度(即string对象中字符的个数)。

 

string::size_type类型

对于size函数来说,返回一个int或者一个unsigned似乎都是合情合理的,其实size函数返回的是一个string::size_type类型的值。

string类及其他大多数标准库类型都定义了几种配套的类型,这些配套类型体现了标准库类型与机器无关的特性,类型size_type即是其中的一种。

尽管我们不太清楚size_type类型的细节,但是一点是可以肯定的:他是一种无符号类型的值而且足够存放下任何string对象的大小。所有用于存放string类的size函数返回值,都应该是size_type类型的。

过去,size_type这种类型有点儿神秘,不太容易理解和使用。在C++11中新标准允许编译器通过auto或者decltype来推断变量的类型:

auto len=line.size() ;  //len的类型是size_type

由于size函数返回的是一个无符号整数类型,因此记住,如果在表达式这混用了带符号数和无符号数将可能产生意想不到的结果。例如,假设n是一个具有负值的int,则表达式s.size()<n的判断结果几乎肯定是true,这是因为负值n会自动转换成一个比较打的无符号数。

 

两个string对象相加

两个string对象相加得到一个新的string对象,其内容是把左侧的运算对象与右侧的运算对象串接而成,也即是说,对string对象使用加法运算符的结果是一个新的string对象,它所包含的字符由两部分组成:前半部分是加号左侧string对象所含的字符、后半部分是加号右侧string对象所含的字符。另外,复合赋值运算符(+=)负责把右侧string对象的内容追加到左侧string对象的后面:

string s1=“hello,”,s2=“World\n”;

string s3=s1+s2;

s1+=s2;

 

字符串和string对象相加

标准库允许把字符字面值和字符串字面值转换为string对象,所以需要string对象的地方就可以使用这两字面值来替代。利用这一点将之前的程序改写为如下形式:

string s1=“hello”,s2=“world”

string s3=s1+”,“+s2+”\n";

当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string;

string s4=s1+“,”;//正确:把一个string对象和一个字面值常量相加

string s5=“hello”+“,”;//错误:两个运算对象都不是string

//正确:每个加法运算符都有一个运算对象是string

string s6=s1+“,”+“world”;

string s7=“hello”+“,”+s2;//错误:两个运算对象都不是string

其中,string s6=s1+“,”+“world”中子表达式s1+“,”的结果是一个string对象,它同时作为第二个加法运算符的左侧运算对象,因此上述语句和下面的两个语句是等价的:

string tmp=s1+“,”;

s6=tmp+“world”;

另一方面,s7的初始化是非法的,根据其语义加上括号之后就成了如下的形式:

string s7=(“hello”+“,”)+s2;

很容易看出,括号内的子表达式试图把两个字符串字面值加在一起,而编译器根本没法做到这一点,所以这条语句是错误的。

切记:字符串字面值与string是不同的类型。

 

处理string对象中的字符

我们经常需要处理单独处理string对象中的字符,不如检查一个string对象是否包含空白,或者把string对象中的字母改为小写,再或者查看某个特定的字符是否出现等。

如下一下获取一个字符,并对字符处理的函数

cctype头文件中的函数

isalnum(c)        当c是字母或数字时为真

isalpha(c)         当c是字母时为真

iscntrl(c)         当c是控制字符时为真

isdigit(c)         当c是数字时为真

isgraph(c)        当c不是空格但可打印时为真

islower(c)         当c是小写字母时为真

isprint(c)         当c是可打印字符时为真(即c是空格或c具有可视形式)

ispunch(c)        当c是标点符号时为真(即c不是控制字符、数字、字符,可打印空白中的一种)

isspace(c)         当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种)

isupper(c)         当c是大写字母时为真

isxdigit(c)          当c是十六进制数字时为真

tolower(c)         如果c是大写字母,输出对应的小写字母:否则原样输出c

toupper(c)         如果c是小写字母,输出对应的大写字母:否则原样输出c

处理每个字符?使用基于范围的for语句

如果想对string对象中的每个字符做点什么操作,目前最好的办法是使用C++11新标准提供的一种语句:范围for语句。这种语句遍历给定序列这的每个元素并对序列这的每个值执行某种操作,其语法形式是:

for(declaration:expression)

  statement;

其中,expression部分是一个对象,用于表示一个序列,declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

一个string对象表示一个字符的序列,因此string对象可以作为范围for语句中的expression部分。

使用范围for语句改变字符串中的字符

如果想要改变string对象中字符的值,必须把循环变量定义成引用类型。记住,所谓引用只是给定对象的一个别名,因此当使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。使用这个引用,我们就能改变它绑定的字符。

例如:

string s("Hello world!");

//转换成大写形式

for(auto &c:s)

  c=toupper(c);//对于s中的每个字符(注意,c是引用)

cout<<s<<endl;

上述结果将是:

HELLO WORLD!!!

每次迭代时,变量c引用string对象s的下一个字符,赋值给c也就是在改变s中对应字符的值。因此当执行下面的语句时:

c=toupper(c);//c是一个引用,因此赋值语句将改变s中字符的值

实际上改变了c绑定的字符的值。整个循环结束后,str中的所有字符都变成了大写形式。

 

只处理一部分字符?

下标运算符([])接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置:返回的是该位置上字符的引用