本文章结合几篇文章,加上自己的理解和实验完成。
其实,从本质上来说,所有文件都是二进制文件,也就是用0101这种形式存储。文本文件是特殊的二进制文件,我也称它为字节文件。将要存储的东西利用ASCII编码,然后存储到文件中,例如ABCD存储为01000000_01000001_01000010_01000011"(下划线''_'',是我为了增强可读性,而手动添加的),而5678存储为00110101_00110110_00110111_00111000,这两个例子都是将要存储的字符翻译成ASCII码值,然后存储到文件。而二进制文件则是根据值编码,例如我要存储5678,编程经验告诉我们,一个int值就能表示5678,00000000_00000000_00010110_00101110,这是一个int的表示形式,那就将这个存到二进制文件中去。这就是文本文件和二进制文件的区别,不难,如果二进制文件存储ABCD呢,那么也是ASCII码值存储到文件中。
下面,引用一个网友的例子。来证明。
struct Student
{
int num;
char name[20];
float score;
};
//使用二进制写入
void write_to_binary_file()
{
struct Student stdu;
stdu.num = 111;
sprintf_s(stdu.name,20,"%s","shine");
stdu.score = 80.0f;
fstream binary_file("test1.dat",ios::out|ios::binary|ios::app); //此处省略文件是否打开失败的判断
binary_file.write((char *)&stdu,sizeof(struct Student));//二进制写入的方式
binary_file.close();
}
//文本格式写入
void write_to_text_file()
{
struct Student stdu;
stdu.num = 111;
sprintf_s(stdu.name,20,"%s","shine");
stdu.score = 80.0f;
FILE *fp = fopen("test2.dat","a+"); //此处省略文件是否打开失败的判断
fprintf(fp,"%d%s%f",stdu.num,stdu.name,stdu.score); //将数据转换成字符串(字符串的格式可以自己定义)
fclose(fp);
}
//MAIN函数调用前面两个方法
int _tmain(int argc, _TCHAR* argv[])
{
write_to_binary_file();
write_to_text_file();
return 0;
}
上面代码的意思是相同的一份struct类型分别用写入文本文件和二进制文件。
这个struct是{111,"shine",80.00000};
然后分布把这两个文件打开(用UltraEdit打开),然后以十六进制方式查看文件:结果如图
二进制文件:
6F 00 00 00这其实是111,一个int型4个字节,没错,然后,为什么6F其实就等于111,为什么在前面呢?因为跟大小端存储有关,Windows是小端存储,即低字节段存储在低地址,而存储是从低地址开始存储的,所以6F先存储,后面依次是高地址。
文本文件
文本文件比较好理解:1的ASCII是31(十六进制),73 68 69 6E 65是shine的ASCII,后面就是80.00000的ASCII。
至此这个实验很好的讲解了二进制文件和文本文件的区别了。
最后讲解一个比较细节的东西。
简单说就是回车换行操作,以二进制文件打开是"\r\n",以文本文件打开是"\n"。
实验结果如下:我们新建了test.txt,文件内容:
若以二进制文件打开则是如下图:
0D 0A就是回车换行了,即"\r\n",那上上图也没表面,以文本文件打开,回车换行是"\n"啊?对,问的好,以文本文件形式打开,确实看不出来,只能通过程序显示出来。
#include<iostream>
#include<string>
#include<fstream>
#include<locale>
#include<strstream>
#include<stdio.h>
#include<sstream>
using namespace std;
int main(){
string *s=new string[2];
char *c=new char[100];
string tmp="\n";
ifstream in("test.txt");
{
for(int i=0;i<2;i++){
in.getline(c,100,'d');
s[i]=c;
}
}
in.close();
const char *cc;
cc=s[1].c_str();
cout<<(int)cc[0]<<endl;
return 0;
}
运行结果:
因为上面s[1]=“回车”+1234,然后我把s[1]赋值给字符数组型的的cc,然后我输出cc[0],即输出回车的第一个字符,显示的是10(十进制),就是0A(十六进制),那你怎么知道cc[1]不是0D呢,因为Windows下的回车一般是"\r\n",即0D 0A,所以cc[0]输出了0D,即'\n',那么就证明我们的猜想了。
综上,C的文本方读写与二进制读写的差别仅仅体现在回车换行符的处理上.文本方式写时,每遇到一个''\n''(0AH换行符),它将其换成''\r\n''(0D
0AH,回车换行),然后再写入文件;当文本读取时,它每遇到一个''\r\n''将其反变化为''\n'',然后送到读缓冲区.正因为文本方式有''\n''--''\r\n''之间的转换,其存在转换耗时.二进制读写时,其不存在任何转换,直接将写缓冲区中数据写入文件.
总地来说,从编程的角度来说,C中文本或二进制读写都是缓冲区与文件中二进制流的交互,只是文本读写时有回车换行的转换.所以当写缓冲区中无换行符''\n''(0AH),文本写与二进制写的结果是一样的,同理,当文件中不存在''\r\n''(0DH0AH)时,文本读与二进制读的结果一样.
容易发生的错误:以文本文件写文件,再以二进制方式读,每行多一个0D;反之以二进制方式写文件,以文本方式读文件,不会出错,这是区别,可以用UltraEdit进行转换。