• 【C++】小心使用文件读写模式:回车(' ') 换行(' ')问题的一次纠结经历


    原来没有仔细注意C++读写文件的二进制模式和文本模式,这次吃了大亏。(平台:windows  VS2012)

    BUG出现:

    写了一个程序A,生成一个文本文件F保存在本地,然后用程序B读取此文件计算MD5值。

    将该文件上传到服务器,再用程序B将文件从服务器上下载下来计算MD5值,神奇的发现两次计算的MD5值不一样,文件被谁改了??

    排除问题:

    1.首先对比了生成文件F和上传到服务器的文件,发现文件复制过程无差错,是同一个文件。

    2.用程序B下载文件F后,保存在本地,发现文件与原文件F不一致,对比二进制发现每行多了一个 。

    3.怀疑服务器传输前对文件格式进行了更改,用wireshark抓包,发现文件内容与服务器上文件一致。那么这个多出来的 从何而来呢,行结尾变成了 。

    4.查看文件F,行结尾是 ,而我记得当初生成文件的时候是以 作为换行符的,纠结一番后想起来了文件读写的模式,只记得是文本与二进制的区别,没有想起来换行符的问题。

    5.几经纠结,查阅C++ primer plus后恍然大悟,都是默认使用文本模式读写文件惹的祸:windows下,文本模式会将 输出成 ,读取时也会将 变成一个 ;所以开始程序B读取文件F并且计算MD5时,是以 来计算的。然而当从服务器上下载下来时,文件是以 作为行结尾的,直接计算MD5会导致值不一样。而将下载下来的文件保存时,由于仍然使用的文本模式,将 变成了 ,导致了当初匪夷所思的结果。

    总结:

    这BUG从出现到调查各方面的原因排除花费了大量的时间,说到底还是因为基础不扎实,这里讲《C++ primer plus》的关键一段话抄下来作为提醒。

    “使用二进制文件模式时,程序将数据从内存传递给文件(反之亦然)时,将不会发生任何隐藏的转换,而默认的文本模式并非如此。例如,对于Windows文本文件,他们使用两个字符的组合吧(回车和换行)表示换行符;Mac文本文件使用回车表示换行符;而UNIX和Linux文件使用换行来表示换行符。C++是从UNIX系统上发展而来的,因此也使用换行来表示换行符。为增加可移植性,Windows C++程序在写文本模式文件时,自动将C++换行符转换为回车和换行;Mac C++程序在写文件时,将换行符转换为回车。在读取文本文件时,这些程序将本地换行符转换为C++模式。对于二进制数据,文本格式会引起问题,因为double值中间的字节可能与换行符的ASCII码有相同的位模式。另外,在文件末尾的检测方式也有区别。因此以二进制格式保存数据时,应使用二进制文件模式。”

    后续验证:

    后来写了一个小程序验证了一下所知,不懂的话可以复制下来跑一下,注意是Windows平台,生成的文件可以用wxHexEditor来查看以二进制形式查看。另外再说一点题外的,不用语言的字符串类型编码可能会不同,例如JavaScript里是UTF-16,而C++默认的是ANSI,下载下来同一个文件计算MD5值的话可能会有问题。

     1 #include <iostream>
     2 #include <fstream>
     3 #include <string>
     4 using namespace std;
     5 int main()
     6 {
     7 
     8     string str1 = "hello!
    ";
     9     ofstream fout("file1");//默认文本模式
    10     fout << str1;
    11     fout.close();
    12 
    13     ifstream fin("file1");
    14     char ch = 0;
    15     string temp;
    16     if (fin) {
    17         while (fin.get(ch))
    18             temp += ch;
    19         cout << "读入file1长度:"<<temp.length() <<endl;
    20         fin.close();
    21     }
    22 
    23     string temp2;
    24     fin.open("file1", ios::binary);//以
    作为换行
    25     getline(fin, temp2);
    26     cout << "二进制模式getline读入file1的长度(结尾包含了\r):" << temp2.length() << endl;
    27     fin.close();
    28 
    29     ofstream fout2("file2");
    30     fout2 << "hello!
    ";
    31     fout2.close();
    32 
    33     string temp3;
    34     fin.open("file2");
    35     if (fin) {
    36         getline(fin, temp3);
    37         cout << "文本模式getline读入file2的长度(同样多了一个\r):" << temp2.length() << endl;
    38     }
    39 
    40     return 0;
    41 }

    by ascii0x03, 2015.9.25

    相关文章:http://blog.csdn.net/xiaofei2010/article/details/8458605/

  • 相关阅读:
    使用python自带的http server共享文件
    vim常用快捷键与快捷操作
    wordpress 安装中文插件提示:To perform the requested action, WordPress needs to access your web server. Please enter your FTP cr
    python async异步编程,await后接task(三)
    虚拟机下ubuntu使用df命令查看磁盘空间小于实际空间
    python线程池
    使用Redis连接错误处理It was not possible to connect to the redis server(s);to create a disconnected multiple
    Sqlite数据库设置密码小工具
    [转]netcore一键部署到linux服务器以服务方式后台运行
    C#3种常见的定时器(多线程)
  • 原文地址:https://www.cnblogs.com/ascii0x03/p/4838671.html
Copyright © 2020-2023  润新知