关于C之二进制模式与文本模式

用二进制模式打开一个文件的时候,文件本身的内容和你编写程序时用函数读到的内容完全相同(或者说和磁盘上的内容完全相同)。

实际上, 所有的数据都是以二进制形式储存的, 甚至连字符都以字符码的二进制表示来储存。 如果文件中的所有数据都被解释成字符码, 则称该文件包含文本数据。 如果部分或所有的数据都被解释成二进制形式的数值数据, 则称该文件包含二进制数据( 另外, 用数据表示机器语言指令的文件都是二进制文件) 。

但是如果用了文本模式,那么操作系统在将文件内容传给上层程序(库函数,或者是你的程序)时,或者上层程序通过操作系统向文件写入内容时,操作系统都会预先进行一层预处理(或者说转义),具体过程依赖于操作系统的实现。在Windows+VC下,最常见就是将回车符"\r\n"(没有引号,且\作转义符用,下同)解释成"\n"(读出时),将"\n"解释成"\r\n"(写入时)。而在Linux下没有这层转换,这也是Windows和Linux文本文件不通用的原因。比如一个TXT文件在Windows下,"\r\n"就当作一个换行符读取,而在Linux下就当作一个回车加换行读取了。

下面用两种方式打开一个文件“file.dat",内容为"ABC\r\nDEF":

1.以文本方式打开并读取:

fp = fopen("file.dat","r");
while(fgetc(fp)!=EOF);

得到的内容为:ABC\nDEF

2.以二进制方式打开并读取:

fp = fopen("file.dat","rb");
while(fgetc(fp)!=EOF);

得到的内容为:ABC\r\nDEF

可以看到两种打开方式,得到的内容不一致。

C的文本读写与二进制读写的差别仅仅体现在回车换行符的处理上。文本方式写时,每遇到一个''\n''(0AH换行符),它将其换成''\r\n''(0D0AH,回车换行),然后再写入文件;当文本读取时,它每遇到一个''\r\n''将其反变化为''\n'',然后送到读缓冲区。正因为文本方式有''\n''--''\r\n''之间的转换,其存在转换耗时。二进制读写时,其不存在任何转换,直接将写缓冲区中数据写入文件。

二进制读写是将内存里面的数据直接读写入文本中;而文本呢,则是先将数据转换成字符串,再写入到文本中。

下面用一个例子说明二者的差别:

#define _CRT_SECURE_NO_WARNINGS
#define SIZE 21
#include <stdio.h>
void write_to_binary_file();
void write_to_text_file();
void read_from_binary_file();
void read_from_text_file();

struct Student {
  int num;
  char name[SIZE];
  float score;
};

//使用二进制写入
void write_to_binary_file() {
  struct Student stdu;
  stdu.num = 111;
  sprintf_s(stdu.name, SIZE, "%s", "shine");
  stdu.score = 80.0f;
  FILE *fp = fopen("test_b.dat", "a+b");
  fwrite(&stdu, sizeof(struct Student), 1, fp);
  fclose(fp);
}

//文本格式写入
void write_to_text_file() {
  struct Student stdu;
  stdu.num = 111;
  sprintf_s(stdu.name, SIZE, "%s", "shine");
  stdu.score = 80.0f;
  FILE *fp = fopen("test_t.dat", "a+");
  fprintf(fp, "%d%s%f", stdu.num, stdu.name, stdu.score); //将数据转换成字符串(格式可自己定义)
  fclose(fp);
}

//从二进制读取
void read_from_binary_file() {
  FILE *fp = fopen("test_b.dat", "a+b");
  struct Student stdu;
  fread(&stdu, sizeof(struct Student), 1, fp);  //注:结构体方式写入就结构体方式读出
  printf("binaryContent=%d%s%f", stdu.num, stdu.name, stdu.score);
  fclose(fp);
}

//从文本读取
void read_from_text_file() {
  char ch;
  FILE *fp = fopen("test_t.dat", "a+");
  printf("textContent=");
  while ((ch = getc(fp)) != EOF) {
    putchar(ch);
  }
}

int main() {
  write_to_binary_file();
  write_to_text_file();

  read_from_binary_file();
  printf("\n");
  read_from_text_file();
  return 0;
}
binaryContent=111shine80.000000
textContent=111shine80.000000

运行程序后在可执行文件目录下生成两个文件test_b.dat和test_t.dat:

分别用二进制器和文本编辑器打开看看:

test_t.dat:

test_b.dat:

对于二进制模式int num=111,刚好等于16进制的6F,1个字节(因为111<127也可推出1个字节);而对于文本模式111就变成字符串"111"了,就是3个'1'(0x31表示),在文本文件中写成31,31,31用了3个字节(因为"111"是三个字符表示也可推出3个字节)。二者前面中间有一段相同的16进制编码7368696E65,对应的就是shine。而最后的是一个浮点类型,对于文本模式,80.000000直接对应ASCII码的38302E303030303030,而二进制模式是按国际标准IEEE 754表示浮点数的(详见:关于整数与浮点数二进制表示),保证精度。

test_t.dat是以文本数据数据存储的,可以看到每个数据都被表示成了字符码。 而test_b.dat是以二进制数据存储的,可以看到每个数据都被表示成了二进制形式的数值数据,真实还原了结构体成员变量{int num; char name[SIZE]; float score;}的数据类型值,int num=111=0x6F,char name=0x7368696E65(本就是字符串这和文本模式下是一致的),float score=80.000000F。

文本模式下,由于每个数据都表示成1个字符码,而1个字符占用1个字节,所以字符串的长度就是占用的字节数,很浪费空间。二进制模式下,int、long、float、double这样的类型,分别也就占4、4、4、8字节(32位系统下),可成倍减少占用的空间,并且可以精确还原成相应的类型值。

下图很直观展示了两种模式下的存取区别:

例如:double num = 1./3.; fprintf(fp,"%f", num); 把num储存为8个字符: 0.333333。 使用%.2f转换说明将其储存为4个字符:0.33,用%.12f转换说明则将其储存为 14 个字符:0.333333333333。改变转换说明将改变储存该值所需的空间数量,也会导致储存不同的值。把num 储存为 0.33 后,读取文件时就无法将其恢复为更高的精度。一般而言,fprintf()把数值转换为字符数据,这种转换可能会改变值。
为保证数值在储存前后一致,最精确的做法是使用与计算机相同的位组合来储存。因此,double 类型的值应该储存在一个 double 大小的单元中。如果以程序所用的表示法把数据储存在文件中,则称以二进制形式储存数据。不存在从数值形式到字符串的转换过程。对于标准 I/O,fread()和 fwrite函数用于以二进制形式处理数据。

二进制和文本的用法很容易混淆。 ANSI C和许多操作系统都识别两种文件格式: 二进制和文本。 能以二进制数据或文本数据形式存储或读取信息。 可以用二进制模式打开文本格式的文件, 可以把文本储存在二进制形式的文件中。 可以调用 getc()拷贝包含二进制数据的文件。 然而, 一般而言,用二进制模式在二进制格式文件中储存二进制数据。 类似地, 最常用的还是以文本格式打开文本文件中的文本数据( 通常文字处理器生成的文件都是二进制文件, 因为这些文件中包含了大量非文本信息, 如字体和格式等)。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值