浅谈文本文件与二进制文件 Shepard-Wang

在学习 C 语言时,我们知道:

一 文本文件与二进制文件

<stdio.h> 支持两种文件类型:文本文件二进制文件

文本文件(text file)中,字节表示字符。(C程序的源代码就储存在文本文件中)

二进制文件(binary file)中,字节不一定表示字符;字节组还可以表示其它类型的数据,比如整数与浮点数(可执行C程序存贮在二进制文本中)

文本文件中二进制文件没有的特性:

1.文本文件分为若干行

文本文件的每一行通常以一两个特殊字符结尾。特殊字符与操作系统有关:

Windows中,行末标记是回车符(’\x0d’)+ 换行符(‘\x0a’)

Unix 与新的MacOS中,行末是一个单独的换行符

2.文本文件可以包含一个特殊的“文件末尾”标记

一些操作系统允许在文本文件的末尾使用一个特殊字节作为标记。在Windows中,标记为 ‘\x1a’ (Ctrl + Z)。

Ctrl + Z 不是必须的,但如果存在, 它就标志着文件的结尾,其后所有的字节都被忽略。

大多数其他操作系统(包括UNIX)没有专门的文件末尾符

二进制文件不分行,也没有行末标记和文件末尾标记 ,所有字节都是平等对待的

例1:向文件写入数据时,我们需要考虑按文本格式存储还是按二进制格式进行存储。为了搞清其中的差别,考虑在文件中存储数32767的情况。

img点击并拖拽以移动

从上例可以看出:

二进制形式存储数据可以节省相当大的空间

总结:

在屏幕上显示文件内容的程序可能要把文件视为文本文件。但是,文件复制程序就不能认为要复制的文件为文本文件(考虑到文本文件可能含有文件末尾字符,这样就不能复制完全)。

在无法确定文件是文本文件还是二进制文件时,安全的做法是把文件假定为二进制文件。

二 C 与 C++ 对文本文件和二进制文件的操作

1. NotePad++ 的 HexEditor 插件

使用 NotePad++ 的 HexEditor 插件可以在文件中看到数据编码前的格式。

2. C

文本文件
void test6pp()
{
	FILE* fp = fopen("text.c.txt", "w+");

	struct Student s = { "Shepard Wang", 20, "man" };

	fprintf(fp, "%s%d%s", s.name, s.age, s.gender);
}

我们打开text.c.txt文件,看到:

写入文件成功,我们再来试试读取:

void test6pp()
{
	FILE* fp = fopen("text.c.txt", "w+");

	struct Student s = { "Shepard Wang", 20, "man" };

	fprintf(fp, "%s%d%s", s.name, s.age, s.gender);

	fclose(fp);

    // 读取
	FILE* fp2 = fopen("text.c.txt", "r");

	struct Student s2;
	
	// 读取出现错误
	// s2.name 是 Shepard,遇到空格结束读取
	fscanf(fp2, "%s%d%s", s2.name, &s2.age, s2.gender);

	fclose(fp2);
}

取消姓名中的空格:

struct Student s = { "ShepardWang", 20, "man" };

再试试?发现 s2.name 把所有数据都读完了,因此,在写文件时,最好给不同的数据项之间加上空格或换行:

fprintf(fp, "%s %d %s", s.name, s.age, s.gender);

这下就可以正确的读取了。

二进制文件
void test6ppp()
{
	FILE* fp = fopen("binary.c.txt", "w+");

	struct Student s = { "ShepardWang", 20, "man" };

	fwrite(&s, sizeof(s), 1, fp);

	fclose(fp);
}

注意一下文本文件和二进制文件中 20 的存储格式:

就像上面 中说的那样。

读取二进制文件:

void test6ppp()
{
	FILE* fp = fopen("binary.c.txt", "w+");

	struct Student s = { "ShepardWang", 20, "man" };

	fwrite(&s, sizeof(s), 1, fp);

	fclose(fp);

	FILE* fp2 = fopen("binary.c.txt", "r");

	struct Student s2;

	fread(&s2, sizeof(s2), 1, fp2);

	fclose(fp2);
}

我们可以二进制文件可以整块的读取,效率高(而且不用操心空格换行

3. C++

文本文件
void test6p()
{
	// 文本格式写文件
	ofstream text("text.cpp.txt");
	
	struct Student s = { "Shepard_Wang", 20, "man" };

	text << s.name << " " << s.age << " " << s.gender << endl;

	text.close();

	ifstream text2("text.cpp.txt");
	
	struct Student s2;

	text2 >> s2.name >> s2.age >> s2.gender;

	text2.close();
}
二进制文件
void test6cpppp()
{
	ofstream text("binary.cpp.txt");

	struct Student s = { "Shepard_Wang", 20, "man" };

	text.write((char*)&s, sizeof(s));

	text.close();

	ifstream text2("binary.cpp.txt");

	struct Student s2;

	text2.read((char*)&s2, sizeof(s2));

	text2.close();
}

三 思考

1. 能否用 read 读取文本文件?或者用 » 读取二进制文件?

对应的 C 语言的问题就是 “能否用 fscanf 读取二进制文件?能否用 fread 读取文本文件?”

文本文件和二进制文件的存储方式不同:

文本文件是按照字符存储,数据项用空字符隔开;而二进制文件是按照字节存储。

如果混用,肯定会造成混乱。