C++中字符串的介绍
字符串是个特殊的字符数组。与普通字符数组不同的是,字符串在结尾处有一个字符“\\0”,表示字符串的结束。在C++中字符串有特殊的初始化方式和专门的处理函数。本节将介绍字符串的特性,在下一节中将介绍专门用于处理字符串的函数。
1.1字符串的特性
1.1.1 什么是字符串
在C++语言中,没有专门的字符串类型,一个字符串,其实就是一个字符数组。不过并不是数组中的所有字符都是字符串的一部分,字符串是以字符“\\0”表示字符串的结束的。所以在字符数组中,所有在字符\\0之前的字符才是字符串中的有效字符。
字符串也有字面常量,其形式是用双引号包围起来一串字符。例如: \"Hello World\";
前面讲到的字符,用的是单引号,字符串用的是双引号,这点不要弄反了。\"a\"是一个字符串,而不是一个字符,虽然这个字符串只有一个有效字符。
上面定义了一个字符串,其存储形式如图1.1所示。
图1.1 字符串的存储形式 每一个字符串,后面都有一个不可见的字符“\\0”,这个字符是字符串的结束标志。因此上面的字符串有12个字符。双引号表示其中的内容是一个字符串,字符串中并不包括双引号。
前面讲字符类型的时候讲过转义字符,比如字符“\\n”表示换行。字符串中可以包含转义字符,例如:
\"Hello\\nWorld\";
如果输出上面的字符串,则会分两行输出。\" \"表示只有空格字符的字符串,而\"\"也是一个字符串,这个字符串只有一个结束标志。字符串还可以分多行定义,例如:
\"Hello\" \"World\";
上面的定义相当于\"HelloWorld\",编译器会自动把分行定义的字符串连接起来。需要注意的是,只有在结束的时候才可以有语句结束的分号\";\",前面都不能有。
字符串的结束标志不需要显式地提供,编译器会自动在后面追加。
1.1.2 定义字符串
用双引号包围的字符串是字符串类型的字面常量,不可以修改。而作为变量的字符串,
其实就是字符数组。定义一个字符串其实就是定义一个字符数组,例如:
char name[10]; // 定义一个名字是name的字符串 也可以定义二维的或者多维的字符数组,例如:
char names[10][10]; // 定义一个二维字符数组, 通常称这样的数组为字符串数组
1.1.3 字符串的初始化
同普通数组一样,字符数组也允许在定义的时候直接初始化,例如:
char name[10] = {'J', 'a', 's', 'o', 'n'};
同样,如果为数组的所有元素提供了初始值,则字符数组的长度可以省略,例如:
char name[] = {'J', 'a', 's', 'o', 'n'}; 这时数组长度被自动定义为5。
还可以用字符串来作为字符数组的初始值,因此还可以这么写:
char name[] = \"Jason\";
上面的语句定义了一个长度为6的字符数组,其最后的一个字符为“\\0”。在这种初始化方式下,不需要花括号\"{\"和\。
通常用上面的方法对字符数组进行初始化,而不用单个字符的方式。
1.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 < len / 2; i ++ ) // 遍历字符串 { char temp = buffer[i]; // 交换两个字符 buffer[i] = buffer[len - i - 1]; buffer[len -i - 1] = temp; } cout <<\"反转之后的字符串为:\" < 图1.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个进行了初始化,剩余部分用空字符串来填充。 不能遗忘两个字符串中间的\。 1.2字符串处理函数 虽然可以对字符数组用字符串进行初始化,可以直接输入、输出字符串,但是在使用字符数组的时候还是有很多不便,比如不能直接对字符数组进行赋值、不能比较两个字符数组的大小等。因此C++提供了一些函数来处理字符串,下面介绍几个常用的函数。 1.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赋值 在下面的例子中需要用户输入两个字符串,然后把这两个字符串的内容互换,程序如示例代码2.1所示。 #include using namespace std; // 使用名称空间std int main(int argc, char *argv[]) // 主函数 { cout<<\"请依次输入两个字符串:\"< cout<<\"第一个字符串为:\"< 图2. 1 交换字符串结果 strcpy函数复制的是字符\\0之前的内容(包括这个结束标志),不是整个字符数组的内容。 1.2.2 计算字符串长度函数strlen 对于字符数组,可以通过sizeof来得到其定义的长度,但是这个长度对于字符串是没有意义的。因为通常想要得到的是其有效内容的长度,也就是在字符\\0之前的字符的个数。在C++标准库中提供了函数strlen,可以实现这个功能。strlen函数的格式如下: strlen( 字符数组名 ) strlen将字符串的实际长度作为函数的返回值。使用例子如下: char str[128] = \"Hello World\"; int len = strlen( str ); // 取得字符串的长度 上面的len值为11,包括里面的空格,但是不包括里面的字符串结束标志。下例中需要用户输入一个字符串,然后用strlen函数计算其长度,程序如示例代码2.2所示。 #include int main() { cout<<\"请输入一个字符串:\"< cout<<\"字符串的长度为:\"< 图2. 2 使用strlen函数示例结果 1.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为\"Hello WorldC++ Programming\"。下例需要用户输入两个字符串,然后把它们连接起来,程序如示例代码2.3所示。 #include using namespace std; // 使用名称空间std int main(int argc, char *argv[]) // 主函数 { cout<<\"请输入两个字符串:\"< cout<<\"新生成的字符串为:\"< 图2. 3 strcat函数使用示例结果 1.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 ) ) // 错误的写 法,结果跟希望正好相反 下例需要用户输入两个字符串,然后对它们进行比较,程序如示例代码2.4所示。 #include using namespace std; // 使用名称空间std int main(int argc, char *argv[]) // 主函数 { cout<<\"请输入两个字符串:\"< cout<<\"比较的结果:\"< return EXIT_SUCCESS; // 主函数返回 } 建立一个控制台工程,并将上述代码复制到源文件中,编译并运行,其结果如图2. 4所示。 图2. 4 使用strcmp函数示例结果 在C++的标准库中提供了一个string类。使用这个类,可以直接进行赋值、比较、计算长度和连接等操作。 1.3 汉字的编码方式及在C++中的处理 1.3.1 汉字编码方式的介绍 对英文字符的处理,7位ASCII码字符集中的字符即可满足使用需求,且英文字符在计算机上的输入及输出也非常简单,因此,英文字符的输入、存储、内部处理和输出都可以只用同一个编码(如ASCII码)。 而汉字是一种象形文字,字数极多(现代汉字中仅常用字就有六、七千个,总字数高达5万个以上),且字形复杂,每一个汉字都有“音、形、义”三要素,同音字、异体字也很多,这些都给汉字的的计算机处理带来了很大的困难。要在计算机中处理汉字,必须解决以下几个问题:首先是汉字的输入,即如何把结构复杂的方块汉字输入到计算机中去,这是汉字处理的关键;其次,汉字在计算机内如何表示和存储?如何与西文兼容?最后,如何将汉字的处理结果从计算机内输出? 为此,必须将汉字代码化,即对汉字进行编码。对应于上述汉字处理过程中的输入、内部处理及输出这三个主要环节,每一个汉字的编码都包括输入码、交换码、内部码和字形码。在计算机的汉字信息处理系统中,处理汉字时要进行如下的代码转换:输入码→交换码→内部码→字形码。 (1)输入码: 作用是利用它和现有的标准西文键盘结合来输入汉字。输入码也称为外码。主要归为四类: a) 数字编码:数字编码是用等长的数字串为汉字逐一编号,以这个编号作为汉字的输入码。例如,区位码、电报码等都属于数字编码。 b) 拼音码:拼音码是以汉字的读音为基础的输入办法。 c) 字形码:字形码是以汉字的字形结构为基础的输入编码。例如,五笔字型码(王码)。 d) 音形码:音形码是兼顾汉字的读音和字形的输入编码。 (2)交换码:用于汉字外码和内部码的交换。交换码的国家标准代号为GB2312-80。 (3)内部码:内部码是汉字在计算机内的基本表示形式,是计算机对汉字进行识别、存储、处理和传输所用的编码。内部码也是双字节编码,将国标码两个字节的最高位都置为“1”,即转换成汉字的内部码。 (4)字形码:字形码是表示汉字字形信息(汉字的结构、形状、笔划等)的编码,用来实现计算机对汉字的输出(显示、打印)。 1.3.2 VC/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不能满足汉字容量的要求,就将每两个字符并在一块(即一个汉字占两个字节),显然,94* 94 =8836基本上已经满足了常用汉字个数的要求。计算机处理字符时,当连续处理到两个大与160(或-95~-1)的字节时,就认为这两个字节存放了一个汉字字符。可以用下面的Demo程序来模拟VC/C++中输出汉字字符的过程: unsigned char input[50]; cin>>input; int flag=0; for(int i =0 ;i < 50 ;i++) { if(input[i] > 0xa0 && input[i] != 0) { if(flag == 1) { cout<<\"chinese character\"< flag = 0; } else { flag++; } } else if(input[i] == 0) { break; } else { cout<<\"english character\"< } } 输入:Hello中国 (“中国”对应的GB2312内码为:214 208,185 250) 输出:english character english character english character english character english character chinese character chinese character 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日推出的Unicode 4.1.0 。 Unicode 编码系统可分为编码方式和实现方式两个层次。 编码方式:Unicode 的编码方式与 ISO 10646 的通用字符集(Universal Character Set,UCS)概念相对应,目前的用于实用的 Unicode 版本对应于 UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示 216 个字符。基本满足各种语言的使用。实际上目前版本的 Unicode 尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。 实现方式:Unicode 的实现方式不同于编码方式。一个字符的 Unicode 编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对 Unicode 编码的实现方式有所不同。Unicode 的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。如,UTF-8 编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他 Unicode 字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。 Java与C#语言都是采用Unicode编码方式,在这两种语言中定义一个字符,在内存中存放的就是这个字符的两字节Unicode码。如下所示: char a='我'; => 内存中存放的Unicode码为:25105 1.3.4内码的相互转换 (1)VC中的实现方法 利用Windows系统提供的API:::MultiByteToWideChar和::WideCharToMultiByte ::MultiByteToWideChar:实现当前码到Unicode码的转换; ::WideCharToMultiByte:实现Unicode码到当前码的转换; (2)Java中的实现方法 String vcString=new String(JavaString.getBytes(\"UTF-8\"),\"gb2312\"); Java的编码应该是UTF-8 VC中的MutiByte Charater Set 这种方式以按字节为单位存放字符,即如果一个字符码为两字节,则在内存中占两字节,字符码为一字节,就占一字节。例如,字符串“中国abc”的编码为:中(0xd6、0xd0)、国(0xb9、0xfa)、a(0x61)、b(0x62)、c(0x63)、(0x00),就存为如下方式 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuowenda.com 版权所有 湘ICP备2023022495号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务