• 字符串的输入输出


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

    C语言字符串表示

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

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

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

    char * chptr = "Anytime";

    但不能这样:

    char * chptr;
    *chptr = "Anytime";

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

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

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

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

    字符串基本输入

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

    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] != '
    ') && (sptr[i] != '') ) //读取sptr[i]直到读到''或'
    '
     7             i++;
     8         if(sptr[i] == '
    ') //如果读到的是'
    '
     9             sptr[i] = ''; //把它变成''
    10         else
    11             while( ( getchar() ) != '
    ' ) //如果读到的是别的东西,一直读到'
    ',防止后面的语句误读
    12                 continue;
    13     }
    14     return re;
    15 }

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

     字符串基本输出

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

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

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

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

    char * cha[16] = "Memory
    ";
    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() ) != '
    ') && (i < size - 1) )
     9         i++;//如果读到非'
    '字符或未超出限定范围,则继续读取
    10     while( (i > size - 1) && (getchar() != '
    '))
    11         continue;//如果读取超过限定范围且后面还有字符,则丢弃
    12     st[i] = ''; //结束读取
    13     return st;
    14 }
    15 
    16 char * ud_puts(char * st, int ad_enter){ //输出
    17     int i = 0;
    18     while(putchar(st[i]) != '') //如果没有读到字符串结尾就继续读
    19         i++;
    20     if(ad_enter) //如果加上'
    '的开关被打开
    21         putchar('
    '); //输出换行符
    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 }

      运行结果:

      

  • 相关阅读:
    python学习笔记Day3
    python学习笔记Day2
    IIS7.5部署除静态页面外都是404的解决方案
    CommandBehavior.CloseConnection有何作用
    没事别老待在家里
    冻结表格行列的思路
    如何构建逻辑清晰的可拖拽树的数据结构
    “仅次于20年前的最好的时间是现在“
    java的静态代理和2种动态代理(未完,待续)
    i++和++i
  • 原文地址:https://www.cnblogs.com/mrblug/p/5726960.html
Copyright © 2020-2023  润新知