深入了解字符串,7.4字符串及应用
深入了解字符串,7.4字符串及应用Hello world!字符串常量被保存在连续的存储空间中,最后用一个特殊的字符'\0'表示字符串的结束,如“Hello world!”在内存中是这样保存的:printf("Hello %s" "world!\n");printf函数执行时,会把字符串"world!\n"去代替格式控制字符串中的%s,因此屏幕上显示:"Hello world!\n" "a=%d,b=%d\n"如果要让屏幕上显示的Hello world两端带有双引号("),则字符串要写成这样的形式:"\"Hello world!\"\n"此时字符串用转义符号'\"'表示双引号,目的是要与用来定界的双引号区别开。字符串常量可以用printf输出,此时要用%s格式符表示。
7.4 字符串及应用上一节介绍了二维数组的定义、初赋值、内存映象及数组元素的使用。本节主要讲字符串、字符串的的存储和使用。
文字处理是计算机应用的重要工作。近年来,计算机更多地被用来文字处理而非数值处理,如文字的输入、编辑、排版、查找、替换和输出等。为了能进行简单文字处理,C语言可以通过定义字符型数据来完成一些的工作。如:
char ch;
ch=getchar( );
putchar(ch);
以上代码定义了一个字型变量,并且可以通过getchar函数输入一个字符存放到字符变量ch中,再通过putchar(ch)把字符输出来。字符常量用单引号定界,可以是普通字符,如'A'、'#'、'9'等,也可以是转义字符,如'\n'、'\t'、'\x41'、'\101等。如果两个单引号之间什么也没有,如'' 则称为空字符,这与空白' '字符不同 空白字符中有一个称为空格的符号。
很多时候,我们要处理更多的字符,因此我们可以把这些字符按顺序排列,并用一对双引号("")定界,称为字符串常量。回顾一下,我们在学习scanf与printf函数时,函数的第一个参数——格式控制字符串就是字符串。如:
"Hello world!\n"
"a=%d,b=%d\n"
如果要让屏幕上显示的Hello world两端带有双引号("),则字符串要写成这样的形式:
"\"Hello world!\"\n"
此时字符串用转义符号'\"'表示双引号,目的是要与用来定界的双引号区别开。
字符串常量可以用printf输出,此时要用%s格式符表示。如;
printf("Hello %s" "world!\n");
printf函数执行时,会把字符串"world!\n"去代替格式控制字符串中的%s,因此屏幕上显示:
Hello world!
字符串常量被保存在连续的存储空间中,最后用一个特殊的字符'\0'表示字符串的结束,如“Hello world!”在内存中是这样保存的:
地址 |
内空空间 |
编号 |
xxx0D |
12 | |
xxx0C |
'\0' |
11 |
xxx0B |
'd' |
10 |
xxx0A |
'l' |
9 |
xxx09 |
'r' |
8 |
xxx08 |
'o' |
7 |
xxx07 |
'w' |
6 |
xxx06 |
' ' |
5 |
xxx05 |
'o' |
4 |
xxx04 |
'l' |
3 |
xxx03 |
'l' |
2 |
xxx02 |
'e' |
1 |
xxx01 |
'H' |
0 |
字符串常量"Hello world!"占用的存储空间中的内容是不可被改变的。我们看到的是一串符号,但在计算机中字符串常量是其第一个字符的首地址,如上图,"Hello world!"的值是一个地址数值xxx01,即字符'H'的首地址。我们可以通过以下输出来验证字符串是一个地址的事实:
printf("%d" "Hello world!\n");
注意这里用的格式符是%d,而不是%s。因此语句执行后显示的结果是一个整数,而不是字符串本身。
很遗憾C语言没有提供字符串变量,这使操作字符串时稍稍麻烦些。由于字符串占用连续存储空间,所以可以通过定义字符数组来保存字符串。如:
char str[80];
字符数组str可以存放79个字符的字符串,因为要留一个字符来存储字符串结束标志'\0'。字符串在定义的同时是可以被赋初值的,有以下几种赋初值的办法:
char str[80]={'H' 'e' 'l' 'l' 'o' '!' '\0'};
char str[80]="Hello!";
char str[80]={"Hello!"};
三种赋初值的结果都一样,但是第一种很显然要麻烦得多,因为一个字符一个字符赋值,并且在最后不能忘记字符串结束标'\0'。而后两种赋初值,会依次把每个字符复制到字符数组str的每个元素中,含最后的字符串结束符号'\0'。
字符串的输出可以用printf或puts函数实现。用printf函数时,要在格式控制字符串中用格式符%s指定,用puts不需要指它格式符,如:
printf("%s" str);
puts(str);
我们前面讲过,数组名字是地址常量,因此str是一个地址。那么怎么输出字符串呢?执行以上语句时,printf函数从str地址对应的字符开始逐个字符输出,直到碰到字符串结束标志'\0'为止。我们可以用前面学习的数组元素的输出方法(逐个元素)来输出字符串,即:
for(i=0;str[i]!='\0';i )
putchar(str[i]);
与一维数组输出不同是循环控制条件。前面用i小于数组长度来表示。字符串的长度可以用strlen函数来确定,但如果先求字符串长度再输出显示多消耗了一些时间。因为知道字符串以'\0'结束。因此,循环条件可设为为str[i]!='\0',即当第i个字符为'\0'时,表示字符串结束,退出循环。
知道了字符串的输出方法后,我们来看一如何输入字符串保存到数组中。我们可以用getchar函数一个字符一个字符地输入,输入一个就保存到数组对应的元素中,此时需要一个计数器记录当前输入的字符个数,只要不碰到回车我们继续输入。给字符数组输入字符串的代码段为:
cnt=0; //计数器为零,表示初始没有字符
while( (ch=getchar( ))!='\0'){
str[cnt]=ch;
cnt ;
}
str[cnt]='\0';
以上代码能完成了字符串的输入并保存到字符数组str中,但是这里假定了str数组是无限长的,在实际应用时,还要限定输入字符串的长度,如cnt<maxLen。由于cnt使用完后再加1,因此可以把字符串输入代码改写为:
cnt=0;
while( (ch=getchar( ))!='\n' && cnt<maxLen)
str[cnt ]=ch;
str[cnt]='\0';
当然我们可以用scanf函数来实现字符串的输入,此时格式控制字符串中要使用%s格式符,如:
scanf("%d" str);
由于数组名str本身是一个地址常量,所以不能在str的前面用取地址运算符&。但是scanf只能实现没有空格的字符串输入,如果输入的字符串含有空格,只取空格前的字符串放到数组中。
也可以用使用gets函数来实现字符串的输入,与scanf相比,可以输入带有空格的字符串,输入方式为:
gets(str);
上面,我们已经学习字符串的概念、存储与字符串的输入与输出,以下通过几个实例深入掌握字符串的应用。
例1:输入一个以回车符为结束标志的字符串(少于80个字符),判断该字符串是否为回文。回文指的是字符串中心对称或正向读与反向读都是一样的,如“abcba” “abccba”是回文,“abcdba”不是回文。
字符串的输入与输出,我们可以直接使用前面的方法实现,所以本例的关键是判断字符串str是否回文。为此,可设两个变量i、j,i保存字符串前面的字符的下标,j保存字符串后面字符的下标,初始时i为0 j为字符串最后一个字符下标(字符串长度-1)。比较str[i]与str[j],如果两个字符相等,则分别让i加1、j减1。如果不等或者i比j大,则程序结束。
初始状态
比较完第一次后i j下标的位置
完整的程序如下:
#include <stdio.h>
int main( )
{
char str[80] ch;
int cnt=0 i j;
while( cnt<80 && ( ch=getchar( ))!='\0')
str[cnt ]=ch;
str[cnt]='\0';
i=0;j=cnt-1;
while(i<j && str[i]==str[j]){
i ;j--;
}
if( i >= j) printf("It is a plalindrome\n");
else printf("It is not a plalindrome\n");
return 0;
}
程序使用i>=j判断是否回文,原因是如果不是回文,一定是因为条件str[i]!=str[j]退出的,此时i小于j。只有从条件i>=j退出,此时说明是回文。
例2:输入一个以回车符为结束标志的字符串(少于80个字符),统计其中数字字符'0'……'9'的个数。
本例我们用gets输入字符串str,然后依次检验每个字符str[i]是否是数字,如果是则让记数器cnt加1。检验字符str[i]的代码为:
if(str[i]>='0' && str[i]<='9')
cnt ;
由于'\0'之前的每个字符都要被检验到,因此可以使用for循环或while循环实现,用for循环如下:
for(i=0;str[i]!='\0';i ) //str[i]!='\0'可简写为str[i]
if(str[i]>='0' && str[i]<='9')
cnt ;
完整的程序如下:
#include<stdio.h>
int main( )
{
char str[80];
int cnt=0;
gets(str);
for(i=0;str[i]!='\0';i ) //str[i]!='\0'可简写为str[i]
if(str[i]>='0' && str[i]<='9')
cnt ;
printf("count=%d\n" cnt);
return 0;
}
例3:输入一个以回车符为结束标志的字符串(少于20个字符),提取其中所有的数字字符( '0'……'9' ),将其转换为一个十进制整数输出。
分析:如有字符串“abc1xyz2uvw3#yuv”,提取的数字字符应该是'1'、'2'、'3',因此构成的十进制整数是123。如何才能把'1'、'2'、'3'变成十进制整数呢?
假设该十进制数为x,则x的初值为0。然后碰到'1'后,要把1放在x的之后(01),可让x乘以10再加上'1'对应的数1 即:x=x*10 '1'-'0'=1;当碰到'2'时,要把2放在x之后(12),可让x乘以10再加上'2'对应的数2,即:x=x*10 '2'-'0'=10 2=12。最后,要把'3'放在x之后(123),即让x=x*10 '3'-'0'=12*10 3=123。因此当str[i]是数字时要作如下处理:
if(str[i]>='0' && str[i]<='9')
x=x*10 str[i]-'0'; //让str[i]作为x的个位
用for循环对字符串每个字符进行处理,代码如下:
for(i=0;str[i];i )
if(str[i]>='0' && str[i]<='9')
x=x*10 str[i]-'0';
完整的程序如下:
#include <stdio.h>
int main( )
{
char str[20] ch;
long long x=0;
short int cnt=0 i;
while( cnt<20 && ( ch=getchar( ) )!='\0')
str[cnt ]=ch;
str[cnt]='\0';
for(i=0;str[i];i )
if(str[i]>='0' && str[i]<='9')
x=x*10 str[i]-'0';
printf("%Ld\n" x);
return 0;
}
最后讲一下字符串的长度,字符串的长度指的是字符串中包含字符的个数。空字符串""指的是不包括任何字符的串,只有一个字符结束符'\0',因此字符串长度为0。字符串"Hello world!"的字符个数为12。但是如果字符串中含有转义符,则多个符号表示的转义符只算作一个字符,如:字符串"\\\x41\\"长度为3而不是8,因为'\\'是一个字符,'\x41'是一个字符'A' 如果用printf输出,显示为:\A\。
本节主要介绍了字符串常量及用字符串数组保存、输入与输出字符串的方法。字符串的应用很频繁,初学者只有不断加强字符串编程训练才能更好掌握字符串应用。本节就讲到这里,下次再见!