• 1. 第6章 字符串(string)6.1 字符数组 6.2 C++处理字符串的方法——字符串类与字符串变量
  • 2. 用来存放字符数据的数组是字符数组,字符数组中的一个元素存放一个字符。字符数组具有数组的共同属性。由于字符串应用广泛,C和C++专门为它提供了许多方便的用法和函数。6.1 字符数组Array of strings
  • 3. 定义字符数组的方法与前面介绍的类似。例如 char c[10]; c[0]=′I′;c[1]=′ ′;c[2]=′a′;c[3]=′m′;c[4]=′ ′;c[5]=′h′;c[6]=′a′;c[7]=′p′;c[8]=′p′; c[9]=′y′; 上面定义了c为字符数组,包含10个元素。在赋值以后数组的状态如图所示。 6.1.1 字符数组的定义和初始化
  • 4. 对字符数组进行初始化,最容易理解的方式是逐个字符赋给数组中各元素。如 char c[10]={′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′}; 把10个字符分别赋给c[0]~c[9]这10个元素。 如果花括号中提供的初值个数大于数组长度,则按语法错误处理。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定为空字符。如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度。如 char c[]={′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′}; 也可以定义和初始化一个二维字符数组,如 char diamond[5][5]={{′ ′,′ ′,′*′},{′ ′, ′*′,′ ′,′*′},{′*′,′ ′,′ ′,′ ′,′*′},{′ ′,′*′,′ ′,′*′},{′ ′,′ ′,′*′}};
  • 5. 只能对字符数组的元素赋值,而不能用赋值语句对整个数组赋值。如 char c[5]; c={′C′,′h′,′i′,′n′,′a′}; //错误,不能对整个数组一次赋值 c[0]=′C′; c[1]=′h′;c[2]=′i′;c[3]=′n′;c[4]=′a′; //对数组元素赋值,正确 如果已定义了a和b是具有相同类型和长度的数组,且b数组已被初始化,请分析: a=b; //错误,不能对整个数组整体赋值 a[0]=b[0]; //正确,引用数组元素 6.1.2 字符数组的赋值与引用
  • 6. 例 设计和输出一个钻石图形。 #include using namespace std; void main( ) {char diamond[][5]={{′ ′,′ ′,′*′},{′ ′,′*′,′ ′,′*′},{′*′,′ ′,′ ′,′ ′,′*′}, {′ ′,′*′,′ ′,′*′},{′ ′,′ ′,′*′}}; int i,j; for (i=0;i<5;i++) {for (j=0;j<5;j++) cout<
  • 7. 运行结果为 * * * * * * * *
  • 8. 用一个字符数组可以存放一个字符串中的字符。如 char str[12]={′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′}; 用一维字符数组str来存放一个字符串″I am happy″中的字符。字符串的实际长度(10)与数组长度(12)不相等,在存放上面10个字符之外,系统对字符数组最后两元素自动填补空字符′\0′。 为了测定字符串的实际长度,C++规定了一个“字符串结束标志”,以字符′\0′代表。在上面的数组中,第11个字符为′\0′,就表明字符串的有效字符为其前面的10个字符。也就是说,遇到字符′\0′就表示字符串到此结束,由它前面的字符组成字符串。6.1.3 字符串和字符串结束标志
  • 9. 对一个字符串常量,系统会自动在所有字符的后面加一个′\0′作为结束符。例如字符串″I am happy″共有10个字符,但在内存中它共占11个字节,最后一个字节′\0′是由系统自动加上的。 在程序中往往依靠检测′\0′的位置来判定字符串是否结束,而不是根据数组的长度来决定字符串长度。当然,在定义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度。如果在一个字符数组中先后存放多个不同长度的字符串,则应使数组长度大于最长的字符串的长度。 说明: ′\0′只是一个供辨别的标志。 如果用以下语句输出一个字符串: cout<<″ How do you do?″;
  • 10. 系统在执行此语句时逐个地输出字符,那么它怎么判断应该输出到哪个字符就停止了呢? 下面再对字符数组初始化补充一种方法: 用字符串常量来初始化字符数组。例如 char str[]={″I am happy″}; 也可以省略花括号,直接写成 char str[]=″I am happy″; 不是用单个字符作为初值,而是用一个字符串(注意字符串的两端是用双撇号而不是单撇号括起来的)作为初值。显然,这种方法直观,方便,符合人们的习惯。注意: 数组str的长度不是10,而是11(因为字符串常量的最后由系统加上一个′\0′)。因此,上面的初始化与下面的初始化等价: char str[]={′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′,′\0′};
  • 11. 而不与下面的等价: char str[]={′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′}; 前者的长度为11,后者的长度为10。如果有 char str[10]=″China″; 数组str的前5个元素为′C′,′h′,′i′,′n′,′a′,第6个元素为′\0′,后4个元素为空字符。见图5.8。 图5.8
  • 12. 需要说明的是:字符数组并不要求它的最后一个字符为′\0′,甚至可以不包含′\0′。如以下这样写完全是合法的: char str[5]={′C′,′h′,′i′,′n′,′a′}; 是否需要加′\0′,完全根据需要决定。但是由于C++编译系统对字符串常量自动加一个′\0′。因此,人们为了使处理方法一致,便于测定字符串的实际长度,以及在程序中作相应的处理,在字符数组中有效字符的后面也人为地加上一个′\0′。如 char str [6]={′C′,′h′,′i′,′n′,′a′,′\0′};
  • 13. 字符数组的输入输出可以有两种方法: (1) 逐个字符输入输出,如上例。 (2) 将整个字符串一次输入或输出。例如有以下程序段: char str[20]; cin>>str; //用字符数组名输入字符串 cout<
  • 14. 输出时,逐个输出字符直到遇结束符′\0′,就停止输出。输出结果为 China 如前所述,字符数组名str代表字符数组第一个元素的地址,执行“cout<
  • 15. (2) 输出字符串时,cout流中用字符数组名,而不是数组元素名。 (3) 如果数组长度大于字符串实际长度,也只输出到遇′\0′结束。 (4) 如果一个字符数组中包含一个以上′\0′,则遇第一个′\0′时输出就结束。 (5) 用cin从键盘向计算机输入一个字符串时,从键盘输入的字符串应短于已定义的字符数组的长度,否则会出现问题。 C++提供了cin流中的getline函数,用于读入一行字符(或一行字符中前若干个字符),使用安全又方便,请参阅第13章13.3.2节。
  • 16. 由于字符串使用广泛,C和C++提供了一些字符串函数,使得用户能很方便地对字符串进行处理。几乎所有版本的C++都提供下面这些函数,它们是放在函数库中的,在string和string.h头文件中定义。如果程序中使用这些字符串函数,应该用#include命令把string.h或string头文件包含到本文件中。下面介绍几种常用的函数。6.1.5 字符串处理函数
  • 17. 1. 字符串连接函数 strcat 其函数原型为 strcat(char[],const char[]); strcat是string catenate(字符串连接)的缩写。该函数有两个字符数组的参数,函数的作用是:将第二个字符数组中的字符串连接到前面字符数组的字符串的后面。第二个字符数组被指定为const,以保证该数组中的内容不会在函数调用期间修改。连接后的字符串放在第一个字符数组中,函数调用后得到的函数值,就是第一个字符数组的地址。例如 char str1[30]=″People′s Republic of ″; char str2[]=″China″; cout<
  • 18. 输出: People′s Republic of China 连接前后的状况如下图所示。
  • 19. 2. 字符串复制函数strcpy 其函数原型为 strcpy(char[],const char[]); strcpy是string copy(字符串复制)的缩写。它的作用是将第二个字符数组中的字符串复制到第一个字符数组中去,将第一个字符数组中的相应字符覆盖。例如 char str1[10],str2[]=″China″; strcpy(str1,str2); 执行后,str2中的5个字符″China″和′\0′(共6个字符)复制到数组str1中。
  • 20. 说明: (1) 在调用strcpy函数时,第一个参数必须是数组名(如str1),第二个参数可以是字符数组名,也可以是一个字符串常量。 (2) 可以用strcpy函数将一个字符串中前若干个字符复制到字符数组中去。 (3) 只能通过调用strcpy函数来实现将一个字符串赋给一个字符数组,而不能用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组。
  • 21. 3. 字符串比较函数strcmp 其函数原型为 strcmp(const char[],const char[]); strcmp是string compare(字符串比较)的缩写。作用是比较两个字符串。由于这两个字符数组只参加比较而不应改变其内容,因此两个参数都加上const声明。以下写法是合法的: strcmp(str1,str2); strcmp(″China″,″Korea″); strcmp(str1,″Beijing″); 比较的结果由函数值带回。 (1) 如果字符串1=字符串2,函数值为0。 (2) 如果字符串1>字符串2,函数值为一正整数。
  • 22. (3) 如果字符串1<字符串2,函数值为一负整数。 字符串比较的规则与其他语言中的规则相同,即对两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到′\0′为止。如全部字符相同,则认为相等;若出现不相同的字符,则以第一个不相同的字符的比较结果为准。 注意:对两个字符串比较,不能用以下形式: if(str1>str2) cout<<″yes″; 字符数组名str1和str2代表数组地址,上面写法表示将两个数组地址进行比较,而不是对数组中的字符串进行比较。对两个字符串比较应该用 if(strcmp(str1,str2)>0) cout<<″yes″;
  • 23. 4. 字符串长度函数strlen 函数原型为 strlen(const char[]); strlen是string length(字符串长度)的缩写。它是测试字符串长度的函数。其函数的值为字符串中的实际长度,不包括′\0′在内。如 char str[10]=″China″; cout<
  • 24. 例6.2 有3个字符串,要求找出其中最大者。要求用函数调用。 程序如下: #include #include using namespace std; int main( ) { void max_string(char str[][30],int i); //函数声明 int i; char country_name[3][30]; for(i=0;i<3;i++) cin>>country_name[i]; //输入3个国家名 max_string(country_name,3); //调用max_string函数 return 0;6.1.6 字符数组应用举例
  • 25. } void max_string(char str[][30],int n) { int i; char string[30]; strcpy(string,str[0]); //使string的值为str[0]的值 for(i=0;i0) //如果str[i]>string strcpy(string,str[i]); //将str[i]中的字符串复制到string cout<
  • 26. 用字符数组来存放字符串并不是最理想和最安全的方法。 C++提供了一种新的数据类型——字符串类型(string类型),在使用方法上,它和char、int类型一样,可以用来定义变量,这就是字符串变量——用一个名字代表一个字符序列。 实际上,string并不是C++语言本身具有的基本类型,它是在C++标准库中声明的一个字符串类,用这种类可以定义对象。每一个字符串变量都是string类的一个对象。6.2 C++处理字符串的方法——字符串类与字符串变量
  • 27. 1. 定义字符串变量 和其他类型变量一样,字符串变量必须先定义后使用,定义字符串变量要用类名string。如 string string1; //定义string1为字符串变量 string string2=″China″; //定义string2同时对其初始化 应当注意: 要使用string类的功能时,必须在本文件的开头将C++标准库中的string头文件包含进来,即应加上 #include //注意头文件名不是string.h6.2.1 字符串变量的定义和引用
  • 28. 2. 对字符串变量的赋值 在定义了字符串变量后,可以用赋值语句对它赋予一个字符串常量,如 string1=″Canada″; 既可以用字符串常量给字符串变量赋值,也可以用一个字符串变量给另一个字符串变量赋值。如 string2=string1; //假设string2和string1均已定义为字符串变量 不要求string2和string1长度相同,假如string2原来是″China″,string1原来是″Canada″,赋值后string2也变成″Canada″。在定义字符串变量时不需指定长度,长度随其中的字符串长度而改变。 可以对字符串变量中某一字符进行操作,如 string word=″Then″; //定义并初始化字符串变量word word[2]=′a′; //修改序号为2的字符,修改后word的值为″Than″
  • 29. 3. 字符串变量的输入输出 可以在输入输出语句中用字符串变量名,输入输出字符串,如 cin>> string1; //从键盘输入一个字符串给字符串变量string1 cout<< string2; //将字符串string2输出
  • 30. 在上一节中可以看到: 在以字符数组存放字符串时,字符串的运算要用字符串函数,如strcat(连接)、strcmp(比较)、strcpy(复制),而对string类对象,可以不用这些函数,而直接用简单的运算符。 (1) 字符串复制用赋值号 string1=string2; 其作用与“strcpy(string1,string2);”相同。 (2) 字符串连接用加号 string string1=″C++″; //定义string1并赋初值 string string2=″Language″; //定义string2并赋初值 string1=string1 + string2; //连接string1和string2 连接后string1为″C++ Language″。6.2.2 字符串变量的运算
  • 31. (3) 字符串比较直接用关系运算符 可以直接用 ==(等于)、>(大于)、<(小于)、!=(不等于)、>=(大于或等于)、<=(小于或等于)等关系运算符来进行字符串的比较。
  • 32. 不仅可以用string定义字符串变量,也可以用string定义字符串数组。如 string name[5]; //定义一个字符串数组,它包含5个字符串元素 string name[5]={″Zhang″,″Li″,″Fun″,″Wang″,″Tan″}; //定义一个字符串数组并初始化 此时name数组的状况如下图所示。 6.2.3 字符串数组
  • 33. 可以看到: (1) 在一个字符串数组中包含若干个(现为5个)元素,每个元素相当于一个字符串变量。 (2) 并不要求每个字符串元素具有相同的长度,即使对同一个元素而言,它的长度也是可以变化的,当向某一个元素重新赋值,其长度就可能发生变化。 (3) 在字符串数组的每一个元素中存放一个字符串,而不是一个字符,这是字符串数组与字符数组的区别。如果用字符数组存放字符串,一个元素只能存放一个字符,用一个一维字符数组存放一个字符串。 (4) 每一个字符串元素中只包含字符串本身的字符而不包括′\0′。
  • 34. 可见用字符串数组存放字符串以及对字符串进行处理是很方便的。 在定义字符串数组时怎样给数组分配存储空间呢?实际上,编译系统为每一个字符串变量分配4个字节,在这个存储单元中,并不是直接存放字符串本身,而是存放字符串的地址。在本例中,就是把字符串″Zhang″的地址存放在name[0],把字符串″Li″ 的地址存放在name[1],把字符串″Fun″的地址存放在name[2]……图5.11只是一个示意图。在字符串变量中存放的是字符串的指针(字符串的地址)。
  • 35. 例6.3 输入3个字符串,要求将字母按由小到大的顺序输出。 #include #include using namespace std; int main( ) {string string1,string2,string3,temp; cout<<″please input three strings:″; //这是对用户输入的提示 cin>>string1>>string2>>string3; //输入3个字符串 if(string2>string3) {temp=string2;string2=string3;string3=temp;} //使串2≤串3 if(string1<=string2) cout<
  • 36. else if(string1<=string3) cout<
  • 37. 例6.4 一个班有n个学生,需要把每个学生的简单材料(姓名和学号)输入计算机保存。然后可以通过输入某一学生的姓名查找其有关资料。当输入一个姓名后,程序就查找该班中有无此学生,如果有,则输出他的姓名和学号,如果查不到,则输出“本班无此人”。 为解此问题,可以分别编写两个函数,函数input_data用来输入n个学生的姓名和学号,函数search用来查找要找的学生是否在本班。 程序可编写如下: #include #include using namespace std; string name[50],num[50]; //定义两个字符串数组,分别存放姓名和学号 int n; //n是实际的学生数
  • 38. int main( ) {void input_data( ); //函数声明 void search(string find_name); //函数声明 string find_name; //定义字符串变量,find_name是要找的学生 cout<<″please input number of this class:″; //输入提示: 请输入本班学生的人数 cin>>n; //输入学生数 input_data( ); //调用input_data函数,输入学生数据 cout<<″please input name you want find:″; //输入提示: 请输入你要找的学生名 cin>>find_name; //输入要找的学生的姓名 search(find_name); //调用search函数,寻找该学生姓名 return 0; } void input_data( ) //函数首部 {int i; for (i=0;i>name[i]>>num[i];} //输入n个学生的姓名和学号 }
  • 39. void search(string find_name) //函数首部 {int i; bool flag=false; for(i=0;i
  • 40. 请考虑: (1) 程序第3行定义全局变量时,数组的大小不指定为50,而用变量n,即string name[n],num[n];n在运行时输入,行不行?为什么? (2) search函数for循环中最后有一个break语句,它起什么作用?不要行不行? (3) 如果不使用全局变量,把变量n和数组name,num都作为局部变量,通过虚实结合的方法在函数间传递数据,这样行不行?请思考并上机试一下。
  • 41. 通过以上两个例子可以看到,用string定义字符串变量,简化了操作,把原来复杂的问题简单化了,这是C++对C的一个发展。 归纳起来,C++对字符串的处理有两种方法: 一种是用字符数组的方法,这是C语言采取的方法, 一般称为Cstring方法;一种是用string类定义字符串变量,称为string方法。显然,string方法概念清楚,使用方便,最好采用这种方法。C++保留C-string方法主要是为了与C兼容,使以前用C写的程序能用于C++环境。