导读:本期聚焦于小伙伴创作的《C#如何操作二进制数据流?读写二进制文件的完整方法解析》,敬请观看详情。想把结构体或自定义格式的数据快速落盘,用文本方式序列化往往既占空间又慢。C#提供了BinaryWriter与BinaryReader配合FileStream直接操作字节流,能够精确控制写入内容与字节顺序。相比Json或Xml,二进制读写在游戏存档、通信协议解析等场景里延迟更低。实际编码时要注意字符编码统一、字段顺序一致,否则读取会错位。本文从底层流对象讲起,演示基本类型与自定义对象如何写入读取,并分析using语句释放资源、大端小端处理等常见坑点,帮你写出稳定可靠的二进制文件操作代码。

在C#中处理二进制文件,核心思路是使用FileStream作为底层字节通道,再借助BinaryWriterBinaryReader完成类型化数据的写入与读取。这种方式跳过了文本编码转换,直接以内存中的字节表现形式持久化数据,适合对性能和体积敏感的场景。

C#如何操作二进制数据流?读写二进制文件的完整方法解析

一、二进制流操作的基础对象

C#读写二进制文件主要依赖三个类。FileStream负责在磁盘文件和内存之间传输原始字节,它不关心数据含义,只保证字节正确到达。在其之上包裹BinaryWriter可以将int、float、string等类型按特定格式写成字节;BinaryReader则反向将字节还原为对应类型。三者配合形成完整的二进制数据流管道。

创建FileStream时通常需要指定文件路径、打开模式以及读写权限。例如使用FileMode.Create会新建或覆盖文件,FileAccess.Write限制为只写。由于这些类实现了IDisposable接口,必须用using语句包裹,确保即使发生异常也能释放文件句柄,避免文件被进程长期占用导致其他操作失败。

using System;
using System.IO;

class Demo
{
    static void Main()
    {
        // 创建文件流,准备写入
        using (FileStream fs = new FileStream("data.bin", FileMode.Create, FileAccess.Write))
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            bw.Write(100);          // 写入整数
            bw.Write(3.14f);        // 写入单精度浮点
            bw.Write("hello");      // 写入字符串
        }
        Console.WriteLine("写入完成");
    }
}

二、使用BinaryWriter写入常见数据类型

BinaryWriter提供了大量重载的Write方法,能够写入布尔值、字节、字符、各种长度的整数与浮点数,以及以长度前缀方式编码的字符串。字符串默认采用UTF-8编码并在前面附加一个七位压缩长度值,读取时BinaryReader会自动按同样规则解析,开发者无需手动计算字节数。

如果需要和某种固定格式协议对接,比如网络报文头部,可能要求字符串以固定字节数存储而非变长。此时应先将字符串用指定编码转为byte[],再调用Write(byte[])写入,读取时用ReadBytes取回定长数组后解码。下面示例展示定长写入用户名的方法,避免长度前缀带来的格式不兼容。

using System;
using System.IO;
using System.Text;

class FixedWriter
{
    static void Main()
    {
        using (FileStream fs = new FileStream("user.bin", FileMode.Create))
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            string name = "tom";
            byte[] nameBytes = Encoding.ASCII.GetBytes(name);
            byte[] buffer = new byte[16];          // 固定16字节用户名区
            Array.Copy(nameBytes, buffer, nameBytes.Length);
            bw.Write(buffer);                      // 写入定长数据
            bw.Write(25);                          // 写入年龄
        }
    }
}

三、使用BinaryReader读取并还原数据

读取时必须严格按照写入时的顺序与类型调用BinaryReader的对应方法。例如先写int再写float,读取就要先ReadInt32ReadSingle。顺序错乱会导致读取到错误字节,甚至抛出EndOfStreamException。对于自定义对象,推荐先约定好字段布局,然后逐一读写,保持两端结构同步。

当文件可能由其他语言生成时,还需注意字节序差异。C#的BinaryWriter在Windows上默认小端序,而某些协议要求大端序。此时不能用默认方法,应借助BitConverter翻转数组后再写。以下代码演示读取前面示例写的定长用户名与年龄,并正确处理编码。

using System;
using System.IO;
using System.Text;

class ReaderDemo
{
    static void Main()
    {
        using (FileStream fs = new FileStream("user.bin", FileMode.Open, FileAccess.Read))
        using (BinaryReader br = new BinaryReader(fs))
        {
            byte[] nameBuf = br.ReadBytes(16);     // 读取16字节
            string name = Encoding.ASCII.GetString(nameBuf).TrimEnd('');
            int age = br.ReadInt32();              // 读取年龄
            Console.WriteLine($"name={name}, age={age}");
        }
    }
}

四、自定义对象的序列化读写实践

真实项目中常需要保存整个对象,比如游戏角色状态。虽然可用BinaryFormatter,但该类已因安全问题被微软标记废弃。更稳妥的做法是手写读写逻辑:在类里加SaveLoad方法,显式控制每个字段。这样既能跨平台又清晰可控,还方便版本演进时做兼容。

下例定义一个Player类,包含坐标与等级,分别用BinaryWriterBinaryReader完成存取。若后续增加新字段,可在文件头写入版本号,读取时按版本分支处理,避免旧文件无法解析。这种手法在长周期项目中非常实用。

using System;
using System.IO;

class Player
{
    public float X { get; set; }
    public float Y { get; set; }
    public int Level { get; set; }

    public void Save(string path)
    {
        using (var fs = new FileStream(path, FileMode.Create))
        using (var bw = new BinaryWriter(fs))
        {
            bw.Write(X);
            bw.Write(Y);
            bw.Write(Level);
        }
    }

    public static Player Load(string path)
    {
        using (var fs = new FileStream(path, FileMode.Open))
        using (var br = new BinaryReader(fs))
        {
            return new Player
            {
                X = br.ReadSingle(),
                Y = br.ReadSingle(),
                Level = br.ReadInt32()
            };
        }
    }
}

五、常见错误与性能注意点

第一类常见错误是忘记释放流对象。未使用using且未手动调用Dispose,文件会一直被锁定,直到垃圾回收才释放,期间其他进程无法删除或修改。第二类是读写类型不匹配,比如写入long却用ReadInt32读取,直接破坏后续所有字段。建议在协议文档中明确定义每个偏移处的类型与长度。

性能方面,若频繁写入零散小数据,应增大FileStream缓冲区或先在内存用MemoryStream攒批再一次性落盘。另外BinaryWriter的字符串长度前缀采用变长编码,对极大量短字符串场景略有开销,可改用定长编码优化。掌握这些细节后,C#二进制数据流操作就能既安全又高效。

C#binary_fileFileStream修改时间:2026-08-11 01:27:32

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。