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. BinaryReaderBinaryWriter:字节的“翻译器”

这两个类专门用于二进制流的读写操作,可直接将字节序列转换为 C# 数据类型(如 intdoublestring)。

// 写入示例  
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# 二进制文件的读写 核心方法,并通过通讯录案例验证了其实用性。二进制读写技术不仅提升了数据处理效率,还为开发者打开了更广阔的应用场景。建议读者在实践中逐步尝试以下进阶方向:

  1. 使用 BinaryFormatter 进行对象序列化(需注意版本兼容性);
  2. 结合 MemoryStream 实现内存中的二进制操作;
  3. 设计自定义二进制文件格式(如添加文件头、校验码)。

掌握这一技能后,您将能够更灵活地应对复杂项目中的数据持久化需求,为构建高性能应用程序打下坚实基础。

最新发布