一、知识点概述
BinaryWriter(二进制写入器)与 BinaryReader(二进制读取器)是C#封装好的高级流操作类,专门用于以二进制形式读写本地文件。该类不能独立工作,必须依托 FileStream 文件流对象才可使用。
相较于原生FileStream,二进制读写工具最大优势:无需手动进行字符串与字节数组的转换,能够直接写入/读取字符串、整数、布尔值、小数等多种数据类型,极大简化文件开发操作。二进制保存的文件用记事本打开会出现乱码,属于正常现象,只能通过对应读取工具解析数据。
常用场景:保存游戏数据、软件配置信息、账号密码、加密存储文件、存储多类型复合型数据。
二、整体功能介绍
1. 按钮1:借助FileStream+BinaryWriter,以二进制模式向1.txt文件写入指定字符串;
2. 按钮2:借助FileStream+BinaryReader,读取二进制文件中的字符串,并赋值给窗体标题展示。
三、完整可运行源代码
using System; using System.IO; using System.Windows.Forms; namespace _3_BinaryWriter_和_BinaryReader { public partial class Form1 : Form { public Form1() { InitializeComponent(); //BinaryWriter和 BinaryReader: 二进制流读写操作,需要提供文件流对象 } //二进制写入数据 private void button1_Click(object sender, EventArgs e) { //1 创建文件流 主要是给BinaryWriter提供一个参数 FileStream file = new FileStream(@"1.txt",FileMode.Create,FileAccess.Write); //2 创建二进制写入对象(写入工具) BinaryWriter bw = new BinaryWriter(file); //3 直接写入字符串数据 bw.Write("世界杯中国什么时候进"); //4 关闭资源 bw.Close(); file.Close(); //5 释放内存资源 bw.Dispose(); file.Dispose(); } //二进制读取数据 private void button2_Click(object sender, EventArgs e) { //1 创建只读文件流 FileStream file = new FileStream(@"1.txt",FileMode.Open,FileAccess.Read); //2 创建二进制读取工具 BinaryReader br = new BinaryReader(file); //3 读取字符串类型的数据 string ss = br.ReadString(); //4 将读取的数据展示在窗体标题 this.Text = ss; //5 关闭、释放资源 br.Close(); file.Close(); br.Dispose(); file.Dispose(); } } }四、二进制写入操作(button1)分步拆解
步骤1:创建FileStream文件流对象
FileStream file = new FileStream(@"1.txt",FileMode.Create,FileAccess.Write);参数解析:
第一个参数 @"1.txt":文件相对路径,文件生成在程序运行根目录;
第二个参数 FileMode.Create:文件创建模式,文件不存在则自动新建,文件已存在则直接覆盖原有内容;
第三个参数 FileAccess.Write:设置文件流权限为写入权限,仅支持写入数据,无法读取数据。
步骤2:创建二进制写入工具
BinaryWriter bw = new BinaryWriter(file);BinaryWriter无法单独使用,构造函数需要传入FileStream对象,绑定对应的文件流,所有写入操作最终都会交给文件流保存到本地。
步骤3:写入数据
bw.Write("世界杯中国什么时候进");Write()为重载方法,支持string、int、double、bool等所有基础数据类型;内部自动完成字符串转二进制字节操作,不需要手动编码转换,简化开发。
步骤4:关闭资源
bw.Close(); file.Close();Close()作用:关闭数据流,解除程序对当前文件的占用;规范写法:先关闭读写工具,再关闭底层文件流。
步骤5:释放内存
bw.Dispose(); file.Dispose();Dispose()作用:销毁对象、释放内存资源,避免内存泄漏,是流操作必不可少的收尾步骤。
五、二进制读取操作(button2)分步拆解
步骤1:创建只读文件流
FileStream file = new FileStream(@"1.txt",FileMode.Open,FileAccess.Read);参数解析:
FileMode.Open:打开指定文件,若文件不存在直接抛出异常;
FileAccess.Read:权限设置为只读,仅能读取文件内容,禁止写入、修改文件。
步骤2:创建二进制读取工具
BinaryReader br = new BinaryReader(file);依托已开启的文件流,创建专属读取器,用于解析文件内的二进制数据。
步骤3:读取数据并展示
string ss = br.ReadString(); this.Text = ss;ReadString():专门读取二进制文件中的字符串数据;
核心规则:写入什么类型数据,读取必须对应调用相同类型的方法,顺序、类型不可错乱,否则程序报错。
步骤4:关闭并释放资源
br.Close(); file.Close(); br.Dispose(); file.Dispose();读取完成后必须关闭并释放资源,防止文件长期被程序占用,无法删除和修改。
六、读写工具配套读取方法
1. 字符串:Write(string) —— ReadString()
2. 整型数据:Write(int) —— ReadInt32()
3. 布尔类型:Write(bool) —— ReadBoolean()
4. 小数类型:Write(double) —— ReadDouble()
七、执行流程
1. 写入流程:创建写入文件流→初始化BinaryWriter→直接写入数据→关闭工具与文件流→释放内存;
2. 读取流程:创建只读文件流→初始化BinaryReader→匹配类型读取数据→展示数据→关闭释放所有资源。
八、知识点总结
1. BinaryWriter、BinaryReader 依赖 FileStream,不能独立实例化使用;
2. 二进制读写最大优点:自动完成字节转换,无需手动编写编码转换代码;
3. 写入和读取的数据类型、存储顺序必须完全一致;
4. 二进制文件记事本打开乱码属于正常现象,只能用BinaryReader解析;
5. 写入操作匹配Create+Write权限,读取操作匹配Open+Read权限;
6. 所有流对象使用完毕,必须执行Close关闭、Dispose释放资源。
九、常见易错点
1. 权限错误:写入使用Read权限、读取使用Write权限,直接运行报错;
2. 模式错误:读取文件使用Create,覆盖原有数据造成数据丢失;
3. 类型不匹配:写入字符串,读取调用读取int的方法,程序直接崩溃;
4. 遗漏收尾:未关闭文件流,文件被进程锁定,无法删除与移动;
5. 误解乱码:误以为二进制文件乱码是代码BUG,二进制文件本身不支持明文查看。
———————BufferedStream 缓存流读写——————
一、知识点简介
BufferedStream 叫做缓存流/缓冲流,属于优化版文件读写类。底层自带内存缓冲区,专门用来优化文件读写性能。
普通FileStream每次读写都会直接操作电脑硬盘,硬盘读写速度慢、损耗大;BufferedStream会先将数据暂存到内存缓冲区中,程序优先读写内存缓存,减少直接访问硬盘的次数,从而提升读写速度、降低硬盘损耗。
BufferedStream不能单独使用,必须包裹FileStream文件流对象,本质是对FileStream的功能升级与性能优化。
二、功能概述
1、写入原理:程序数据→内存缓冲区→统一写入硬盘;
2、读取原理:硬盘数据一次性加载到缓冲区→程序从内存读取数据;
3、本次案例包含两部分:缓存流写入文件(注释代码)、缓存流循环读取文件并展示到窗体标题。
三、完整源代码
using System; using System.IO; using System.Text; using System.Windows.Forms; namespace _3BufferedStream { public partial class Form1 : Form { public Form1() { InitializeComponent(); //BufferedStream:缓存区文件读写操作,主要把本地数据读取到缓存区里面 //后续读取直接读取缓存区数据,减少磁盘IO操作,提高读写速度 #region 【缓存流写入(注释部分)】 ////1.创建缓存流对象,包裹创建文件的文件流 //BufferedStream buffer = new BufferedStream(File.Create(@"1.txt")); ////2.字符串转为字节数组 //byte[] bs = Encoding.Default.GetBytes("主要把本地数据读取到缓存区里面,"); ////3.向缓冲区写入数据 //buffer.Write(bs, 0, bs.Length); ////4.Flush:强制刷新缓冲区,将缓存数据写入硬盘 //buffer.Flush(); ////5.关闭缓存流 //buffer.Close(); #endregion #region 【缓存流读取(主代码)】 //1.创建只读文件流,并包裹为缓存流对象 BufferedStream b1 = new BufferedStream(File.OpenRead(@"1.txt")); //2.根据缓存流数据长度,创建对应字节数组 byte[] bs = new byte[b1.Length]; //3.定义空字符串,用来拼接读取到的全部内容 string ss = ""; //4.循环读取缓冲区数据 //Read返回值>0:代表还读取到数据;返回值<=0:读取完毕 while (b1.Read(bs, 0, bs.Length) > 0) { //字节数组转字符串,累加保存 ss += Encoding.Default.GetString(bs); //控制台打印读取内容,方便调试查看 Console.WriteLine(Encoding.Default.GetString(bs)); } //5.将读取完整内容赋值给窗体标题 this.Text = ss; //6.关闭缓存流,释放文件占用 b1.Close(); #endregion } } }四、缓存流写入代码分步详解
1、创建缓存流对象
BufferedStream buffer = new BufferedStream(File.Create(@"1.txt"));File.Create(@"1.txt"):创建文件流,文件不存在新建,存在则覆盖;
将文件流作为参数传入BufferedStream,完成缓存流绑定,实现缓存读写。
2、字符串转字节数组
byte[] bs = Encoding.Default.GetBytes("主要把本地数据读取到缓存区里面,");计算机底层只能识别字节,所有写入缓存/文件的字符串,必须转为byte字节数组。
3、Write写入缓冲区
buffer.Write(bs, 0, bs.Length);将字节数组数据写入内存缓冲区,此时数据暂时保存在内存,并未直接写入硬盘。
4、Flush刷新缓冲区(重点)
buffer.Flush();作用:强制将内存缓冲区中所有暂存的数据,一次性写入本地硬盘;如果不调用Flush,数据只会停留在内存,不会保存到文件。
5、Close关闭流
关闭缓存流,解除程序对文件的占用,同时自动清空缓冲区资源。
五、缓存流读取代码分步详解
1、初始化只读缓存流
BufferedStream b1 = new BufferedStream(File.OpenRead(@"1.txt"));File.OpenRead:打开文件并生成只读文件流,只能读取不能写入;
外层包裹BufferedStream,启用缓存读取模式,优化读取效率。
2、创建字节缓存数组
byte[] bs = new byte[b1.Length];根据缓存流内部数据总长度,创建匹配大小的字节数组,用来接收读取的数据。
3、while循环读取数据
while (b1.Read(bs, 0, bs.Length)>0)Read方法返回值含义:
返回值 > 0:成功读取到字节数据,继续循环读取;
返回值 = 0:文件数据全部读取完毕,结束循环。
4、字节数组还原字符串
ss += Encoding.Default.GetString(bs);将每次循环读取到的字节数组,还原为字符串并累加,最终拼接成完整文件内容。
5、数据展示与关闭流
this.Text = ss:将读取完整的文件内容,赋值给窗体标题进行展示;
b1.Close():读取完成,关闭缓存流,释放文件与内存资源。
六、核心方法解析
1、Write(字节数组,起始下标,长度):向内存缓冲区写入字节数据;
2、Read(字节数组,起始下标,长度):从缓冲区读取数据存入数组,返回真实读取字节数;
3、Flush():强制刷新缓冲区,将内存数据落地保存到硬盘;
4、Close():关闭缓存流,释放所有资源。
七、BufferedStream优点
1、减少硬盘IO访问次数:批量读写,避免频繁操作硬盘;
2、读写效率更高:内存读写速度远远快于硬盘读写速度;
3、延长硬盘寿命:降低硬盘频繁读写造成的硬件损耗;
4、适配大文件:大文件读写优先推荐使用缓存流。
八、知识点总结
1、BufferedStream是FileStream的包装类,必须依赖文件流才能使用;
2、核心原理:利用内存缓冲区中转数据,减少磁盘访问次数;
3、写入数据必须调用Flush(),否则数据仅保存在内存,无法保存到文件;
4、采用while循环读取文件,适配任意大小文件,避免数据读取不全;
5、流对象使用完毕必须Close关闭,防止文件被程序锁定占用。
九、易错点
1、写入缓存流忘记调用Flush(),数据丢失,文件为空;
2、OpenRead只能读取,不能写入,强行写入直接报错;
3、读取文件时文件路径错误、文件不存在,程序抛出异常;
4、不使用循环读取大文件,容易出现数据读取残缺;
5、混淆缓冲区与文件:缓冲区在内存,断电数据直接清空。
————————File类——————————
一、知识点介绍
1、File类属于静态类,位于System.IO命名空间,所有方法全部为静态方法,不需要实例化对象,直接通过类名调用。
2、分工区别:
Directory:专门用来操作文件夹;
File:专门用来操作文件,支持文件创建、删除、复制、移动、一键读写、追加内容。
3、优缺点:File类写法简单,一行代码即可完成读写,适合小型文本文件;底层一次性加载全部数据,不适合超大文件,大文件建议使用FileStream缓存流。
二、完整源代码
namespace _5File类 { public partial class Form1 : Form { public Form1() { InitializeComponent(); //所有代码下面分段逐一讲解 } } }三、第一段:整体文本读写 WriteAllText / ReadAllText
//写入完整文本 File.WriteAllText(@"1.txt","dsdssdfsfdfddsdd是多少",Encoding.Default); //读取完整文本 MessageBox.Show(File.ReadAllText(@"1.txt",Encoding.Default));1、WriteAllText(覆盖写入)
作用:快速向文件写入字符串内容;文件不存在自动创建,文件已存在直接覆盖原有全部内容。
参数1:文件相对路径;参数2:需要写入的文本内容;参数3:编码格式。
2、ReadAllText(整体读取)
作用:读取目标文件中所有内容,整体返回为一个字符串,适合读取单行、简单文本。
四、第二段:数组分行读写 WriteAllLines / ReadAllLines
//定义字符串数组 string[] contents = { "张三丰", "杨过" }; //将数组换行写入文件 File.WriteAllLines(@"2.txt",contents); //读取文件,按行转为数组 string[] ss = File.ReadAllLines(@"2.txt"); foreach(string s in ss) { MessageBox.Show(s); }1、WriteAllLines
接收字符串数组,自动将数组里面的每一个元素,单独占一行写入文件,自动换行,适合多条数据存储。
2、ReadAllLines
读取文件所有内容,文件的每一行对应数组中的一个元素,返回字符串数组;一般搭配foreach循环遍历读取每一行数据。
五、第三段:字节形式读写 WriteAllBytes / ReadAllBytes
//字节形式写入 File.WriteAllBytes(@"3.txt", Encoding.Default.GetBytes("dsdfdf颠三倒四")); //字节形式读取 MessageBox.Show(Encoding.Default.GetString(File.ReadAllBytes(@"3.txt")));原理:和FileStream原理一致,以字节数组方式读写数据。
特点:适用性最强,不仅能读写txt文本,还可以读写图片、音频、视频等所有类型文件。
写入:字符串转字节数组;读取:字节数组还原为字符串。
六、第四段:基础文件操作(增、删、复制、剪切、判断)
File.Create(@"4.txt"); //创建空白文件 File.Delete(@"4.txt"); //删除指定文件 File.Move(@"4.txt",@"../5.txt"); //剪切/移动文件 File.Copy(@"../5.txt", @"6.txt"); //复制文件 File.Exists(@"6.txt"); //判断文件是否存在1、Create():创建一个空白文件,返回FileStream文件流对象;
2、Delete():永久删除文件,删除后无法从回收站恢复;
3、Move():剪切文件,既可以移动文件位置,也可以用于重命名文件;
4、Copy():复制一份一模一样的文件;
5、Exists():判断文件是否存在,返回布尔值true / false,防止路径报错。
七、第五段:AppendText 追加写入(重点)
//获取程序当前运行绝对路径 Directory.GetCurrentDirectory(); //打开文件并开启追加模式 StreamWriter sw = File.AppendText("6.txt"); //在文件末尾追加写入内容 sw.Write(Directory.GetCurrentDirectory()); //关闭写入流,释放资源 sw.Close();1、WriteAllText 属于覆盖写入,会清空旧数据;AppendText为追加写入,不会覆盖原有内容,在文件末尾新增数据。
2、File.AppendText():打开指定文件,返回StreamWriter写入对象。
3、Directory.GetCurrentDirectory():获取当前程序Debug运行目录的绝对路径。
4、sw.Write():向文件末尾写入内容;使用完毕必须Close()关闭流,防止文件被占用。
八、知识点总结
1、File是静态类,无需new对象,直接类名调用方法;
2、WriteAllText:覆盖写入普通文本;ReadAllText:读取全部文本;
3、WriteAllLines:数组分行写入;ReadAllLines:按行读取返回数组;
4、WriteAllBytes:字节读写,支持所有格式文件;
5、Create/Delete/Copy/Move/Exists 完成文件基础增删复制移动;
6、AppendText实现追加写入,解决覆盖旧数据的问题,结尾必须关闭流。
StreamReader与StreamWriter 读写流
一、知识点概述
StreamWriter 和 StreamReader 专门用于纯文本文件的读写操作,位于System.IO命名空间。
1、StreamWriter:文本写入流,专门用来写入字符串,支持换行写入,底层自动封装FileStream,不需要手动转换字节数组;
2、StreamReader:文本读取流,专门用来读取文本文件,一般采用while循环按行读取;
3、特点:按行读写、内存占用小、适合大文本;优于File类,File一次性加载全部内容,容易内存溢出。
二、完整源代码
using System; using System.IO; using System.Windows.Forms; namespace _6StreamReader_StreamWriter { public partial class Form1 : Form { public Form1() { InitializeComponent(); #region 【StreamWriter 写入(注释代码)】 ////1.创建文本写入流对象 //StreamWriter sw = new StreamWriter(@"1.txt"); ////2.定义数组数据 //string[] names = { "张三", "李四" }; ////3.遍历数组,逐行写入文件 //foreach (string name in names) //{ // sw.WriteLine(name); //} ////4.关闭写入流 //sw.Close(); #endregion #region 【StreamReader 读取(运行代码)】 //1.创建文本读取流对象,打开指定文件 StreamReader sr = new StreamReader(@"1.txt"); //2.定义空字符串,用来接收每一行读取的数据 string str = string.Empty; //3.循环按行读取文件 while ((str = sr.ReadLine()) != null) { //弹窗显示每一行内容 MessageBox.Show(str); } //4.关闭读取流 sr.Close(); #endregion } } }三、第一部分:StreamWriter 文本写入(分段拆解)
1、创建写入流对象
StreamWriter sw = new StreamWriter(@"1.txt");传入文件路径,自动创建FileStream;文件不存在自动新建,文件存在默认覆盖原有内容。
2、定义需要写入的数据
string[] names = { "张三", "李四" };定义字符串数组,存储多条需要写入文件的文本数据。
3、foreach遍历 + WriteLine换行写入
foreach (string name in names) { sw.WriteLine(name); }WriteLine():写入内容并自动换行;
区别:Write()只写入内容,不会自动换行。
4、关闭流资源
sw.Close();释放文件占用,保存数据,防止文件被程序锁定。
四、第二部分:StreamReader 文本读取(分段拆解)
1、创建读取流
StreamReader sr = new StreamReader(@"1.txt");根据路径打开文本文件,创建只读读取流,只能读取文本,不能读取图片、视频。
2、定义接收变量
string str = string.Empty;定义空字符串,专门用来接收ReadLine读取到的每一行数据。
3、while循环读取(核心重点)
while ((str = sr.ReadLine()) != null)ReadLine():每次只读一行数据;
读取规则:读取到内容返回当前行字符串;读取完毕没有数据返回null;
循环逻辑:先读取赋值给str,再判断是否等于null,不等于null就继续读取。
4、展示数据
MessageBox.Show(str);将每一行读取到的数据弹窗展示。
5、关闭读取流
sr.Close();读取完成必须关闭流,解除程序对文件的占用。
五、核心方法总结
StreamWriter(写入)
1、Write():写入内容,不换行;
2、WriteLine():写入内容,自动换行;
3、Close():关闭写入流,保存资源。
StreamReader(读取)
1、ReadLine():读取单行数据,读完返回null;
2、ReadToEnd():一次性读取全部文本;
3、Close():关闭读取流。
六、File类 VS Stream读写类
1、File类:一行代码读写,简单粗暴,一次性加载全部内容,适合小文件;
2、StreamWriter/StreamReader:按行读写,内存占用极小,专门适合大文本文件。
七、易错点
1、StreamWriter默认覆盖文件内容,想要追加内容需要重载构造函数;
2、ReadLine只能读取文本,不能读取二进制文件;
3、while循环判断不能写反,必须判断 != null;
4、读写结束不Close,文件会被进程占用,无法删除修改;
5、编码不一致会出现中文乱码。