Unity学习----【数据持久化】二进制存储(二)--文件流
·来源于唐老狮的视频教学,仅作记录和感悟记录,方便日后复习或者查找
一.文件流是什么
二.文件流的用法
类名:FileStream
需要引用命名空间:System.IO
1.打开或创建文件
1.1.new Stream
//参数一:路径//参数二:打开模式// CreateNew:创建新文件 如果文件存在 则报错// Create:创建文件,如果文件存在 则覆盖// Open:打开文件,如果文件不存在 报错// OpenOrCreate:打开或者创建文件根据实际情况操作// Append:若存在文件,则打开并查找文件尾,或者创建一个新文件// Truncate:打开并清空文件内容//参数三:访问模式//参数四:共享权限// None 谢绝共享// Read 允许别的程序读取当前文件// Write 允许别的程序写入该文件// ReadWrite 允许别的程序读写该文件FileStream fs = new FileStream(Application.dataPath + \"/Lesson3.tang\", FileMode.Create, FileAccess.ReadWrite);
①文件流都需要指定一个文件进行操作,这里也不例外
②这里参数四我们一般暂时还用不到
1.2.File.Create
//参数一:路径//参数二:缓存大小//参数三:描述如何创建或覆盖该文件(不常用)// Asynchronous 可用于异步读写// DeleteOnClose 不在使用时,自动删除// Encrypted 加密// None 不应用其它选项// RandomAccess 随机访问文件// SequentialScan 从头到尾顺序访问文件// WriteThrough 通过中间缓存直接写入磁盘FileStream fs2 = File.Create(Application.dataPath + \"/Lesson3.tang\");
①这里就相当于直接创建一个新的文件,并打开作为文件流
1.3.File.Open
//参数一:路径//参数二:打开模式FileStream fs3 = File.Open(Application.dataPath + \"/Lesson3.tang\", FileMode.Open);
①就是指定打开一个文件,一般这种方法我们用的最多(因为他也可以指定没有文件的时候创建一个文件)
2.重要属性和方法
//文本字节长度print(fs.Length);//是否可写if (fs.CanRead) {}//是否可读if (fs.CanWrite) {}//将字节写入文件 当写入后 一定执行一次fs.Flush();//关闭流 当文件读写完毕后 一定执行fs.Close();//缓存资源销毁回收fs.Dispose();
①这里常用的是获取字节长度,用来在获取之后进行一次性反序列化
②记得用完之后一定要Close并Dispose,不过其实在使用Dispose的时候,它也是会自动使用一次Close先的
3.写入字节
文件流最重要的功能就是用于把字节写入到文件当中
print(Application.persistentDataPath); using (FileStream fs = new FileStream(Application.persistentDataPath + \"/Lesson3.tang\", FileMode.OpenOrCreate, FileAccess.Write)) { byte[] bytes = BitConverter.GetBytes(999); //方法:Write //参数一:写入的字节数组 //参数二:数组中的开始索引 //参数三:写入多少个字节 fs.Write(bytes, 0, bytes.Length); //写入字符串时 bytes = Encoding.UTF8.GetBytes(\"hanako daisuki\"); //先写入长度 //int length = bytes.Length; fs.Write(BitConverter.GetBytes(bytes.Length), 0, 4); //再写入字符串具体内容 fs.Write(bytes, 0, bytes.Length); //避免数据丢失 一定写入后要执行的方法 fs.Flush(); //销毁缓存 释放资源 fs.Dispose(); }
①这里用using来指定文件流的存活范围,超出这个范围之后它就会被自动Dispose掉。
②每次写入之后,文件流的光标都会移动到写入位置的末尾。因此每次写入的开始位置偏移值填0即可,然后填入写入字节数组的长度
③对于常规变量,写入每次写入的长度固定,比如int就是写入4位。对于字符串,它的长度并不固定,所以我们需要先获取它的长度值,并存储起来,之后再用这个长度去存储字符串。
4.读取字节
可以一个一个读出,也可以一次性读出
4.1.一个一个读
using (FileStream fs2 = File.Open(Application.persistentDataPath + \"/Lesson3.tang\", FileMode.Open, FileAccess.Read)) { //读取第一个整形 byte[] bytes2 = new byte[4]; //参数一:用于存储读取的字节数组的容器 //参数二:容器中开始的位置 //参数三:读取多少个字节装入容器 //返回值:当前流索引前进了几个位置 int index = fs2.Read(bytes2, 0, 4); int i = BitConverter.ToInt32(bytes2, 0); print(\"取出来的第一个整数\" + i);//999 print(\"索引向前移动\" + index + \"个位置\"); //读取第二个字符串 //读取字符串字节数组长度 index = fs2.Read(bytes2, 0, 4); print(\"索引向前移动\" + index + \"个位置\"); int length = BitConverter.ToInt32(bytes2, 0); //要根据我们存储的字符串字节数组的长度 来声明一个新的字节数组 用来装载读取出来的数据 bytes2 = new byte[length]; index = fs2.Read(bytes2, 0, length); print(\"索引向前移动\" + index + \"个位置\"); //得到最终的字符串 打印出来 print(Encoding.UTF8.GetString(bytes2)); fs2.Dispose(); }
①这里主要用到的是.Read(接收容器,读取位置偏移,读取长度)。其中每次读取完毕后都会往后移动对应位置,所以一般给读取位置偏移填0。读取长度填入需要读取的变量的对应的字节长度即可
②每次进行读取之前,都需要先声明一个对应长度的字节数组,然后用这个字节数组去装载读取的结果
4.2.一次性读取
using (FileStream fs3 = File.Open(Application.persistentDataPath + \"/Lesson3.tang\", FileMode.Open, FileAccess.Read)){ //一开始就申明一个 和文件字节数组长度一样的容器 byte[] bytes3 = new byte[fs3.Length]; fs3.Read(bytes3, 0, (int)fs3.Length); fs3.Dispose(); //读取整数 print(BitConverter.ToInt32(bytes3, 0)); //得去字符串字节数组的长度 int length2 = BitConverter.ToInt32(bytes3, 4); //得到字符串 print(Encoding.UTF8.GetString(bytes3, 8, length2));}
①这里主要是先获取文件流的总字节数组长度,然后声明一个对应长度的字节数组去接收。
②之后我们通过BitConverter这个工具类中的方法,来根据变量所在的对应位置索引与长度去逐个把变量由字节数组中转化出来
5.更加安全地使用文件流
三.总结
①文件流是相当于打开整个文件的一个数据流,我们可以在其中写入或者读取出任意位置和长度的字节数组。
②文件流有三种创建方式,通常我们通过直接打开或者创建文件来获取它。
③文件流建议搭配using(){} 使用,这样在使用完毕之后可以自动释放空间,避免遗忘。
④文件流的写入是写入字节数组,需要提供写入的偏移值(一般为0)以及写入的长度。常规变量的长度固定的,但是对于字节这种不定长度的变量,需要先存储它的长度。
⑤文件流的读取可以一个一个地指定位置和长度读取字节数组,也可以直接把文件流中的字节直接读取到一个字节数组中再在里面指定位置和长度去转化。一般我们用第一种,如果文件比较短小,那可以用第二种。