二进制文件
-
定义: 二进制文件就是把内存中的数据按其在内存中存储的形式原样输出到磁盘中存放,即存放的是数据的原形式。二进制文件是包含在 ASCII 及扩展 ASCII 字符中编写的数据或程序指令的文件。一般是可执行程序、图形、声音等文件,有自己特殊的编解码格式。不同的应用程序对二进制文件中的每个值会有不同的解读,要打开二进制文件需要对应的二进制文件解码器。
-
读取流程: 用记事本打开二进制文件的流程是怎样的呢?记事本无论打开什么文件都按既定的字符编码工作(如ASCII码),用ASCII码的规则去解读二进制文件时,会出现乱码。所以当他打开二进制文件时,出现乱码也是很必然的一件事情,解码和译码不对应。例如文件流”00000000_00000000_00000000_00000001”
可能在二进制文件中对应的是一个四字节的整数int 1,在记事本里解释就变成了”NULL_NULL_NULL_SOH”
这四个控制符。文本文件将浮点数80.000000用了38(表示8) 30(表示0) 2E(表示.) 30(表示0) 30(表示0) 30(表示0) 30(表示0) 30(表示0) 30(表示0),二进制文件用了4个字节表示浮点数00 00 A0 42。字符型的内容都是ASCii码的形式,没有区别。
-
例子: 在计算机中,所有的颜色都可以映射为一个二进制的值。图片存储时,图片上每个点都有自己的颜色值,将每个点的颜色值,以及图片本身的宽高信息储存起来,就是最基本的位图存储(bmp),位图存储是没有压缩的。将位图信息,经过二次编码,压缩就形成了压缩后的图片。算法不同产生的图片格式也有区别。常见的包括jpg,png,gif等。文本文件基本上是定长编码的(也有非定长的编码如UTF-8)。而二进制文件可看成是变长编码的,因为是值编码,多少个比特代表一个值,完全由自定义的编解码规则决定。像BMP文件,其头部是较为固定长度的文件头信息,前2字节用来记录文件为BMP格式,接下来的8个字节用来记录文件长度,再接下来的4字节用来记录bmp文件头的长度。
-
linux 二进制的兼容性: Linux上二进制有一个显著的特点就是可移植性不强。我们在不同的发行版之间,不同的内核版本之间,程序往往是不能通用的,比如把ubunbtu下面编译的二进制可执行文件拷贝到CentOS上,基本不可能运行。原因是程序总需要使用或多或少的系统调用,系统调用是核心代码实现的,核心不同自然也就不兼容了。另外不同系统的对执行文件加载方式也不同。
如果应用对内核kernel版本有要求,则不建议使用docker,docker底层复用的host的kernel.,如果可以用docker,可以解决除kernel版本外的兼容性问题,build once run everywhere。
文本文件
-
定义: 文本文件是把数据的终端形式的二进制数据输出到磁盘上存放,即存放的是数据的终端形式. 文本文件(也称为ASCII文件):它的每一个字节存放的是可表示为一个字符的ASCII代码的文件。它是以 “行”为基本结构的一种信息组织和存储方式的文件,可用任何文字处理程序阅读的简单文本文件。
-
读取流程: 文本工具打开一个文件的过程是怎样的呢?拿记事本来说,它首先读取文件物理上所对应的二进制比特流,然后按照你所选择的解码方式来解释这个流,然后将解释结果显示出来。一般来说,你选取的解码方式会是ASCII码形式(ASCII码的一个字符是8个比特),接下来,它8个比特8个比特地来解释这个文件流。例如对于这么一个文件流”01000000_01000001_01000010_01000011”
, 第一个8比特”01000000”按ASCII码来解码的话,所对应的字符是字符”A”,同理其它3个8比特可分别解码为”BCD”,即这个文件流可解释成“ABCD”,然后记事本就将这个“ABCD”显示在屏幕上。
-
选择: 如果是需要频繁的保存和访问数据,那么应该采取二进制文件进行存放,这样可以节省存储空间和转换时间。
如果需要频繁的向终端显示数据或从终端读入数据,那么应该采用文本文件进行存放,这样可以节省转换时间。
-
区别: window 文本模式中,在存储\n时要转化为\r\n,读取文件时进行逆转换。window二进制模式时,则不进行转换。
-
^m原因: 回车和换行是不相同的,Windows中文字另起一行内存表示为<回车><换行>对应\r\n,而Linux、Unix中则是单单一个<换行>对应\n。Mac中则是一个<回车>对应\r。C语言起源于Unix的产生,对于文件中的另起一行,Unix采用单个\n表示,我们通常在输出换行时加一个\n就可以了,Windows也是如此。到了数据从内存到硬盘上进行存储时,对于unix来说,\n照样是\n,在Linux下,二进制文件和文本文件都是以’\n’作为行结束符,所以不需要转换,而Windows为了防止读取时混乱必须转换为\r\n。Windows 格式的文本文件,用\r\n 作为换行符,而Unix 的则是以\n作为换行符,所以dos 底下的文本文件到了unix的话,换行符就会多出来一个 0D(CR) 显示为 ^M。
结构体写入文件
- 结构体写入文件:C语言把文件看作一个字符(字节)的序列,即由一个一个字符(字节)的数据顺序组成。根据数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件又称为文本(text)文件,它的每个字节放一个ASCII代码,代表一个字符。二进制文件是把内存中的数据按其在内在中的存储形式原样输出到磁盘上存放。二进制将数据在内存中的样子原封不动的搬到文件中,文本格式则是将每一个数据转换成字符写入到文件中,他们在大小上,布局上都有着区别。由此可以看出,二进制文件可以读出来直接用,但是文本文件还多一个“翻译”的过程,因此二进制文件的可移植性好。
- C语言把一个结构体数组写入文件分三步:
1、以二进制写方式(wb)打开文件
2、调用写入函数fwrite()将结构体数据写入文件
3、关闭文件指针
相应的,读文件也要与之匹配:
1、以二进制读方式(rb)打开文件
2、调用读文件函数fread()读取文件中的数据到结构体变量
3、关闭文件指针
- fwrite :函数按照指定的数据类型将矩阵中的元素写入到文件中。写二进制文件其调用格式为:
COUNT=fwrite (fd, A, precision)
其中COUNT返回所写的数据元素个数,fd为文件句柄,A用来存放写入文件的数据,precision用于控制所写数据的类型,其形式与fread函数相同。当我们按照二进制方式往文件中写入数据,则将数据在内存中的存储形式原样输出到文件中。用fwrite 写入文件的如果是字符,那么就会显示为字符,写入的是数字显示不出来,写的是二进制内容(所谓的乱码)
- fprintf :写文本文件 函数的调用格式为:
COUNT= fprintf(fd, format, A)
其中A存放要写入文件的数据。先按format指定的格式将数据矩阵A格式化,然后写入到fid所指定的文件。format用以控制读取的数据格式,由%加上格式符组成,常见的格式符有d,f,c,s。fid为文件句柄。fprintf写的是数字转换成ASCII码之后的字符。
- 当使用fwrite将一个int型数字65写入文本文件时,由于65对应的二进制数是1000001,十六进制数是0x41,存储的是以二进制的形式1000001.在notepad++中使用十六进制方式打开显示的是:0x0041,转换为十进制则为65。记事本打开可能存在乱码,因为fwrite在写入的时候是采用整字节的二进制写入,而文本编辑器采用的是ascll码显示,两者不兼容。
- 当使用fpintf将一个int型数字65写入文本文件时,将65每一位转换为ASCII码存储,6、5分别对应ASCII码54、53,存储的是ASCII码54、53.在notepad++中使用十六进制方式打开显示的是:3635,转换为十进制则为54、53,这正是数字6、5的ASCII码。使用记事本打开这个文本文件时,记事本将存储在其中的54、53当做ASCII码翻译为字符6、5显示,我们看到的是便是字符65。
- 二进制读写是将内存里面的数据直接读写入文本中,而文本呢,则是将数据先转换成了字符串,再写入到文本中。下面我用个例子来说明。
我们定义了一个结构体,表示一个学生信息,我们打算把学生的信息分别用二进制和文本的方式写入到文件中。
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;
}
二进制 图
文本文件 图
-
stdu.num = 111: 二进制文件里面将111编码成
6F
,1个字节,这刚好是111的16进制表示,而文本文件中则写成31 31 31
用了3个字节,表示111。
-
stdu.name = “shine”: 二进制和文本文件中
73 68 69 6E 65
都表示shine,这体现对字符数据处理的一致性。
-
stdu.score = 80.0f: 二进制文件里是几个连续的FE,而文本文件中是38 30…。文本文件将浮点数80.000000用了38(表示8) 30(表示0) 2E(表示.) 30(表示0) 30(表示0) 30(表示0) 30(表示0) 30(表示0) 30(表示0),二进制文件用了4个字节表示浮点数
00 00 A0 42
- 通过这里我们可以初见端倪了,二进制将数据在内存中的样子原封不动的搬到文件中,文本格式则是将每一个数据转换成字符写入到文件中,他们在大小上,布局上都有着区别。由此可以看出,二进制文件可以从读出来直接用,但是文本文件还多一个“翻译”的过程,因此二进制文件的可移植性好。