标签:
原来没有仔细注意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值的话可能会有问题。
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 using namespace std; 5 int main() 6 { 7 8 string str1 = "hello!\n"; 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);//以\n作为换行 25 getline(fin, temp2); 26 cout << "二进制模式getline读入file1的长度(结尾包含了\\r):" << temp2.length() << endl; 27 fin.close(); 28 29 ofstream fout2("file2"); 30 fout2 << "hello!\r\n"; 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
【C++】小心使用文件读写模式:回车('\r') 换行('\n')问题的一次纠结经历
标签:
原文地址:http://www.cnblogs.com/ascii0x03/p/4838671.html