IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 记解决一个c语言fwrite(),fread()读写的bug -> 正文阅读

[C++知识库]记解决一个c语言fwrite(),fread()读写的bug

项目场景:

有两个txt文件,分别命名为flag1.txt和flag2.txt,分别存有64个整数。要求读取两个文件的数据,把所有数据合并存入一个新txt文件中。


问题描述:

使用了fread()和fwrite(),开了一个整数类型的数组buf[]暂存读取和写入的数据。程序运行一切正常,但是最终结果却是生成的文件3.txt中的数据缺了两个,分别是flag1.txt和flag2.txt的最后一个数。

问题代码:

#include <stdio.h>
#include <stdlib.h>

int merge(const char *in1, const char *in2, const char *out);

int main()
{
    int flag;
    flag = merge("flag1.txt", "flag2.txt", "3.txt");
    if(!flag)
        printf("Failed!");
    else
        printf("Successfully!");
    
    return 0;
}

int merge(const char *in1, const char *in2, const char *out)
{
    FILE *f_in, *f_out;
    int buf[100];
    int n;
    
    f_out = fopen(out, "wb");
    if(!f_out)
        return 0;
    
    f_in = fopen(in1, "rb");
    if(!f_in)
        return 0;
    
    while((n=fread(buf,sizeof(int),sizeof(buf)/sizeof(int),f_in)))
    {
        fwrite(buf,sizeof(int),n,f_out);
    }
    fclose(f_in);
    
    f_in = fopen(in2, "rb");
    if(!f_in)
        return 0;
    
    while((n=fread(buf,sizeof(int),sizeof(buf)/sizeof(int),f_in)))
    {
        fwrite(buf,sizeof(int),n,f_out);
    }
    fclose(f_in);
    fclose(f_out);
    return 1;
}

输出结果是Successfully! 但是最后缺了两个数 如flag1.txt中最后一个数32671,在合并到3.txt文件中后变成了326。

原因分析:

我一开始以为是数据类型设置不对,可能是32671这个数太大了,超过了int类型可以表达的最大数。但是上网查阅后发现现在的int类型可以表达2的32次方这样大的数了。并非如此

后来试了一下,发现还是IO流的问题。我的读写格式设置的不对,而且代码里是分别对两个文件进行读取,对3.txt文件写入了两次。为了避免出错我在第一次写入后就关闭了3.txt这个文件,然后重新打开。

添加了这么两行:

fclose(f_out);  // close f_out first
    
f_out = fopen(out, "wb");   // open file again 

修改完后测试,发现确实能把最后一个整数完整的读取和写入3.txt,但是最后3.txt文件中只显示了flag2.txt文件中的整数,没有显示flag1.txt的整数。

很快我发现问题出在读写格式上
我原来设置的读写格式是"wb"。这个格式的特点是以二进制打开文件,可读可写。但是每次都是从文件开头读写。如果原来存在写入的数据,就会被覆盖掉。于是,我修改了第二次打开3.txt文件的写入格式。在子函数里把第二个"wb"改成了"ab",这一格式也是以二进制打开文件,可读可写,但是如果文件原来存在数据会接在文件的末尾,接着写入新的数据

f_out = fopen(out, "ab");   // open file again with "ab"

这一下,成功解决了读写文件末尾数据写入不全的问题,并且成功合并写入了两个文件中的数据。但是,又有一个很让人抓狂的现象,写入的两组来自不同文件的整数之间竟然没有空格!第一组的最后一个数和第二组的第一个数竟然合在了一起。

我决定再在这两组数之间加一个空格。于是又加了这么两行

char space[] = {' '};
fwrite(space,1,1,f_out);    // add a space at the end

然而,正当我满心欢喜以为我解决了这个问题的时候。隔了一天,一个漂亮小姐姐copy了这一段修改去尝试。她告诉我在她的电脑文本编辑器里又出现了最开始遇到的文件末尾数据读写不全的问题。

原来,虽然文件都是以二进制的形式存在的,但是txt文件普遍使用ASCII或者其他所谓通用编码,如果直接以二进制格式读写,还像我原来一样按数据块每四个字节读写一次,那么就很容易引入一些编码中的换行符、休止符、制表符之类符号引起的错误。更加别提微软记事本会在文本文件开头引入格式前缀的问题了。

虽然我们直接打开文本文件看不到这些符号和前缀,但用fread()和fwrite()读写的时候都会读取这些内容。

因此无需像我之前的那段代码这么麻烦。所有读写格式里的"b"去掉。

f_out = fopen(out, "a");
if(!f_out)
    return 0;
    
f_in = fopen(in1, "r");
if(!f_in)
    return 0;

另外,请不要随便按照超过一个字节的数据块的形式读取数据。还是老老实实一个字节一个字节地读取。

fwrite()和fread()函数的功能实际上很强大,它支持按照数据块的形式读写,但是对于超过一个字节大小的数据类型,你按照每单个字节来读写一次的方式进行读写也是可以的。

while((n=fread(buf,1,sizeof(buf),f_in)))    
// read by single byte
{
    fwrite(buf,1,n,f_out);  
// write by single byte
}

这样写的好处是,无论什么编码格式,每个字符所占的空间不会小于一个字节,单字节读写能避免很多格式错误的引入。


解决方案:

修改后的完整代码如下:

#include <stdio.h>
#include <stdlib.h>

int merge(const char *in1, const char *in2, const char *out);

int main()
{
    int flag;
    flag = merge("flag1.txt", "flag2.txt", "3.txt");
    if(!flag)
        printf("Failed!");
    else
        printf("Successfully!");
    
    return 0;
}

int merge(const char *in1, const char *in2, const char *out)
{
    FILE *f_in, *f_out;
    int buf[100];
    int n;
    
    f_out = fopen(out, "a");
    if(!f_out)
        return 0;
    
    f_in = fopen(in1, "r");
    if(!f_in)
        return 0;
    
    while((n=fread(buf,1,sizeof(buf),f_in)))    // read by single byte
    {
        fwrite(buf,1,n,f_out);  // write by single byte
    }
    char space[] = {' '};
    fwrite(space,1,1,f_out);    // add a space at the end
    fclose(f_in);
    fclose(f_out);  // close f_out first
    
    f_out = fopen(out, "a");   // open file again with "a"
    if(!f_out)
        return 0;
    
    f_in = fopen(in2, "r");
    if(!f_in)
        return 0;
    
    while((n=fread(buf,1,sizeof(buf),f_in)))
    {
        fwrite(buf,1,n,f_out);
    }
    fclose(f_in);
    fclose(f_out);
    return 1;
}

啊,世界终于清静了~~搞定

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:01:53  更:2021-07-27 16:03:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 4:03:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码