字符串的输入输出

本文部分内容参考了C Primer Plus(Fifth Edition)

C语言字符串表示

  字符串是C语言中最常用也是最重要的数据类型,但是C语言没有专门提供这种类型。因为字符串由字符组成,所以声明字符串,我们用字符数组。字符数组是字符串的变量表示方法。纯字符数组和字符串的区别和联系就是:字符串是一个以'\0'结尾的字符数组。因此,我们声明一个字符数组char ch[32]实际上它最多只能存储31个可显示字符,最后一个字符是'\0',它是字符串结尾的标志。

  字符串还有一种表示方法,那就是字符串常量(字符串字面量)。例如printf("%s","King and Queen");这个表达式语句中"King and Queen"就是字符串常量。实际上,它也是一个元素为字符常量的数组,这个数组内容为(char []){'K','i','n','g',' ','a','n','d',' ','Q','u','e','e','n','\0'};千万注意别忘了'\0'。

  因为字符串常量如"Anytime"可以表示这个字符串(准确说是元素为字符常量的数组)的首地址。因此,我们可以用指针来操作字符串。我们可以这样声名:

char * chptr = "Anytime";

但不能这样:

char * chptr;
*chptr = "Anytime";

因为*chptr表示chptr所指向的地址上面的内容,如果这个指针未初始化,那么这就是个很危险的操作,指针有可能乱指向内存空间,如果指向的是系统文件,它就会修改系统文件。即使指针已经初始化,我们也不应该这样做,原因有两个:

  1.这样做可能导致溢出(超出了安全的内存空间)

  2.会更改不应该更改的内容(详见这篇文章,它解释得很好)

因此,指针操作字符串常量不安全,一般只用来传输字符串变量的地址(内容为字符变量的数组)。

字符串基本输入

  scanf()虽然有专门的%s来输入字符串,但它的终止条件是遇到如空格,换行符等空白字符。不如说它是用来处理单词输入的。而处理长字符串输入,最早是用gets(),当它读到换行符时丢弃'\n'并结束输入,它很好用(对于很早以前来说),但我不希望大家掌握它,尽管很多随便的程序都在用它。

char cha[5];
gets(cha);

这个函数真的非常好用,直接把数组首地址代进去就行了。你可否注意到一个严重的缺陷?这个函数不知道这个数组的大小,也就是不知道它最多只能接受多少字符的输入,这就会导致溢出!有一些UNIX系统的代码大量使用了gets(),使得黑客有机会通过这个漏洞编写程序将垃圾数据写入系统,导致系统瘫痪,这就是流行于这些UNIX计算机之间的蠕虫病毒。

  我推荐大家使用fgets()函数,这个函数使用起来比gets()安全,但更加麻烦。

 

char * cha[16];
fgets(cha, 16, stdin); //fgets(名称,大小,读取文件)
//如果要用这个函数从键盘读取,请在读取文件的地方用上stdin

 

传入的大小为n,它就最多读取n - 1个字符或遇到换行符时终止。例如,上面的代码运行后我输入"1234567890123456"后,cha = "123456789012345"。这个函数看起来很完美,但十全十美的东西是不存在的。fgets()的缺陷在于它读到换行符时保存了换行符!下面是C Primer Plus第五版的有关程序示例:

  此时,我们很迫切想编写一个函数,让它丢弃fgets()保存的换行符及后面的无效字符。如果不丢弃无效字符,就会导致后面的语句误读了缓冲区,就像初学字符输入时输入一个字符回车后再要输入一个字符,可还没有输入就已经执行到后面去了。我们把我们自己编写的函数取名为s_gets(),我们让它的返回值和fgets()的返回值一样。我们编写的函数代码如下:

 1 char * s_gets(char * sptr, int size){
 2     int i = 0; //i表示读取项数
 3     char * re; //re返回和fgets()一样的数值
 4     re = fgets(sptr,size,stdin);
 5     if(re){ //如果re != NULL
 6         while( (sptr[i] != '\n') && (sptr[i] != '\0') ) //读取sptr[i]直到读到'\0'或'\n'
 7             i++;
 8         if(sptr[i] == '\n') //如果读到的是'\n'
 9             sptr[i] = '\0'; //把它变成'\0'
10         else
11             while( ( getchar() ) != '\n' ) //如果读到的是别的东西,一直读到'\n',防止后面的语句误读
12                 continue;
13     }
14     return re;
15 }

  如果日后需要进行安全的字符串输入,用这个函数就OK了。运用这个函数的示例运行:

 字符串基本输出

  printf()函数提供了一个接近于完美的字符串输出,而且,它还可以直接输出数字(如%d,%u等)。printf()函数的通用性很强,因此,如果不想使用其它的字符串输出函数,一定要记住这个。相信即使刚学C的初学者也知道这个函数的用法。

  puts()函数简洁易用,直接给出字符串的地址就行了。需要注意的是,puts()函数在字符串输出后会加上'\n',所以puts()和gets()以及上面我们自己写的s_gets()配套使用。

char cha[16] = "Memory";
puts(cha);

  fputs()函数主要和fgets()配套,需要提醒的是,这两个函数不仅可以用在标准I/O上,还可以进行文件处理,而且一般是用在文件处理上的。fputs()函数需要两个参数,第一个参数给出字符串地址,第二个参数给出输出位置。因为它和fgets()配套使用,所以它在输出字符串之后不加上'\n'。

char * cha[16] = "Memory\n";
fputs(cha,stdin);

  下面给出C Primer Plus第五版中有关的例程:

自己编写自定义的字符串输入输出函数!

  你也可以自己编写你自己的输入输出函数,而且,假设你编写的函数没有什么太大的错误,这些函数比上面所提到的大部分函数都更加可靠和灵活。我们可以使用getchar()和putchar()来完成字符串的输入输入功能。

  下面是我写的函数,读者在读完后也应该自己动手写写,说不定以后还能用上呢!

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 #define SIZE 16
 5 
 6 char * ud_gets(char * st, int size){ //输入
 7     int i = 0; //计数器
 8     while( ( ( st[i] = getchar() ) != '\n') && (i < size - 1) )
 9         i++;//如果读到非'\n'字符或未超出限定范围,则继续读取
10     while( (i > size - 1) && (getchar() != '\n'))
11         continue;//如果读取超过限定范围且后面还有字符,则丢弃
12     st[i] = '\0'; //结束读取
13     return st;
14 }
15 
16 char * ud_puts(char * st, int ad_enter){ //输出
17     int i = 0;
18     while(putchar(st[i]) != '\0') //如果没有读到字符串结尾就继续读
19         i++;
20     if(ad_enter) //如果加上'\n'的开关被打开
21         putchar('\n'); //输出换行符
22     return st;
23 }
24 
25 int main(int argc, char * argv[]){
26     char cha[SIZE];
27     ud_gets(cha,SIZE);
28     ud_puts(cha,0);
29     getch();
30     return 0;
31 }

  运行结果:

  

posted @ 2016-08-01 21:42  Mr_Blug  阅读(7818)  评论(0编辑  收藏  举报