配色: 字号:
C++中字符串的介绍
2012-03-11 | 阅:  转:  |  分享 
  
C++中字符串的介绍

字符串是个特殊的字符数组。与普通字符数组不同的是,字符串在结尾处有一个字符\0”,表示字符串的结束。在C++中字符串有特殊的初始化方式和专门的处理函数。本节将介绍字符串的特性,在下一节中将介绍专门用于处理字符串的函数。

.1.1?什么是字符串

在C++语言中,没有专门的字符串类型,一个字符串,其实就是一个字符数组。不过并不是数组中的所有字符都是字符串的一部分,字符串是以字符\0”表示字符串的结束的。所以在字符数组中,所有在字符\0之前的字符才是字符串中的有效字符。

字符串也有字面常量,其形式是用双引号包围起来一串字符。例如:

"Hello?World";?

前面讲到的字符,用的是单引号,字符串用的是双引号,这点不要弄反了。"a"是一个字符串,而不是一个字符,虽然这个字符串只有一个有效字符。

上面定义了一个字符串,其存储形式如图.1所示。? 图.1?字符串的存储形式 每一个字符串,后面都有一个不可见的字符\0”,这个字符是字符串的结束标志。因此上面的字符串有12个字符。双引号表示其中的内容是一个字符串,字符串中并不包括双引号。

前面讲字符类型的时候讲过转义字符,比如字符\n”表示换行。字符串中可以包含转义字符,例如:

"Hello\nWorld";?

如果输出上面的字符串,则会分两行输出。""表示只有空格字符的字符串,而""也是一个字符串,这个字符串只有一个结束标志。字符串还可以分多行定义,例如:

"Hello"?

"World";?

上面的定义相当于"HelloWorld",编译器会自动把分行定义的字符串连接起来。需要注意的是,只有在结束的时候才可以有语句结束的分号";",前面都不能有。

字符串的结束标志不需要显式地提供,编译器会自动在后面追加。.1.2?定义字符串

用双引号包围的字符串是字符串类型的字面常量,不可以修改。而作为变量的字符串,其实就是字符数组。定义一个字符串其实就是定义一个字符数组,例如:

char?name[10];??????????//?定义一个名字是name的字符串?

也可以定义二维的或者多维的字符数组,例如:

char?names[10][10];?????//?定义一个二维字符数组,通常称这样的数组为字符串数组?

.1.3?字符串的初始化

同普通数组一样,字符数组也允许在定义的时候直接初始化,例如:

char?name[10]?=?{''J'',?''a'',?''s'',?''o'',?''n''};?

同样,如果为数组的所有元素提供了初始值,则字符数组的长度可以省略,例如:

char?name[]?=?{''J'',?''a'',?''s'',?''o'',?''n''};?

这时数组长度被自动定义为5。

还可以用字符串来作为字符数组的初始值,因此还可以这么写:

char?name[]?=?"Jason";?

上面的语句定义了一个长度为6的字符数组,其最后的一个字符为\0”。在这种初始化方式下,不需要花括号"{"和"}"。

通常用上面的方法对字符数组进行初始化,而不用单个字符的方式。

.1.4?操作字符串

字符串作为一个字符数组,同样可以使用下标的方法来操作每一个字符,不过字符串有一些普通数组没有的特性。

前面在介绍数组的时候提到过,cin和cout不可以直接操作数组,但是字符数组是个特例。可以直接用C++的标准输入cin输入整个数组的元素,用标准输出cout输出整个字符数组。下面对一个输入的字符串进行反转,其算法是:第一个字符和最后一个字符交换,第二个字符和倒数第二个字符交换,依此类推。假定字符串为abcdefg,那么其交换过程示意如下:

a?????g?

b?????f?

c?????e?

d???保持不变?

交换之后就成了gfedcba。

交换到字符d时,也就是字符串一半的时候,这时的结果已经是gfedcba,交换已经完成了,如果继续再交换下去,则又会恢复成交换之前的样子。

该实例的具体代码如示例代码6.4所示。

示例代码6.4

#include??

using?namespace?std;?????????????????????????????//?使用名称空间std?int?main(int?argc,?char?argv[])??????????????//?主函数?

{?

????char?buffer[128];???????????????????????????//?字符数组?

????cin>>buffer;????????????????????????????????//?输入字符串?

????int?len?=?(int)strlen(?buffer?);????????????//?取得字符串的长度?

????for(?int?i?=?0;?i?
????{?

????????char?temp?=?buffer[i];??????????????????//?交换两个字符?

????????buffer[i]?=?buffer[len?-?i?-?1];?

????????buffer[len?-i?-?1]?=?temp;?

????}?

????cout?<<"反转之后的字符串为:"??<
????return?EXIT_SUCCESS;????????????????????????????//?主函数返回?

}?

建立一个控制台工程,并将上述代码复制到源文件中,编译并运行,其结果如图6.10所示。

? 图.2?字符串反转结果 对于buffer[0],其对应的要交换的为buffer[len-1],对于第i个字符buffer[i],其需要交换的为buffer[len-i-1]。最后来看循环结束的条件:如果字符串的长度len为偶数,最后一对需要交换的是buffer[len/2-1]和buffer[len/2];如果字符串的长度len为奇数,buffer[len/2]是中间字符,不需要交换,最后一对需要交换的字符是buffer[len/2-1]和buffer[len/2+1]。因此用条件i
也可以定义二维的字符数组,也就是字符串的数组。例如下面定义可以存储多个人名的数组:

char?names[10][40];?

同普通的二维数组一样,字符串数组在定义的时候也可以初始化:

char?names[10][40]?=?

{?

????"Tom",?

????"Mary",?

????"Jacky",?

????"Jason"?

};?

上面定义了一个长度为10的字符串数组,并给前4个进行了初始化,剩余部分用空字符串来填充。

不能遗忘两个字符串中间的","。

虽然可以对字符数组用字符串进行初始化,可以直接输入、输出字符串,但是在使用字符数组的时候还是有很多不便,比如不能直接对字符数组进行赋值、不能比较两个字符数组的大小等。因此C++提供了一些函数来处理字符串,下面介绍几个常用的函数。

.2.1字符串复制函数strcpy

字符串是一个字符数组,因此它就有数组的局限性,不能用一个字符串给另外一个字符串赋值,例如:

char?name[10];?

name?=?"Jason";?????????//?错误,不可以这样直接赋值?

为了方便实现上面的功能,C++标准库中提供了strcpy函数,其定义格式为:

strcpy(?字符数组名1,?字符数组名2?)?

函数把字符数组2中的字符串复制到字符数组1中,串结束标志\0也一同复制。使用例子如下:

char?str1[128],str2[]?=?"Hello?World";?

strcpy(?str1,?str2?);???????//?把str2复制到str1?

程序员需要自己保证str1的长度足够容纳str2的内容,如果str2的长度超过了str1的长度,可能会导致系统崩溃。

其中第二个参数可以直接是一个字符串,因此也可以这样使用:

char?str[128];?

strcpy(?str,?"Hello?World"?);???//?直接给str赋值?

在下面的例子中需要用户输入两个字符串,然后把这两个字符串的内容互换,程序如示例代码.1所示。

#include??using?namespace?std;???????????????????????//?使用名称空间std?int?main(int?argc,?char?argv[])????????????//?主函数?

{?

????cout<<"请依次输入两个字符串:"<
????char?str1[128],str2[128];???????//?字符串变量?

????cin>>str1;??????????????//?输入?

????cin>>str2;??

????cout<<"第一个字符串为:"<
????cout<<"第二个字符串为:"<
????cout<
????char?temp[128];?????????????//?临时字符串变量?

????strcpy(?temp,?str1?);???????//?交换两个字符串?

????strcpy(?str1,?str2?);?

????strcpy(?str2,?temp?);?????cout<<"交换之后的字符串为:"<
????cout<<"第一个字符串为:"<
????cout<<"第二个字符串为:"<
????system("PAUSE");????????????????????????//?等待用户反应?

????return?EXIT_SUCCESS;????????????????????//?主函数返回?

}?

建立一个控制台工程,并将上述代码复制到源文件中,编译并运行,其结果如图.1所示。

? 图.1?交换字符串结果 strcpy函数复制的是字符\0之前的内容(包括这个结束标志),不是整个字符数组的内容。.2.2计算字符串长度函数strlen

对于字符数组,可以通过sizeof来得到其定义的长度,但是这个长度对于字符串是没有意义的。因为通常想要得到的是其有效内容的长度,也就是在字符\0之前的字符的个数。在C++标准库中提供了函数strlen,可以实现这个功能。strlen函数的格式如下:

strlen(?字符数组名?)?

strlen将字符串的实际长度作为函数的返回值。使用例子如下:

char?str[128]?=?"Hello?World";?

int?len?=?strlen(?str?);????????????????//?取得字符串的长度?

上面的len值为11,包括里面的空格,但是不包括里面的字符串结束标志。下例中需要用户输入一个字符串,然后用strlen函数计算其长度,程序如示例代码.2所示。

#include??

using?namespace?std;?int?main()?

{?

????cout<<"请输入一个字符串:"<
????char?str[128];?

????cin>>str;?

????cout<<"字符串的长度为:"<
????return?0;?

}?

建立一个控制台工程,并将上述代码复制到源文件中,编译并运行,其结果如图.2所示。

? 图.2?使用strlen函数示例结果 .2.3字符串连接函数strcat

对于两个整数a和b,下面的代码结果是把两个整数相加:

int?a?=?3;?

int?b?=?5;?

int?c?=?a?+?b;?

对于字符串,其加法的意义应该是把两个字符串连接起来,对于下面两个定义:

char?str1[128]?=?"Hello?World";?

char?str2[128]?=?"C++?Programming";?

如果想把两个字符串连接起来,直接用+是不可以的,在C++标准库中提供了一个可以实现这个功能的函数,这个函数就是strcat。函数格式如下:

strcat?(?字符数组名1,?字符数组名2?)?

函数把字符数组2中的字符串连接到字符数组1中字符串的后面,并删去字符串1后的串标志\0。本函数返回值是字符数组1的首地址。使用例子如下:

char?str1[128]?=?"Hello?World";?

char?str2[128]?=?"C++?Programming";?

strcat(?str1,?str2?);???????????????????//?连接字符串?

上面的代码把str2连接到str1的后面,现在str1为"HelloWorldC++Programming"。下例需要用户输入两个字符串,然后把它们连接起来,程序如示例代码.3所示。

#include??

using?namespace?std;????????????????????????//?使用名称空间std?int?main(int?argc,?char?argv[])????????//?主函数?

{?

????cout<<"请输入两个字符串:"<
????char?str1[128];?????????????????????????????//?字符串变量?

????char?str2[128];?

????char?buffer[128];?????cin>>str1>>str2;????????????????????????????//?输入字符串?

????strcpy(?buffer,?str1?);?????????????????????//?复制?

????strcat(?buffer,?str2?);?????????????????????//?连接?

????cout<<"新生成的字符串为:"<
????????return?EXIT_SUCCESS;????????????????????????????//?主函数返回?

}?

建立一个控制台工程,并将上述代码复制到源文件中,编译并运行,其结果如图.3所示。

? 图.3?strcat函数使用示例结果 .2.4字符串比较函数strcmp

对于两个字符串,其比较是有意义的,但是字符数组不能直接进行比较,因此在C++标准库中提供了对字符串进行比较的函数,函数格式如下:

strcmp(?字符数组名1,字符数组名2?)?

函数按照ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果:

字符串1=字符串2,返回值=0。

字符串2>字符串2,返回值>0。

字符串1<字符串2,返回值<0。

当两个字符串相等的时候,返回值为0,因此,在比较两个字符串是否相等的时候,应当像下面的代码这么写:

if(?strcmp(?str1,?str2?)?==?0?)?????//?判断两个字符串是否相同?

而不能这么写:

if(?strcmp(?str1,?str2?)?)??????????//?错误的写法,结果跟希望正好相反?

下例需要用户输入两个字符串,然后对它们进行比较,程序如示例代码.4所示。

#include??

using?namespace?std;????????????????????????//?使用名称空间std?int?main(int?argc,?char?argv[])?????????????//?主函数?

{?

????cout<<"请输入两个字符串:"<
????char?str1[128];?????????????????????????????//?字符串变量?

????char?str2[128];?????cin>>str1>>str2;????????????????????????????//?输入字符串?

????int?res?=?strcmp(?str1,?str2?);?????????????//?比较?

????cout<<"比较的结果:"<
????cout<
????if(?res?==?0?)?cout<<"==";?

????if(?res?>?0?)?cout<<">";?

????if(?res?
????cout<
????system("PAUSE");????????????????????????

????//?等待用户反应?

????return?EXIT_SUCCESS;???????????????????

????//?主函数返回?

}?

建立一个控制台工程,并将上述代码复制到源文件中,编译并运行,其结果如图.4所示。

? 图.4?使用strcmp函数示例结果 在C++的标准库中提供了一个string类。使用这个类,可以直接进行赋值、比较、计算长度和连接等操作。.3.1汉字编码方式的介绍

对英文字符的处理,7位ASCII码字符集中的字符即可满足使用需求,且英文字符在计算机上的输入及输出也非常简单,因此,英文字符的输入、存储、内部处理和输出都可以只用同一个编码(如ASCII码)。

而汉字是一种象形文字,字数极多(现代汉字中仅常用字就有六、七千个,总字数高达5万个以上),且字形复杂,每一个汉字都有“音、形、义”三要素,同音字、异体字也很多,这些都给汉字的的计算机处理带来了很大的困难。要在计算机中处理汉字,必须解决以下几个问题:首先是汉字的输入,即如何把结构复杂的方块汉字输入到计算机中去,这是汉字处理的关键;其次,汉字在计算机内如何表示和存储?如何与西文兼容?最后,如何将汉字的处理结果从计算机内输出?

为此,必须将汉字代码化,即对汉字进行编码。对应于上述汉字处理过程中的输入、内部处理及输出这三个主要环节,每一个汉字的编码都包括输入码、交换码、内部码和字形码。在计算机的汉字信息处理系统中,处理汉字时要进行如下的代码转换:输入码→交换码→内部码→字形码。

(1)输入码:作用是利用它和现有的标准西文键盘结合来输入汉字。输入码也称为外码。主要归为四类:

a)数字编码:数字编码是用等长的数字串为汉字逐一编号,以这个编号作为汉字的输入码。例如,区位码、电报码等都属于数字编码。

b)拼音码:拼音码是以汉字的读音为基础的输入办法。

c)字形码:字形码是以汉字的字形结构为基础的输入编码。例如,五笔字型码(王码)。

d)音形码:音形码是兼顾汉字的读音和字形的输入编码。

(2)交换码:用于汉字外码和内部码的交换。交换码的国家标准代号为GB2312-80。

(3)内部码:内部码是汉字在计算机内的基本表示形式,是计算机对汉字进行识别、存储、处理和传输所用的编码。内部码也是双字节编码,将国标码两个字节的最高位都置为“1”,即转换成汉字的内部码。

(4)字形码:字形码是表示汉字字形信息(汉字的结构、形状、笔划等)的编码,用来实现计算机对汉字的输出(显示、打印)。

1.3.2VC/C++中汉字的编码方式

VC/C++正是采用了GB2312内部码作为汉字的编码方式,因此VC/C++中的各种输入输出方法,如cin/wcin,cout/wcout,scanf/wsanf,printf/wprintf……都是基于GB2312的,如果汉字的内码不是这种编码方式,那么利用上述各种方法就不会正确的解析汉字。

仔细观察ASCII字符表,从第161个字符开始,后面的字符并不经常为用户所使用,负值也未使用。GB2312编码方式充分利用这一特性,将161-255(-95~-1)之间的数值空间作为汉字的标识码。既然255-161=94不能满足汉字容量的要求,就将每两个字符并在一块(即一个汉字占两个字节),显然,9494=8836基本上已经满足了常用汉字个数的要求。计算机处理字符时,当连续处理到两个大与160(或-95~-1)的字节时,就认为这两个字节存放了一个汉字字符。可以用下面的Demo程序来模拟VC/C++中输出汉字字符的过程:

unsignedcharinput[50];

cin>>input;

intflag=0;

for(inti=0;i<50;i++)

{

if(input[i]>0xa0&&input[i]!=0)

{

if(flag==1)

{

cout<<"chinesecharacter"
}

else

{

flag++;

}

}

elseif(input[i]==0)

{

break;

}

else

{

cout<<"englishcharacter"<}

}

输入:Hello中国(“中国”对应的GB2312内码为:214208,185250)

输出:englishcharacter

englishcharacter

englishcharacter

englishcharacter

englishcharacter

chinesecharacter

chinesecharacter

VC/C++中的英文字符仍然采用ASCII编码方式。可以设想,其他国家程序员利用VC/C++编写程序输入本国字符时,VC/C+++则会采用该国的字符编码方式来处理这些字符。

问题又产生了,韩国的VC/C++程序在中国的VC/C++上运行时,如果没有相应的内码库,则对韩语字符的显示有可能出现乱码。我个人猜测,VC安装程序中应该带有不同国家的内码库,这样一来肯定会占用很大的空间。如果所有的国家使用统一的编码方式,且所有的程序设计语言和开发工具都支持这种编码方式该多好!而现实中,确实已经有这种编码方式了,且许多新的语言也都支持这种编码方式,如Java、C#等,它就是下面的Unicode编码

1.3.3新的内码标准——Unicode

Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。随着计算机工作能力的增强,Unicode也在面世以来的十多年里得到普及。最新版本的Unicode是2005年3月31日推出的Unicode4.1.0。

Unicode编码系统可分为编码方式和实现方式两个层次。

编码方式:Unicode的编码方式与ISO10646的通用字符集(UniversalCharacterSet,UCS)概念相对应,目前的用于实用的Unicode版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示216个字符。基本满足各种语言的使用。实际上目前版本的Unicode尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。

实现方式:Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(UnicodeTranslationFormat,简称为UTF)。如,UTF-8编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。

Java与C#语言都是采用Unicode编码方式,在这两种语言中定义一个字符,在内存中存放的就是这个字符的两字节Unicode码。如下所示:

chara=''我'';=>内存中存放的Unicode码为:25105

1.3.4内码的相互转换

(1)VC中的实现方法

利用Windows系统提供的API:::MultiByteToWideChar和::WideCharToMultiByte

::MultiByteToWideChar:实现当前码到Unicode码的转换;

::WideCharToMultiByte:实现Unicode码到当前码的转换;

(2)Java中的实现方法

StringvcString=newString(JavaString.getBytes("UTF-8"),"gb2312");

Java的编码应该是UTF-8

VC中的MutiByteCharaterSet

这种方式以按字节为单位存放字符,即如果一个字符码为两字节,则在内存中占两字节,字符码为一字节,就占一字节。例如,字符串“中国abc”的编码为:中(0xd6、0xd0)、国(0xb9、0xfa)、a(0x61)、b(0x62)、c(0x63)、(0x00),就存为如下方式

献花(0)
+1
(本文系诛仙无恨首藏)