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

BUG出现:

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

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

排除问题:

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

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

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

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

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

总结:

这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值的话可能会有问题。

 #include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{ string str1 = "hello!\n";
ofstream fout("file1");//默认文本模式
fout << str1;
fout.close(); ifstream fin("file1");
char ch = ;
string temp;
if (fin) {
while (fin.get(ch))
temp += ch;
cout << "读入file1长度:"<<temp.length() <<endl;
fin.close();
} string temp2;
fin.open("file1", ios::binary);//以\n作为换行
getline(fin, temp2);
cout << "二进制模式getline读入file1的长度(结尾包含了\\r):" << temp2.length() << endl;
fin.close(); ofstream fout2("file2");
fout2 << "hello!\r\n";
fout2.close(); string temp3;
fin.open("file2");
if (fin) {
getline(fin, temp3);
cout << "文本模式getline读入file2的长度(同样多了一个\\r):" << temp2.length() << endl;
} return ;
}

by ascii0x03, 2015.9.25

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

【C++】小心使用文件读写模式:回车('\r') 换行('\n')问题的一次纠结经历的更多相关文章

  1. python文件读写模式 --- r,w,a,r+,w+,a+,rb,wb

    要了解文件读写模式,需要了解几种模式的区别,以及对应指针 r : 读取文件,若文件不存在则会报错 w: 写入文件,若文件不存在则会先创建再写入,会覆盖原文件 a : 写入文件,若文件不存在则会先创建再 ...

  2. python 文件读写模式r,r+,w,w+,a,a+的区别(附代码示例)

    如下表   模式 可做操作 若文件不存在 是否覆盖 r 只能读 报错 - r+ 可读可写 报错 是 w 只能写 创建 是 w+ 可读可写 创建 是 a 只能写 创建 否,追加写 a+ 可读可写 创建 ...

  3. python3的文件读写模式

    任何一种语言,文件的读写都是非常常见的.python的文件读写非常简单,仅仅一个函数open(file也可以,但是我不常用). 先看看官网的解释: open(file, mode='r', buffe ...

  4. 一篇搞懂python文件读写操作(r/r+/rb/w/w+/wb/a/a+/ab)

           关于文件操作的几种常用方式,网上已有很多解说,内容很丰富,但也因此有些杂乱复杂.今天,我就以我个人的学习经验写一篇详细又易懂的总结文章,希望大家看完之后会有所收获. 一.核心功能 ‘r’ ...

  5. Python文件读写模式

    r 打开只读文件,该文件必须存在. r+ 打开可读写的文件,该文件必须存在.可读,可写,可追加. w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失.若文件不存在则建立该文件. w+ 打 ...

  6. 正确理解Python文件读写模式字w+、a+和r+

    w+ 和 r+的差别不难理解.还有a+ +同一时候读写,就可以读又可写,边写边读.边读边写,不用flush,用seek 和 tell可測得. fp = open("a.txt", ...

  7. python 文件读写模式区别,以及如何边写入边保存flush()

    如表: 模式 可做操作 若文件不存在 是否覆盖 r 只能读 报错 - r+ 可读可写 报错 是 w 只能写 创建 是 w+ 可读可写 创建 是 a 只能写 创建 否,追加写 a+ 可读可写 创建 否, ...

  8. Python之文件读写

    本节内容: I/O操作概述 文件读写实现原理与操作步骤 文件打开模式 Python文件操作步骤示例 Python文件读取相关方法 文件读写与字符编码 一.I/O操作概述 I/O在计算机中是指Input ...

  9. PHP文件读写操作之文件写入代码

    在PHP网站开发中,存储数据通常有两种方式,一种以文本文件方式存储,比如txt文件,一种是以数据库方式存储,比如Mysql,相对于数据库存储,文件存储并没有什么优势,但是文件读写操作在基本的PHP开发 ...

随机推荐

  1. string与QString之间的转换(两种方法:fromStdString直接转换,或者fromLocal8Bit(cstr.c_str())

    string str;QString qstr; //从QString 到 std::stringstr = qstr.toStdString(); //从std::string 到QStringqs ...

  2. linux使用.rpm包安装mysql

    一:下载mysql的.rpm安装包 点击链接查看下载教程:点击打开链接 二:创建目录,上传文件 创建mysql目录:# mkdir mysql 进入目录:# cd mysql 将下载好的MySQL-s ...

  3. 【????】最短路(short)

    问题描述: 给出N个点,M条无向边的简单图,问所有点对之间的最短路. 数据输入: 第1行两个正整数N,M(N<=100,M<=5000) 下面M行,每行3个正整数x, y, w,为一条连接 ...

  4. cocos2d-x之道~制作第一款文字游戏(二)

    在 cocos2d-x之道~制作第一款文字游戏(一)中,使用cocos2d-x把主界面显示出来.分别有每一个级别提供的初始短语TileView,和目标短语TargetView.初步接触了cocos2d ...

  5. solrj 7.x Expected mime type application/octet-stream but got text/html.

    出现这种情况是因为baseurl填写错误,最开始的时候我写的是用tomcat启动后浏览器中访问solr的地址 结果就出现了如题的异常,当然提示的是404,还有可能提示405,Method not al ...

  6. 征服OA 飞鱼工作流程的在线培训课程(两)HTML形成基于

    表HTML的重要作用,等效混凝土框架建筑的行,于div在此之前流行.是否所有形式的世界.在角色表页面主要是针对页面布局和定位.通过整合人才规划表设计出合理的页面布局. 当然.更重要的是,存在是表示数据 ...

  7. POJ 1418 基本操作和圆 离散弧

    Viva Confetti Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 761   Accepted: 319 Descr ...

  8. JS高级程序设计拾遗

    <JavaScript高级程序设计(第三版)>反反复复看了好多遍了,这次复习作为2017年上半年的最后一次,将所有模糊的.记不清的地方记录下来,方便以后巩固. 0. <script& ...

  9. Linux的设备文件名与硬盘分区已经挂载点的关系

    以CentOS6.3为例. 选择的硬盘设备名是/dev/sda,即第一块STAT硬盘,然后在该硬盘分了3个主分区和1个扩展分区,设备名分别是/dev/sda1,/dev/sda2,/dev/sda3, ...

  10. Qt实用技巧:使用QTableView、QSqlTableMode与QSqlDatabase对数据库数据进行操作

    本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78615800 Qt实用技巧:使用QTableView.QSqlTableMode与Q ...