4.文件操作相关-文件流相关
4.1 知识点
什么是文件流
C#中,有一个叫做FileStream的类,它提供了对文件进行读写的详细控制。相比我们之前学过的File类只能对整个文件进行读写,FileStream可以以字节的形式处理文件。
说认话,文件中存储的数据就像是一个数据流(类似数组或列表),我们可以通过FileStream逐个部分地读取或写入这个数据流。
举个例子,我们可以先存储一个int(4个字节),再存储一个bool(1个字节),再存储一个string(n个字节)。利用FileStream,我们可以以流式的方式逐个读取或写入这些数据。
FileStream文件流类常用方法
类名:FileStream
需要引用命名空间:System.IO
打开或创建指定文件
new FileStream
//方法一:new FileStream
//参数一:路径
//参数二:打开模式
// CreateNew:创建新文件 如果文件存在 则报错
// Create:创建文件,如果文件存在 则覆盖
// Open:打开文件,如果文件不存在 报错
// OpenOrCreate:打开或者创建文件根据实际情况操作
// Append:若存在文件,则打开并查找文件尾,或者创建一个新文件
// Truncate:打开并清空文件内容
//参数三:访问读写模式
//参数四:共享权限
// None 谢绝共享
// Read 允许别的程序读取当前文件
// Write 允许别的程序写入该文件
// ReadWrite 允许别的程序读写该文件
FileStream fileStream1 = new FileStream(Application.dataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.Create, FileAccess.ReadWrite);
File.Create
//方法二:File.Create
//参数一:路径
//参数二:缓存大小
//参数三:描述如何创建或覆盖该文件(不常用)
// Asynchronous 可用于异步读写
// DeleteOnClose 不在使用时,自动删除
// Encrypted 加密
// None 不应用其它选项
// RandomAccess 随机访问文件
// SequentialScan 从头到尾顺序访问文件
// WriteThrough 通过中间缓存直接写入磁盘
FileStream fileStream2 = File.Create(Application.dataPath + "/Lesson04_文件操作相关_文件流相关.tao",2048);
File.Open
//方法三:File.Open
//参数一:路径
//参数二:打开模式
FileStream fileStream3 = File.Open(Application.dataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.OpenOrCreate);
重要属性和方法
FileStream fileStream4 = File.Open(Application.dataPath + "Lesson04_文件操作相关_文件流相关.tao", FileMode.OpenOrCreate);
FileStream.Length 文本字节长度
//文本字节长度
print(fileStream4.Length);//0
FileStream.CanRead 是否可写
//是否可写
if (fileStream4.CanRead)
{
}
FileStream.CanWrite 是否可读
//是否可读
if (fileStream4.CanWrite)
{
}
FileStream.Flush 将缓冲区中的数据立即写入到文件中
通常情况下,文件写入是通过缓冲区进行的,这样可以提高写入效率。但是,如果您希望确保数据尽快写入到磁盘,而不是等待缓冲区满或者流关闭时才写入,您可以调用 Flush() 方法。
调用 Flush() 方法时,它会强制将缓冲区中的数据刷新到文件中,然后清空缓冲区,这样可以确保文件中的数据是最新的。这在某些情况下非常有用,比如在需要确保数据实时保存到文件时,或者在程序即将关闭之前需要确保所有数据都已写入到文件中。
//将字节写入文件 当写入后 一定执行写入流 才能保证写入文件成功 不然可能丢失
fileStream4.Flush();
FileStream.Close 关闭流
//关闭流 当文件读写完毕后 一定执行关闭流
fileStream4.Close();
FileStream.Dispose 缓存资源销毁回收
//缓存资源销毁回收
fileStream4.Dispose();
FileStream.Write 写入字节
//打印可读写路径
print(Application.persistentDataPath);
using (FileStream fileStream5 = new FileStream(Application.persistentDataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] bytes = BitConverter.GetBytes(999);
//方法:Write
//参数一:写入的字节数组
//参数二:数组中的开始索引
//参数三:写入多少个字节
//要传入开始索引和写入多少字节的原因:有可能一个数组前面代表一个数据 中间一截代表另一串数据 可以直接传数组长度
fileStream5.Write(bytes, 0, bytes.Length);
//fileStream5.Write(bytes, 0, 4);
//写入字符串时 先把字符串转成字节数组
bytes = Encoding.UTF8.GetBytes("林文韬哈哈哈哈");
//要先写入长度 不然无法自动字符串有多长 传入字节数组长度的直接数组
//int length = bytes.Length;
fileStream5.Write(BitConverter.GetBytes(bytes.Length), 0, 4);
//再写入字符串具体内容 传入直接数组
fileStream5.Write(bytes, 0, bytes.Length);
//避免数据丢失 一定写入后要执行的方法
fileStream5.Flush();
//销毁缓存 释放资源
fileStream5.Dispose();
}
FileStream.Read 读取字节
挨个读取字节数组并转换
核心思想是读一个转换一个
using (FileStream fileStream6 = File.Open(Application.persistentDataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.Open, FileAccess.Read))
{
//读取时可以理解为流有个看不见的索引 读到哪索引加到哪
//用于存储读取的字节数组的容器
byte[] bytes2 = new byte[4];
//参数一:用于存储读取的字节数组的容器
//参数二:容器中开始的位置
//参数三:读取多少个字节装入容器
//返回值:当前流索引前进了几个位置
int index = fileStream6.Read(bytes2, 0, 4);
int i = BitConverter.ToInt32(bytes2, 0);
print("取出来的第一个整数" + i);//999
print("索引向前移动" + index + "个位置");
//读取第二个字符串
//读取字符串字节数组长度
index = fileStream6.Read(bytes2, 0, 4);
print("索引向前移动" + index + "个位置");
//转换出直接数组有多长
int length = BitConverter.ToInt32(bytes2, 0);
print("字符串数组长度为" + length);//21
//要根据我们存储的字符串字节数组的长度 来声明一个新的字节数组 用来装载读取出来的数据
bytes2 = new byte[length];
index = fileStream6.Read(bytes2, 0, length);
print("索引向前移动" + index + "个位置");
//得到最终的字符串 打印出来
print(Encoding.UTF8.GetString(bytes2));//林文韬哈哈哈哈
fileStream6.Dispose();
}
一次性读取再挨个转换
核心思想是读完全部再转换
using (FileStream fileStream7 = File.Open(Application.persistentDataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.Open, FileAccess.Read))
{
//一开始就申明一个 和文件字节数组长度一样的容器
byte[] bytes3 = new byte[fileStream7.Length];
//一口气读取文件所有字节
fileStream7.Read(bytes3, 0, (int)fileStream7.Length);
fileStream7.Dispose();
//读取整数 因为是int自动读4个字节 从0位置开始读
print(BitConverter.ToInt32(bytes3, 0));//999
//得去字符串字节数组的长度 因为是int自动读4个字节 从4位置开始读
int length2 = BitConverter.ToInt32(bytes3, 4);//21
//得到字符串 读取长度是刚刚取出来的字节数组的长度 因为是string 要指定读几位 读味
print(Encoding.UTF8.GetString(bytes3, 8, length2));//林文韬哈哈哈哈
}
更加安全的使用文件流对象
普通用法:
- //申明一个引用对象
- //使用对象
- //销毁对象
using关键字用法:
- //using (申明一个引用对象)
- //{
- //使用对象
- //}
无论发生什么情况,当using语句块结束后,会自动调用该对象的销毁方法,避免忘记销毁或关闭流。using 是一种更安全的使用方法。
强调目前我们对文件流进行操作,为了文件操作安全,都用 using 来进行处理最好。
总结
在使用 FileStream 进行读写操作时,需要确保读写的规则是一致的。存储数据的顺序由我们制定的规则决定,按照规则进行读写可以保证数据的正确性。
4.2 知识点代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
public class Lesson04_文件操作相关_文件流相关 : MonoBehaviour
{
void Start()
{
#region 知识点一 什么是文件流
//在C#中提供了一个文件流类 FileStream类
//它主要作用是用于读写文件的细节
//我们之前学过的File只能整体读写文件
//而FileStream可以以读写字节的形式处理文件
//说人话:
//文件里面存储的数据就像是一条数据流(数组或者列表)
//我们可以通过FileStream一部分一部分的读写数据流
//比如我可以先存一个int(4个字节)再存一个bool(1个字节)再存一个string(n个字节)
//利用FileStream可以以流式逐个读写
#endregion
#region 知识点二 FileStream文件流类常用方法
//类名:FileStream
//需要引用命名空间:System.IO
#region 打开或创建指定文件
//方法一:new FileStream
//参数一:路径
//参数二:打开模式
// CreateNew:创建新文件 如果文件存在 则报错
// Create:创建文件,如果文件存在 则覆盖
// Open:打开文件,如果文件不存在 报错
// OpenOrCreate:打开或者创建文件根据实际情况操作
// Append:若存在文件,则打开并查找文件尾,或者创建一个新文件
// Truncate:打开并清空文件内容
//参数三:访问读写模式
//参数四:共享权限
// None 谢绝共享
// Read 允许别的程序读取当前文件
// Write 允许别的程序写入该文件
// ReadWrite 允许别的程序读写该文件
FileStream fileStream1 = new FileStream(Application.dataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.Create, FileAccess.ReadWrite);
//方法二:File.Create
//参数一:路径
//参数二:缓存大小
//参数三:描述如何创建或覆盖该文件(不常用)
// Asynchronous 可用于异步读写
// DeleteOnClose 不在使用时,自动删除
// Encrypted 加密
// None 不应用其它选项
// RandomAccess 随机访问文件
// SequentialScan 从头到尾顺序访问文件
// WriteThrough 通过中间缓存直接写入磁盘
//FileStream fileStream2 = File.Create(Application.dataPath + "/Lesson04_文件操作相关_文件流相关.tao",2048);
//方法三:File.Open
//参数一:路径
//参数二:打开模式
//FileStream fileStream3 = File.Open(Application.dataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.OpenOrCreate);
#endregion
#region 重要属性和方法
FileStream fileStream4 = File.Open(Application.dataPath + "Lesson04_文件操作相关_文件流相关.tao", FileMode.OpenOrCreate);
//文本字节长度
print(fileStream4.Length);//0
//是否可写
if (fileStream4.CanRead)
{
}
//是否可读
if (fileStream4.CanWrite)
{
}
//将字节写入文件 当写入后 一定执行写入流 才能保证写入文件成功 不然可能丢失
fileStream4.Flush();
//关闭流 当文件读写完毕后 一定执行关闭流
fileStream4.Close();
//缓存资源销毁回收
fileStream4.Dispose();
#endregion
#region 写入字节
//打印可读写路径
print(Application.persistentDataPath);
using (FileStream fileStream5 = new FileStream(Application.persistentDataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] bytes = BitConverter.GetBytes(999);
//方法:Write
//参数一:写入的字节数组
//参数二:数组中的开始索引
//参数三:写入多少个字节
//要传入开始索引和写入多少字节的原因:有可能一个数组前面代表一个数据 中间一截代表另一串数据 可以直接传数组长度
fileStream5.Write(bytes, 0, bytes.Length);
//fileStream5.Write(bytes, 0, 4);
//写入字符串时 先把字符串转成字节数组
bytes = Encoding.UTF8.GetBytes("林文韬哈哈哈哈");
//要先写入长度 不然无法自动字符串有多长 传入字节数组长度的直接数组
//int length = bytes.Length;
fileStream5.Write(BitConverter.GetBytes(bytes.Length), 0, 4);
//再写入字符串具体内容 传入直接数组
fileStream5.Write(bytes, 0, bytes.Length);
//避免数据丢失 一定写入后要执行的方法
fileStream5.Flush();
//销毁缓存 释放资源
fileStream5.Dispose();
}
#endregion
#region 读取字节
#region 方法一:挨个读取字节数组
using (FileStream fileStream6 = File.Open(Application.persistentDataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.Open, FileAccess.Read))
{
//读取时可以理解为流有个看不见的索引 读到哪索引加到哪
//用于存储读取的字节数组的容器
byte[] bytes2 = new byte[4];
//参数一:用于存储读取的字节数组的容器
//参数二:容器中开始的位置
//参数三:读取多少个字节装入容器
//返回值:当前流索引前进了几个位置
int index = fileStream6.Read(bytes2, 0, 4);
int i = BitConverter.ToInt32(bytes2, 0);
print("取出来的第一个整数" + i);//999
print("索引向前移动" + index + "个位置");
//读取第二个字符串
//读取字符串字节数组长度
index = fileStream6.Read(bytes2, 0, 4);
print("索引向前移动" + index + "个位置");
//转换出直接数组有多长
int length = BitConverter.ToInt32(bytes2, 0);
print("字符串数组长度为" + length);//21
//要根据我们存储的字符串字节数组的长度 来声明一个新的字节数组 用来装载读取出来的数据
bytes2 = new byte[length];
index = fileStream6.Read(bytes2, 0, length);
print("索引向前移动" + index + "个位置");
//得到最终的字符串 打印出来
print(Encoding.UTF8.GetString(bytes2));//林文韬哈哈哈哈
fileStream6.Dispose();
}
#endregion
#region 方法二:一次性读取再挨个读取
print("***************************");
using (FileStream fileStream7 = File.Open(Application.persistentDataPath + "/Lesson04_文件操作相关_文件流相关.tao", FileMode.Open, FileAccess.Read))
{
//一开始就申明一个 和文件字节数组长度一样的容器
byte[] bytes3 = new byte[fileStream7.Length];
//一口气读取文件所有字节
fileStream7.Read(bytes3, 0, (int)fileStream7.Length);
fileStream7.Dispose();
//读取整数
print(BitConverter.ToInt32(bytes3, 0));//999
//得去字符串字节数组的长度
int length2 = BitConverter.ToInt32(bytes3, 4);//21
//得到字符串 读取长度是刚刚取出来的字节数组的长度
print(Encoding.UTF8.GetString(bytes3, 8, length2));//林文韬哈哈哈哈
}
#endregion
#endregion
#endregion
#region 知识点三 更加安全的使用文件流对象
//普通用法:
//申明一个引用对象
//使用对象
//销毁对象
//using关键字用法:
//using (申明一个引用对象)
//{
//使用对象
//}
//无论发生什么情况 当using语句块结束后
//会自动调用该对象的销毁方法 避免忘记销毁或关闭流
//using是一种更安全的使用方法
//强调目前我们对文件流进行操作 为了文件操作安全 都用using来进行处理最好
#endregion
#region 总结
//通过FIleStream读写时一定要注意
//读的规则一定是要和写是一致的
//我们存储数据的先后顺序是我们制定的规则
//只要按照规则读写就能保证数据的正确性
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com