按行读写文件

       相信大家对puts函数已不陌生了, 前面我们多次用该函数对字符串进行输出处理(将字符串输出到屏幕上). puts函数的主要功能是向标准输出流stdout写一串字符, 在写完这一串字符后, 在写完的字符串后自动添加一个换行符'\n'. 在默认情况下, 标准输出流指的就是屏幕, 所以puts函数用于向屏幕写一串字符.

        如果我们不是向标准输出流stdout写入一串字符, 而是向任意流写一串字符, 那么就要使用fputs函数. fputs函数是puts函数的一个更加通用的版本, 它可以向任意流写入一串字符(第二个参数指明了要写入一串字符的流, 也就是文件指针). 我们可以这样理解, fputs函数的功能是将字符串s输出到fp指向的文件中.

       这个函数与puts函数相同的一点是若出现写入错误, 返回EOF, 否则返回非负数; 与puts函数不同的一点是fputs函数不会在写入的字符串后自动添加换行符, 除非写入的字符串本身就含有换行符. 

       与puts函数配对使用的一个函数就是gets函数, 该函数的主要功能是从标准输入流stdin读取字符串, 读到换行符时停止. 在默认情况下, 标准输入流stdin指的就是键盘, 因此gets函数的主要功能是从键盘读取一串字符, 直到遇到换行符才停止读入. 

       前面我们已经提到, gets函数并不安全: 我们通过键盘输入一串字符, 直到敲下回车才停止读入, 之后gets函数会一股脑地将我们输入的字符写入到s指向的缓冲区中. 如果我们写入的字符较多而缓冲区的空间不足以容纳这些字符, 就会发生缓冲区溢出问题. 如果该问题发生, 程序会异常终止. 换句话说, gets函数并不理会缓冲区的大小, 很容易发生缓冲区溢出问题, 这给黑客实施缓冲区攻击提供了机会. 有统计表明, 80%以上的黑客都是利用缓冲区溢出漏洞实施缓冲区溢出攻击的. 

       fgets函数是gets函数的一个更加通用的版本, 它解决了gets函数极易发生缓冲区溢出的问题.

        fgets函数可以从任意流读取一串字符, 它的第三个参数指明了要读取的流(也就是文件指针), 其第二个参数用来说明缓冲区的大小(主要用于对读取的字符串长度加以限制): 有了这个参数后, 就可以限制fgets函数读入(到s所指缓冲区的)字符串的长度了. 也就是说, 当fgets函数读到回车符、文件末尾或读满n - 1个字符时, 函数即返回字符串的首地址, 如此可保证其在从fp指向的文件读取字符串时, 最多只能读取n - 1个字符. 因此用fgets(buf, sizeof(buf), stdin) 代替 gets(buf) 更加安全.

       接下来我们一起看一下gets函数和fgets函数的相同点与不同点: 二者的相同点是都会在字符串末尾添加字符串结束标志'\0', 然后返回指向读入的字符串的指针; 当读取失败或者到达文件尾时都返回NULL. 那么我们如何判断到底是读取失败还是到达文件尾呢, 可以用feof函数或ferror函数来确定fgets函数返回NULL的确切原因. 二者也有不同点: gets函数从标准输入流stdin读取字符串, 读到换行符时会将其替换为字符串结束标志'\0', 也就是说gets函数读取字符串时是不会在字符串中保留换行符'\n'的, 而fgets函数则不同, 它从指定的流读取字符串, 读到换行符时, 会将换行符也作为字符串的一部分读到字符串中, 也就是在字符串中保留这个换行符, 之后将换行符和其他读入的字符一起存储到第一个参数指向的缓冲区中, 最后在换行符后添加一个字符串结束标志. 

       在之前的博文中, 我们使用下面的程序将从键盘键入的字符一个一个地写入指定文件(demo.txt)中. 

       这里我们看一下如何使用fputs函数代替上述程序中的fputc函数

#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE *fp;/* 定义文件指针fp */
    char str[80];
    if((fp = fopen("C:\\Users\\surface\\Desktop\\demo.txt", "w")) == NULL)/* 以只读的方式打开文件 */
    {
        /* 打开文件时发生错误 */
        printf("Failure to open demo.txt!\n");
        exit(0);
    }
    gets(str);/* 通过键盘读取字符串 */
    if(fputs(str, fp) == EOF)
    {
        /* 发生写入操作 */
        printf("Error on writing.\n");
        exit(0);
    }

    if(fclose(fp) == 0)
    {
        /* 文件关闭成功, fclose()返回0 */
        printf("\nSuccess for writing the string to file demo.txt.\n");
    }
    else
    {
        /* 文件关闭失败, fclose()返回EOF(-1) */
        exit(0);
    }
    return 0;
}

       运行上述程序, 输入字符串"We are the world!", 并敲下回车.

       查看demo.txt文件中的内容.

       如果我们将str数组的长度改为8, 运行程序看看会发生什么.

       可见, 此时程序在运行过程中异常终止了, 这是因为str数组的容量不足以放下我们从键盘键入的字符. 为了提升程序的安全性(避免发生缓冲区溢出问题), 可以将gets函数用fgets函数替代.

#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE *fp;/* 定义文件指针fp */
    char str[8];
    if((fp = fopen("C:\\Users\\surface\\Desktop\\demo.txt", "w")) == NULL)/* 以只读的方式打开文件 */
    {
        /* 打开文件时发生错误 */
        printf("Failure to open demo.txt!\n");
        exit(0);
    }
    fgets(str, sizeof(str), stdin);/* 通过键盘读取字符串 */
    if(fputs(str, fp) == EOF)
    {
        /* 发生写入操作 */
        printf("Error oWe aren writing.\n");
        exit(0);
    }
    if(fclose(fp) == 0)
    {
        /* 文件关闭成功, fclose()返回0 */
        printf("\nSuccess for writing the string to file demo.txt.\n");
    }
    else
    {
        /* 文件关闭失败, fclose()返回EOF(-1) */
        exit(0);
    }
    return 0;
}
       运行上述程序.

       查看demo.txt文件中的内容.

       为了使运行结果更易于分析, 我们再次运行程序, 输入"123456789", 并敲下回车.

       再次查看demo.txt中的内容.

       可以发现, 此时程序只将我们键入的9个字符中的前7个写入到文件中. 

       如果我们想将demo.txt文件中的内容输出到屏幕上, 应像下面程序这样进行操作.

#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE *fp;/* 定义文件指针fp */
    char str[8];
    if((fp = fopen("C:\\Users\\surface\\Desktop\\demo.txt", "r")) == NULL)/* 以只读的方式打开文件 */
    {
        /* 打开文件时发生错误 */
        printf("Failure to open demo.txt!\n");
        exit(0);
    }
    fgets(str, sizeof(str), fp);/* 从fp指向的文件中读取数据 */
    puts(str);

    if(fclose(fp) == 0)
    {
        /* 文件关闭成功, fclose()返回0 */
        printf("\nSuccess for writing the string to file demo.txt.\n");
    }
    else
    {
        /* 文件关闭失败, fclose()返回EOF(-1) */
        exit(0);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好梦成真Kevin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值