C# 二进制文件的读写(保姆级教程)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在编程领域,文件读写是开发者必须掌握的核心技能之一。对于 C# 开发者而言,掌握 C# 二进制文件的读写 技术,不仅能高效处理非文本数据(如图像、音频、自定义数据结构等),还能为开发复杂应用(如游戏存档、数据库系统、网络通信协议)奠定基础。本文将从基础概念到实战案例,逐步解析如何在 C# 中实现二进制文件的读写操作,并通过生动的比喻和代码示例,帮助读者快速掌握这一实用技能。
二进制文件与文本文件的区别:快递包裹 vs. 明信片
在理解二进制文件读写之前,我们需要先明确二进制文件与文本文件的本质差异。
文本文件(如 .txt
、.csv
)类似于“明信片”——内容以人类可读的字符形式存储,例如数字“123”直接保存为 ASCII 编码的字符序列。而 二进制文件(如 .exe
、.png
)则更像“加密的快递包裹”:数据以原始字节(0/1 组合)形式存储,不经过编码转换。这种特性使得二进制文件在存储效率和数据完整性上更具优势。
例如,一个浮点数 3.14
在文本文件中需占用 4 个字符的空间,而在二进制文件中仅需 4 个字节(32 位)。因此,当需要处理大量结构化数据时,二进制文件的读写效率更高。
C# 二进制读写的三大核心组件
在 C# 中,二进制文件的读写主要依赖以下三类类库:
1. FileStream
:文件的“快递通道”
FileStream
类是所有文件操作的基础,它负责与物理文件建立连接,并管理字节流的输入输出。
// 创建或打开文件流(以二进制模式读写)
using (FileStream fs = new FileStream("data.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
// 在此处执行读写操作
}
关键参数说明:
| 参数名 | 作用描述 |
|----------------|-------------------------|
| FileMode
| 定义文件打开模式(如新建、追加等) |
| FileAccess
| 指定访问权限(读、写或两者) |
2. BinaryReader
和 BinaryWriter
:字节的“翻译器”
这两个类专门用于二进制流的读写操作,可直接将字节序列转换为 C# 数据类型(如 int
、double
、string
)。
// 写入示例
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(123); // 写入整数
writer.Write(3.14); // 写入浮点数
writer.Write("Hello"); // 写入字符串
}
// 读取示例
using (BinaryReader reader = new BinaryReader(fs))
{
int num = reader.ReadInt32(); // 读取整数
float value = reader.ReadSingle(); // 读取浮点数
string text = reader.ReadString(); // 读取字符串
}
3. BitConverter
:字节的“变形金刚”
当需要手动操作字节时(例如跨平台兼容性处理),BitConverter
可将基本类型与字节数组相互转换。
// 整数转字节
int number = 456;
byte[] bytes = BitConverter.GetBytes(number);
// 字节转整数
int restored = BitConverter.ToInt32(bytes, 0);
二进制文件的读写步骤:从入门到实践
步骤 1:创建文件流并定位指针
在读写前,必须通过 FileStream
打开文件,并通过 Seek
方法定位读写起点:
// 定位到文件末尾(追加写入)
fs.Seek(0, SeekOrigin.End);
步骤 2:写入数据
使用 BinaryWriter
将数据转换为字节流并写入:
// 写入一个整数和字符串
writer.Write(2023);
writer.Write("C#二进制读写实战");
步骤 3:读取数据
通过 BinaryReader
按顺序读取字节并还原为原始数据:
// 读取前需确保指针位置正确
fs.Seek(0, SeekOrigin.Begin);
int year = reader.ReadInt32();
string message = reader.ReadString();
进阶技巧:结构体的二进制序列化
对于自定义数据结构(如 Person
类),可使用 BinaryWriter
手动序列化:
public struct Person
{
public int Age;
public string Name;
}
// 写入结构体
Person p = new Person { Age = 25, Name = "Alice" };
writer.Write(p.Age);
writer.Write(p.Name);
// 读取结构体
Person readP = new Person();
readP.Age = reader.ReadInt32();
readP.Name = reader.ReadString();
注意:手动序列化需严格保证读写顺序一致,否则可能导致数据错位。
实战案例:通讯录的二进制存储
场景描述
假设我们需要开发一个通讯录应用,需将联系人信息(姓名、电话、生日)以二进制格式保存到文件中。
代码实现
using System;
using System.IO;
public struct Contact
{
public string Name;
public string Phone;
public DateTime Birthday;
}
class Program
{
static void SaveContacts(Contact[] contacts)
{
using (FileStream fs = new FileStream("contacts.bin", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(contacts.Length); // 先写入联系人数量
foreach (var contact in contacts)
{
writer.Write(contact.Name);
writer.Write(contact.Phone);
writer.Write(contact.Birthday.Ticks); // DateTime 转为 Ticks 长整型
}
}
}
static Contact[] LoadContacts()
{
if (!File.Exists("contacts.bin")) return Array.Empty<Contact>();
using (FileStream fs = new FileStream("contacts.bin", FileMode.Open))
using (BinaryReader reader = new BinaryReader(fs))
{
int count = reader.ReadInt32();
Contact[] contacts = new Contact[count];
for (int i = 0; i < count; i++)
{
contacts[i].Name = reader.ReadString();
contacts[i].Phone = reader.ReadString();
contacts[i].Birthday = new DateTime(reader.ReadInt64());
}
return contacts;
}
}
static void Main()
{
Contact[] data =
{
new Contact { Name = "Bob", Phone = "138-1234-5678", Birthday = new DateTime(1990, 5, 15) },
new Contact { Name = "Charlie", Phone = "139-9876-5432", Birthday = new DateTime(1985, 8, 22) }
};
SaveContacts(data);
Contact[] loaded = LoadContacts();
foreach (var item in loaded)
{
Console.WriteLine($"Name: {item.Name}, Phone: {item.Phone}, Birthday: {item.Birthday.ToShortDateString()}");
}
}
}
运行结果
Name: Bob, Phone: 138-1234-5678, Birthday: 15/05/1990
Name: Charlie, Phone: 139-9876-5432, Birthday: 22/08/1985
常见问题解答
Q:二进制文件读写时,如何处理异常?
A:通过 try-catch
块捕获 IOException
,并确保在 finally
中释放资源(或使用 using
语句自动处理)。
try
{
using (FileStream fs = new FileStream("data.bin", FileMode.Open))
{
// 执行操作
}
}
catch (IOException ex)
{
Console.WriteLine($"文件操作失败: {ex.Message}");
}
Q:跨平台环境下如何保证二进制兼容性?
A:使用 BitConverter.IsLittleEndian
判断字节序,并在读写时进行调整。例如:
byte[] bytes = BitConverter.GetBytes(number);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes); // 转换为大端序存储
}
结论
通过本文的讲解,我们系统掌握了 C# 二进制文件的读写 核心方法,并通过通讯录案例验证了其实用性。二进制读写技术不仅提升了数据处理效率,还为开发者打开了更广阔的应用场景。建议读者在实践中逐步尝试以下进阶方向:
- 使用
BinaryFormatter
进行对象序列化(需注意版本兼容性); - 结合
MemoryStream
实现内存中的二进制操作; - 设计自定义二进制文件格式(如添加文件头、校验码)。
掌握这一技能后,您将能够更灵活地应对复杂项目中的数据持久化需求,为构建高性能应用程序打下坚实基础。