UD4L3——文件流相关
UD4L3——文件流相关
本章代码关键字
1 | FileStream //文件流类,用于读写文件的细节 |
文件流
在C#中提供了一个文件流类FileStream
类
它主要作用是用于读写文件的细节
我们之前学过的File只能整体读写文件,而FileStream
可以以读写字节的形式处理文件
说人话:
文件里面存储的数据就像是一条数据流(数组或者列表)
我们可以通过FileStream
一部分一部分的读写数据流
比如我可以先存一个int
(4个字节)再存一个bool
(1个字节)再存一个string
(n个字节)
利用FileStream
可以以流式逐个读写
通过FileStream
读写时一定要注意,读的规则一定是要和写是一致的
我们存储数据的先后顺序是我们制定的规则,只要按照规则读写就能保证数据的正确性
FileStream文件流类常用方法
类名:FileStream
命名空间:System.IO
-
打开或创建指定文件
-
new FileStream()
使用构造函数来构造一个文件流
-
参数一:路径
-
参数二:打开模式
-
CreateNew
:创建新文件 如果文件存在 则报错 -
Create
:创建文件,如果文件存在 则覆盖 -
Open
:打开文件,如果文件不存在 报错 -
OpenOrCreate
:打开或者创建文件根据实际情况操作 -
Append
:若存在文件,则打开并查找文件尾,或者创建一个新文件 -
Truncate
:打开并清空文件内容
-
-
参数三:访问模式
-
Read
:只读 -
Write
:只写 -
ReadWrite
:读写
-
-
参数四:共享权限(一般可以不填)
-
None
:谢绝共享 -
Read
:允许别的程序读取当前文件 -
Write
:允许别的程序写入该文件 -
ReadWrite
:允许别的程序读写该文件
-
1
FileStream fileStream = new FileStream(Application.dataPath + "/Lesson3.uni", FileMode.Create, FileAccess.ReadWrite);
-
-
File.Create
-
参数一:路径
-
参数二:缓存大小(可不写)
-
参数三:描述如何创建或覆盖该文件(不常用)
-
Asynchronous
可用于异步读写 -
DeleteOnClose
不在使用时,自动删除 -
Encrypted
加密 -
None
不应用其它选项 -
RandomAccess
随机访问文件 -
SequentialScan
从头到尾顺序访问文件 -
WriteThrough
通过中间缓存直接写入磁盘
-
1
FileStream fileStream = File.Create(Application.dataPath + "/Lesson3.uni");
-
-
File.Open
-
参数一:路径
-
参数二:打开模式
-
CreateNew
:创建新文件 如果文件存在 则报错 -
Create
:创建文件,如果文件存在 则覆盖 -
Open
:打开文件,如果文件不存在 报错 -
OpenOrCreate
:打开或者创建文件根据实际情况操作 -
Append
:若存在文件,则打开并查找文件尾,或者创建一个新文件 -
Truncate
:打开并清空文件内容
-
1
FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.Open);
-
-
-
重要属性和方法
-
文本字节长度
即该文件流所有内容的长度,注意,返回的是****
long
类型的整数! ,这意味着一个文件流的内的字节长度可以非常大1
2FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.OpenOrCreate);
print(fileStream.Length); -
是否可写
该文件流是否可以写入内容
1
2
3
4
5FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.OpenOrCreate);
if (fileStream.CanWrite)
{
print("可写");
} -
是否可读
该文件流是否可以读取内容
1
2
3
4
5FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.OpenOrCreate);
if (fileStream.CanRead)
{
print("可读");
} -
将缓存内字节写入文件
将缓存内的字节数据全部写入文件,当在写入以后一定要执行一次!
1
2FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.OpenOrCreate);
fileStream.Flush(); -
关闭流
将文件流关闭,当文件读写完毕后一定要执行
1
2FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.OpenOrCreate);
fileStream.Close(); -
缓存资源销毁回收
1
2FileStream fileStream = File.Open(Application.dataPath + "/Lesson3.uni", FileMode.OpenOrCreate);
fileStream.Dispose();
-
-
写入字节
- 参数一:写入的字节数组
- 参数二:数组中的开始索引(数组从哪个索引的元素开始写入到文件内)
- 参数三:写入多少个字节
1
2
3
4
5
6
7
8
9FileStream fileStream = new FileStream(Application.persistentDataPath + "/Lesson3.uni",
FileMode.OpenOrCreate,
FileAccess.Write);
byte[] bytes = BitConverter.GetBytes(999);
fileStream.Write(bytes, 0, bytes.Length);
//不要忘记将字节写入文件,并关闭流,避免数据丢失
fileStream.Flush();
fileStream.Close();
fileStream.Dispose();写入字符串时,最好先写入长度,再写入字符串具体内容
因为字符串的编码格式,长度都是不确定的,记录了长度以后就会更方便读取字符串的内容至于下面例子里的
using
是用来做什么的,可看 ——> using1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17using (FileStream fileStream = new FileStream(Application.persistentDataPath + "/Lesson3.uni",
FileMode.OpenOrCreate,
FileAccess.Write))
{
byte[] bytes = BitConverter.GetBytes(999);
fileStream.Write(bytes, 0, bytes.Length);
//写入字符串时
byte[] strBytes = Encoding.UTF8.GetBytes("字符串写入测试");
//先写入长度
fileStream.Write(BitConverter.GetBytes(strBytes.Length), 0, 4);
//再写入字符串具体内容
fileStream.Write(strBytes, 0, strBytes.Length);
//不要忘记将字节写入文件,并关闭流,避免数据丢失
fileStream.Flush();
fileStream.Close();
fileStream.Dispose();
} -
读取字节
- 参数一:用于存储读取的字节数组的容器
- 参数二:容器中开始的位置(读取到的字节从数组的第几个元素开始装入)
- 参数三:读取多少个字节装入容器
- 返回值:当前流索引前进了几个位置(打个比方,这个索引你可以类比为文字编辑器里的光标)
-
挨个读取字节数组
至于下面代码里的
using
是用来做什么的,可看 ——> using1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25using (FileStream fileStream = File.Open(Application.persistentDataPath + "/Lesson3.uni",
FileMode.Open,
FileAccess.Read))
{
//读取第一个整数
byte[] bytes = new byte[4];
int index = fileStream.Read(bytes, 0, 4);
int i = BitConverter.ToInt32(bytes, 0);
print("取出来的第一个整数:" + i);
print("索引向前移动" + index + "个位置");
//读取字符串的长度
index = fileStream.Read(bytes, 0, 4);
print("索引向前移动" + index + "个位置");
//得到字符串的字节数组的长度
int length = BitConverter.ToInt32(bytes, 0);
byte[] strBytes = new byte[length];
//读取字符串
index = fileStream.Read(strBytes, 0, length);
print("读出来的字符串:" + Encoding.UTF8.GetString(strBytes));
print("索引向前移动" + index + "个位置");
//不要忘记关闭流
fileStream.Close();
fileStream.Dispose();
}运行结果:
-
一次性读取再挨个读取
就是将文件流的内容一次性读取到字节数组内,再挨个读取字节数组内的字节
注意,文件流的长度
length
,采用的是long
整数类型来计数(最大值900万兆),这意味着一个文件流可能非常大
而文件流的Read()
方法,一次性最多能读取的字节数是int
类型整数的最大值(最大值21亿)
这意味着,当这个文件流长度非常长,以至于超出了****int
类型整数的最大值时,一次****Read()
方法将无法读完所有内容****因此这个方法不适合超大型文件流的读取至于下面代码里的
using
是用来做什么的,可看 ——> using1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17using (FileStream fileStream = File.Open(Application.persistentDataPath + "/Lesson3.uni",
FileMode.Open,
FileAccess.Read))
{
//以文件流的字节长度声明字节数组
byte[] fileBytes = new byte[fileStream.Length];
//一次读取文件流所有的字节数据,装载到字节数组内,需要注意的是,这个方法一次只能读取int最大值的字节数
fileStream.Read(fileBytes, 0, (int)fileStream.Length);
fileStream.Dispose();
//读取整数
int i = BitConverter.ToInt32(fileBytes, 0);
print("取出来的第一个整数:" + i);
//读取字符串长度
int length = BitConverter.ToInt32(fileBytes, 4);
//读取字符串
print("读出来的字符串:" + Encoding.UTF8.GetString(fileBytes, 8, length));
}运行结果
更加安全的使用文件流对象
using
关键字重要用法,关于它的其他详细讲解可看 ——>using using
1 | using (申明一个引用对象) |
无论发生什么情况 当using
语句块结束后,会自动调用该对象的销毁方法 避免忘记销毁或关闭流
using
是一种更安全的使用方法
强调:目前我们对文件流进行操作 为了文件操作安全 都用using来进行处理最好
通过下面的代码,可以发现,不同using
语句块内的同名临时变量是不一样的,生命周期也不同
不过要注意,如果using
语句块外部已经声明了某个临时变量,using
内部就不能再次声明和该变量名同名的变量
1 | using (FileStream fileStream = File.Open(Application.persistentDataPath + "/Lesson3.uni", |