第8章字符串处理在第2章中讲述了字符型数据和字符串常量的基本用法,本章讲述如何对字符串进行存储、引用和处理。8.1字符串的存储与引用8.1.1字符串在内存中的存储形式8在C语言程序中,总是用一对双引号将字符串常量括起来,这里的双引号称为字符串的定界符,其作用是表示字符串的起始和终止位置。但是,将一个字符串常量存储到内存中时,并不存储作为字符串定界符的双引号。既然如此,在内存中如何表示字符串的起始位置和终止位置呢?其实,要确定一个字符串的起始位置很容易,这在稍后的论述中可以看到;而要确定一个字符串的终止位置,则必须添加特殊的标志才能表示出来为此,C语言规定:在内存中存储字符串常量时,必须在其末尾添加空字符"O'(ASCII码为0的字符)作为字符串结束标志。当编译系统将一个字符串常量存人内存时,会自动为它添加结束标志。HeI例如,字符串常量“Hello”在内存中的存储形式如图8.1字符串的存储形式图8.1所示。8.1.2用字符数组存储和引用字符串C语言中有字符串常量,但是并没有与之对应的字符串变量。那么,在C语言程序中如何存储字符串常量呢?既然一个学符型变量能够存储一一个字符,那么,一个字符型数组就可以存储一串字符。因此,在C语言中通常利用字符型数组来存储和处理字符串。除此之外,也可以利用字符型指针来处理字符串。如何将一个字符串存人字符数组中呢?最简单的方法,就是在定义字符数组的同时,将字符串存人字符数组中,这就是字符数组的初始化。1.以单个字符的形式初始化字符数组例如:chars[1o]=("G',‘o',o",'d);此时,若字符个数少于数组元素个数,则多余的数组元素会自动初始化为空字符"0'。这种初始化方式颇为烦琐,所以在实际编程中较少使用。2.以字符串的形式初始化字符数组例如:char s[20]="Good bye";char t[3][2o]=("Hello","How are you","Good bye"]:
很显然,这种初始化方式要简洁方便得多。不过,需要注意下列赋值语句是错误的。char s[20];S="Good bye";因为数组名s是一个指针常量,而常量是不能被赋值的,所以不能直接对数组名进行赋值。3.不指定数组长度初始化字符数组例如:chars[]='G','o',o',"d'}当以单个字符形式初始化字符数组,同时缺省数组长度时,系统不会在末尾自动添加空字符0',因此字符数组s中有4个元素。又如:char t[]="Good";当以字符串形式初始化字符数组时,系统会在末尾自动添加空字符"0,因此字符数组t中有5个元素。由此可见,在程序中以字符串常量形式出现,其末尾就隐含了一个空字符"0;而以字符常量形式出现的,其未尾就不隐含空字符。4.用字符数组名引用字符串将一个字符串存人字符数组后,就可以在程序中通过字符数组名来引用这个字符串。当然,也可以通过数组元素来引用这个字符串中的某个字符例如:#include <stdio.h>int main(void)[char t[20]="Good bye";t[5]="B';*用数组名引用字符串*printf("gsin",t);/*用数组元素引用单个字符*,printf("ecln",t[5]):return 0;可见,可以通过给字符数组元素赋值的方式,修改字符数组中的字符串。【例8.1】编写程序实现如下功能,从键盘输人一位数字,将其转换为相应的汉字大写数字输出。编程思路:(1)为便于字符转化,可先将10个汉字存入一个字符数组中。第(2)若将10个汉字作为一个字符串,存人一个一维字符数组中,即采用“charup[21]=8"零壹贰参肆伍陆柒捌玖”这种形式,则从其中提取某个汉字时不甚方便。*(3)若将10个汉字作为10个字符串,存入一个二维字符数组中,操作起来就方便得多。算法设计:字(1)首先,利用初始化语句“charup[10][3]-{"零""壹""贰""叁参""肆""伍""陆""柒"符串"捌""玖"将10个汉字存入一个10行3列的二维字符数组中,每行存储一个汉字。此时,处其行号恰好就是对应的数字,如图8.2所示。理113
(2)在转化时,直接以输入的数字作为行号,而对应行的字符串就10up[0]零是要转化的大写形式。壹10up[1]源程序:up[2] 贰10510up[3]#include<stdio.h>10肆int main(void)up[4](intn;10up[5] charup[10][3]={"零""壹","贰","叁","肆","伍","陆",up[6]陆10"柴”,"捌","玖"};柴10up[7]/*每个汉字看作两个字符,不能作为字符常量*/捌10up[8]printf("请输人一位数字:In");1oup[9]玖scanf("gd",&n);图8.210个汉字printf("&sn",up[n]);return 0;在二维字符数组中的存储形式想一想,若要完成一个多位数的转化,应该如何编写程序实现呢?8.1.3用字符指针变量引用字符串在C语言中,除了可以利用字符型数组来存储和引用字符串之外,还可以利用字符指针变量来引用字符串,前提是该字符指针变量已经指向待引用的字符串。1.字符指针指向字符串使字符指针变量指向一个字符串,通常有以下两种方式:(1)字符指针变量赋值方式。例如:char *p:p-"How are you!";(2)字符指针变量初始化方式。例如:char *p="How areyou!"需要注意,这里的赋值或初始化,并不表示将整个字符串存人字符指针变量中;其正确的含义,是首先将该字符串常量存人内存中的空闲区域中,然后将该字符串中首字符的地址赋给指针变量p。因为p是字符指针变量,所以只能存储字符的地址值。2.字符指针引用字符串c将一个字符指针变量指向一个字符串后,就可以在程序中通过字符指针变量名来引用语这个字符串。当然,也可以通过字符指针变量来间接引用这个字符串中的某个字符,言例如:程#include<stdio.h>库int main(void)设fchar *p;计p="How are you!";新/*用指针变量名引用字符串*printf("%sln",p);思/*该语句有错*/*(p+4)=A";路114
printf("%cn",*(p+4));/*用指针变量间接引用单个字符*return O;可见,不能通过字符指针修改字符串常量的内容,因为常量的值不可以改变。但是存储在字符数组中的字符串可以修改,因为字符数组的元素是变量8.2字符串的输人和输出在程序中,对于字符串最常用的操作是输人与输出。C语言中,通常利用printf函数和puts函数来输出字符串,利用scanf函数和gets函数来输人字符串。8.2.1用printf函数输出字符串其一般形式为printf("gs",字符串引用)其中,字符串引用可以是字符串常量、字符数组名或字符指针变量(甚至是字符指针的表达式)例如:printf("gsln","Hello");当然该语句也可以简化为printf("Helloln");又如:char a[10]="Hello";printf("gs\n",a);/*输出项是字符数组名*再如:char *p-"Hello";printf("gsln",p);/*输出项是字符指针变量名*/需要注意,虽然可以利用包含%c格式符的printf函数结合循环语句来实现字符串的输出,但是不建议采用这种方式。8.2.2用puts函数输出字符串其一般形式为puts(字符串引用)其中,字符串引用可以是字符串常量、字符数组名或字符指针变量(甚至是字符指针的表达式)。例如:第puts("Hello");8又如:章char a[l0]="Hello";puts(a);字再如:符char *p="Hello";串处puts(p);可以发现,当利用puts函数输出字符串时,能够实现自动换行。理115
8.2.3用scanf函数输入字符串其一般形式为scanf("%s",字符数组名)注意,这里的第二个参数只能是字符数组名。例如:#include<stdio.hsint main(void){char a[20]iscanf("%s",a);/*输人项是字符数组名*printf("gsln",a);return 0;当运行这个程序时,若键入:How are you则输出结果:How原因何在呢?这是因为当用scanf函数输人字符串时,会将空格看作字符串之间的分隔符,因此不允许字符串中包含空格。要输人包含空格的字符串需要使用8.2.4节中介绍的gets函数。需要特别注意,必须首先分配好存储字符串的内存空间(如定义一个字符数组),然后才能输入字符串。因此,一般不能使用字符指针变量作为scanf函数的参数来输入字符串(除非该字符指针变量已经指向一个字符数组或其他预先分配好的内存空间)。以下是一个输人字符串的错例:#include<stdio.h>int main(void)[char *p;scanf("s",p);/*该语句错误*/printf("%sln",p);return 0;因为定义一个字符指针变量,只是分配了存储一个地址的内存空间(在32位编译系统中是4字节),并没有分配存储一个字符串的内存空间。此外,虽然可以利用包含%c格式符的scanf函数结合循环语句来实现字符串的输入,但是不建议采用这种方式。c语8.2.4用gets函数输入字符串言程其一般形式为序gets(字符数组名)设注意,这里的函数参数只能是字符数组名计例如:新#include <stdio.h>思int main(void)路116