原来没有仔细注意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. php实现求一个数的质数因子

    php实现求一个数的质数因子 一.总结 一句话总结:这么简单的题目,还是把变量定义的位置和自增的位置写错. 1 <?php 2 $num=trim(fgets(STDIN)); 3 //如果$n ...

  2. 一大波Java来袭(四)String类、StringBuilder类、StringBuffer类对照

    本文主要介绍String类.StringBuffer类.StringBuilder类的差别  : 一.概述 (一)String 字符串常量.可是它具有不可变性,就是一旦创建,对它进行的不论什么改动操作 ...

  3. Jquery前端分页插件pagination同步加载和异步加载

    上一篇文章介绍了Jquery前端分页插件pagination的基本使用方法和使用案例,大致原理就是一次性加载所有的数据再分页.https://www.jianshu.com/p/a1b8b1db025 ...

  4. Oracle中的sid与servicename

    在平时自己创建库的时候,习惯的将sid与servicename取同名,但是在实际生产中这两个名字很有可能不一致,再使用jdbc连接数据库的url时就需要注意区分了,否则创建不了数据库连接 //serv ...

  5. 【9602】&&【b402】合并果子

    Time Limit: 1 second Memory Limit: 50 MB [问题描述] 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成 ...

  6. 模块化模式与 OSGi

    模块化模式与 OSGi Android 模块化探索与实践 一.前言 万维网发明人 Tim Berners-Lee 谈到设计原理时说过:“简单性和模块化是软件工程的基石:分布式和容错性是互联网的生命.” ...

  7. 【u115】&&【t031】 01迷宫

    01迷宫(maze01) Time Limit: 1 second Memory Limit: 128 MB [问题描述] 有一个仅由数字0与1组成的n×n格迷宫.若你位于一格0上,那么你可以移动到相 ...

  8. 前端自动化之路之gulp,node.js

    随着现在前端技术的不断发展,和各个公司对前端项目开发更新速度的要求,前端自动化越来越受到大家的重视,之前传统的前端开发方式已经越来越不能满足开发的需求了,于是各种自动化工具随之产生了.而gulp就是其 ...

  9. JAVA SE回顾及思考(1)——面向对象的特点

    学习Java已经三年了,现在开始做Android开发,虽说还在用Java语言但本人现在才真真的意识到无论学什么基础才是最重要的,可能一些刚接触Java或者Android的朋友现在还体会不到基础的重要性 ...

  10. 在WSL中安装和运行Docker CE

    安装环境 Windows 10 64bit 专业版 1809 WSL安装ubuntu 18.04 LTS DOCKER安装 安装过程没有什么问题,安装docker的网站说明进行安装https://do ...