原来没有仔细注意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. uml精粹——11.活动图(及整个读书笔记分享)

    11.活动图activity diagram   活动图是描写叙述过程化逻辑procedural logic.业务过程business process和工作流work flow的技术. 他和流程图fl ...

  2. Matlab Tricks(二十五)—— plot 属性

    marker: 边缘:'MarkerEdgeColor', [],(RGB 配色) 填充:'MarkerFaceColor', [](RGB 配色)

  3. NOIP模拟 run - 双向链表

    题目大意: 企鹅国正在举办全面运动会,第一项比赛就是跑步.N 个人在圆形跑道上跑步,他们都有各自的速度和起点.但这个跑步规则很奇怪,当两个人相遇的时候编号较小的就会出局,当场上剩下最后一个人的时候跑步 ...

  4. 与Boss大雷探讨JavaWeb开发、电商与网络安全

    最近几个月,与公司Boss大雷交流得比较多,也学习到了很多新的东西,了解到了一些没有接触和实践的业界做法. 简要介绍下Boss,姓雷,定居武汉好几年了,之前在一号店.UC.支付宝干过,有丰富的电商-支 ...

  5. 《Windows核心编程》之“完成端口”(对所有IO都是如此,不仅仅是对socket)

    <Windows核心编程>第10章开头部分一再强调:“IO Completion Port”是“构建高性能.可升缩的应用程序”的最佳设施之一,它不仅适用于处理设备IO,也适用于其它越来越多 ...

  6. 【codeforces 791A】Bear and Big Brother

    [题目链接]:http://codeforces.com/contest/791/problem/A [题意] 给你两个数字a和b; a每次乘3,b每次乘2 问你什么时候a第一次大于b [题解] 傻逼 ...

  7. java序列化框架(protobuf、thrift、kryo、fst、fastjson、Jackson、gson、hessian)性能对比

     我们为什么要序列化 举个栗子:下雨天我们要打伞,但是之后我们要把伞折叠起来,方便我们存放.那么运用到我们java中道理是一样的,我们要将数据分解成字节流,以便存储在文件中或在网络上传输,这叫序列 ...

  8. Configuring a remote m-phy

    An interface for low power, high bandwidth communications between units in a device in provided here ...

  9. Node child_process Study.2

    child_process 模块用于新建子进程.子进程的运行结果存储在系统缓存之中,等到子进程运行结束之后,主进程再用回调函数读取子进程的运行结果 1.exec() exec 方法用于执行base命令 ...

  10. Android 事件分发机制具体解释

    很多其它内容请參照我的个人网站: http://stackvoid.com/ 网上非常多关于Android事件分发机制的解释,大多数描写叙述的都不够清晰,没有吧来龙去脉搞清晰,本文将带你从Touch事 ...