1. 文本文件
想想看:电脑其实啥都存成1和0(或者说,不怕这个词的话——就是一串字节)。但如果你把同一个文件给两个程序用,它们可能反应完全不一样。比如一个程序能看到正常文本,另一个就只看到一堆“乱码字节”。这就是文件格式的问题——也就是这些字节到底怎么解释。格式描述了数据的内部结构、读取规则和编码方式。程序如果不知道格式,可能根本搞不懂文件里存的啥。
文本文件——就是那种数据直接用普通文本存的文件,用某种编码(最常见是UTF-8,或者硬核点用ASCII)。这种文件你用任何文本编辑器(比如Notepad或者VS Code)都能打开直接看——就是一行行字符,不是什么神秘的控制字节或者二进制结构。
常见例子
- *.txt —— 普通文本文件,比如“to-do.txt”或者“best-books.txt”。
- *.csv —— 数据用逗号或者分号分开的文件(很适合表格)。
- *.json —— 用来交换结构化数据的文件(比如书单)。
- *.xml,*.html —— 用来存和传结构化数据。
CSV文件例子
沙丘;弗兰克·赫伯特;1965
一九八四;乔治·奥威尔;1949
大师与玛格丽塔;米哈伊尔·布尔加科夫;1966
每一行就是一条记录(比如一本书)。行里的值用分号分开,这就是CSV(Comma-Separated Values)的例子。Comma本来是逗号,但这里用别的分隔符(;)。人能看到的字符,其实就是程序读到的字节,只要程序知道怎么解释文本格式和文件编码。
程序怎么操作文本文件
只要“按文本”打开文件,就能一行行读。例子(以后你会用到的):
// 从文件简单读取每一行(细节后面讲)
string[] lines = File.ReadAllLines("books.txt");
foreach (string line in lines)
{
Console.WriteLine(line);
}
文本文件的优点
- 人能直接看懂(human-readable)。
- 手动编辑超方便(随便用Notepad都行)。
- 调试和不同系统之间交换数据很爽。
- 跨平台(Windows、Linux、Mac)搬来搬去没压力。
文本文件的缺点
- 结构不严格(txt),格式错了很容易出bug。
- 占空间大(比如数字12345 = 5字节,二进制只要4字节)。
- 复杂数据(比如图片、音频)用文本传很麻烦甚至不行。
- 性能:数据量大时慢。
有用小知识
- 不同平台对“换行”理解不一样:Windows用\r\n,Unix用\n。
- 编码方式可能不同(UTF-8、UTF-16、ANSI等),所以一个系统写的文件在另一个系统可能全是“乱码”(其实就是字节解释错了)。
2. 二进制文件
二进制文件——文件里存的不是字符,而是任意字节。可以是数字、结构体、图片、音乐,甚至机器码。你试试用记事本打开.exe,全是看不懂的鬼画符!
常见例子
- *.exe, *.dll —— 可执行文件和库。
- *.jpg, *.png, *.gif—— 图片。
- *.mp3, *.wav—— 音频文件。
- *.dat —— 任意自定义数据文件。
- 应用专用格式(*.docx、*.xlsx等)——几乎都是二进制。
二进制文件是怎么存的?
程序一字节一字节地按自己需要的格式写和读数据。比如int类型的数字就是4字节(32位),字符串一般是长度+字符的字节(按某种编码)。
在文本文件里,数字12345就是字符'1'、'2'、'3'、'4'、'5'对应的字节:
| 字符 | UTF-8编码 |
|---|---|
| 1 | 49 |
| 2 | 50 |
| 3 | 51 |
| 4 | 52 |
| 5 | 53 |
在二进制文件里,int 12345(小端序,little-endian)是这样:
| 位置 | 字节(hex) | 值 |
|---|---|---|
| 0 | 0x39 | 57 |
| 1 | 0x30 | 48 |
| 2 | 0x00 | 0 |
| 3 | 0x00 | 0 |
(也就是说:12345 = 0x00003039。)
你可以试试把同一个数字用文本和二进制方式写到文件里——对比下文件大小。二进制会更小。
程序怎么操作二进制文件
要用专门的流(比如BinaryReader、BinaryWriter)。和文本文件不一样,必须完全知道结构,否则根本读不出来。
// 把数字写进二进制文件的例子
var writer = new BinaryWriter(File.Open("books.bin", FileMode.Create));
// 比如写字符串:先写字符数(int),再写字符的字节
string title = "沙丘";
writer.Write(title.Length); // 写长度(int)
writer.Write(title); // 写字符串(会用UTF-8)
writer.Write(1965); // 写年份(int)
writer.Close(); // 关闭流
这只是个例子。后面几节课我会详细讲二进制文件怎么搞。
二进制文件的优点
- 更省空间:占用更小。
- 读写速度快(尤其数据量大时)。
- 能存复杂结构:数组、嵌套对象啥的都行。
二进制文件的缺点
- 人看不懂(human-unreadable)。
- 结构变了,老数据可能全废或者“炸了”。
- 必须有详细的解析说明才能读。
3. 选哪种格式?
文本 vs 二进制文件
| 对比点 | 文本文件 | 二进制文件 |
|---|---|---|
| 人能看懂 | 能 | 不能 |
| 文件大小 | 大 | 小 |
| 通用性 | 高(随便编辑器) | 只能用专门程序 |
| 速度 | 慢 | 快 |
| 格式严格 | 不严格 | 必须严格 |
| 存结构 | 难 | 容易 |
| 数据损坏风险 | 低(能手动修) | 高(出点错就全废) |
怎么选
- 想让数据人能看懂、方便改、方便调试?——用文本文件(比如.txt、.csv、.json)。
- 数据复杂、对大小和速度有要求?——二进制格式。
- 要跨平台交换数据?——文本更合适,但编码要用标准的(UTF-8)。
- 程序要读写大量数据,而且格式很严格?——选二进制。
我们做的“书单”控制台小程序,一开始用文本格式(.csv或.txt)就很合适。以后结构复杂了或者想加快加载速度,可以学二进制文件,或者用JSON/XML。
4. 有用的细节
生活中哪里会遇到文本和二进制文件?
文本文件:
- 程序配置(经常是.ini或.json)。
- 脚本、日志、笔记。
- 导入/导出文件(应用间数据交换)。
二进制文件:
- 程序、游戏、库(一般人不会去看)。
- 大型数据库和压缩包(比如.mdb、.zip)。
- 多媒体:音乐、照片、视频。
一次性读完整个文件(文本方式):
string allText = File.ReadAllText("books.txt");
// 现在allText就是整个文件内容的大字符串
按行读(适合列表):
string[] lines = File.ReadAllLines("books.txt");
foreach (string line in lines)
{
// 可以把一行拆成几部分
string[] parts = line.Split(';');
// parts[0] —— 书名,parts[1] —— 作者,parts[2] —— 年份
}
二进制读取——必须按结构来
var reader = new BinaryReader(File.Open("books.bin", FileMode.Open));
int count = reader.ReadInt32(); // 书的数量
for (int i = 0; i < count; i++)
{
string title = reader.ReadString();
string author = reader.ReadString();
int year = reader.ReadInt32();
// 现在可以还原一条记录了!
}
reader.Close();
5. 实战例子:创建书单文件
把数据存到文本文件
我们每本书存成一行,格式是:
书名;作者;第一版年份
// 我们要保存的书单
var books = new List<(string Title, string Author, int Year)>
{
("沙丘", "弗兰克·赫伯特", 1965),
("一九八四", "乔治·奥威尔", 1949),
("大师与玛格丽塔", "米哈伊尔·布尔加科夫", 1966)
};
// 保存到文本文件
var writer = new StreamWriter("books.txt");
foreach (var book in books)
writer.WriteLine($"{book.Title};{book.Author};{book.Year}");
writer.Close();
你可以用books.txt用记事本打开,直接看到所有数据。
把数据存到二进制文件
现在我们试试更紧凑的方式(虽然人看不懂):
var writer = new BinaryWriter(File.Open("books.bin", FileMode.Create));
writer.Write(books.Count); // 写书的数量
foreach (var book in books)
{
writer.Write(book.Title); // 字符串:长度+字节
writer.Write(book.Author); // 字符串
writer.Write(book.Year); // 年份(int)
}
writer.Close();
你要是用文本编辑器打开books.bin,看到的就是一堆乱码。这很正常。二进制文件本来就不是给人看的——只有知道怎么写的程序才能正确处理。
6. 操作格式时常见的坑
你要是把二进制文件当文本读,看到的就是一锅粥或者直接报错。反过来,把文本文件当二进制读、还想有严格结构——也会出问题。
最常见的坑:数据结构和读写顺序不一致。比如先写了年份再写书名,结果读的时候顺序反了——拿到的就不是你想要的内容。一定要保证操作顺序一致!
很多程序员喜欢“偷看”文本文件,检查保存和加载逻辑——这也是文本格式新手阶段特别方便的原因。二进制更适合进阶用,或者你想让用户看不到存储细节。
GO TO FULL VERSION