数据持久化:二进制

L1各数据转字节

1
2
3
4
5
6
7
8
9
10
11
byte[] bytes = BitConverter.GetBytes(1 << 6);
PrintByte(bytes);
//字节数组转类型
var i = BitConverter.ToInt32(bytes, 0);
print(i);
//字符串指定编码转字节
byte[] bytes1 = Encoding.UTF8.GetBytes("你好世界");
PrintByte(bytes1);
//字节转字符串
string byteString1 = Encoding.UTF8.GetString(bytes1);
print(byteString1);

L2文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//1.判断文件存在
print(File.Exists(Application.dataPath + "/../Lesson"));
//2.创建文件
//FileStream fs = File.Create(Application.dataPath + "/Test.Test");
//3.写入文件
byte[] bytes = Encoding.UTF8.GetBytes("你好世界");
byte[] bytes1 = BitConverter.GetBytes(1 << 8);
File.WriteAllBytes(Application.dataPath + "/Test.Test", bytes1);
//将指定的 string 数组内容一行行写入到指定路径中
string[] strings = new string[] { "sdad", "dsadw" };
File.WriteAllLines(Application.dataPath + "/Test1.Test", strings);
//将指定字符串写入指定路径
string strings1 = "\n你好世界\r\n";
File.WriteAllText(Application.dataPath + "/Test2.Test", strings1);
//4.读取文件
bytes = File.ReadAllBytes(Application.dataPath + "/Test.Test");
PrintByte(bytes);
strings = File.ReadAllLines(Application.dataPath + "/Test1.Test");
PrintStrings(strings);
strings1 = File.ReadAllText(Application.dataPath + "/Test2.Test");
print(strings1);
//5.删除
File.Delete(Application.dataPath + "/Test.Test");
//6.复制文件
// 参数一:现有文件,需要是流关闭状态
// 参数二:目标文件
// 参数三:是否覆盖
File.Copy(AssetURL("Test1.Test"), AssetURL("Test2.Test"), true);
//7.文件替换
// 参数一:用来替换的路径
// 参数二:被替换的路径
// 参数三:备份路径
File.Replace(AssetURL("Test2.Test"), AssetURL("Test1.Test"), AssetURL("Test2Copy.Test"));
//8.以流的形式打开文件并写入或读取
//参数一:路径
//参数二:打开模式
//参数三:访问模式
FileStream fs = File.Open(AssetURL("Test1.Test"), FileMode.OpenOrCreate, FileAccess.Read);

//辅助函数
private String AssetURL(string url)
{
return Application.dataPath + "/" + url;
}

private void PrintByte(byte[] bytes)
{
var byteStr = "";
foreach (byte b in bytes)
{
byteStr += b;
}
print(byteStr);
}
private void PrintStrings(string[] strings)
{
var byteStr = "";
foreach (string b in strings)
{
byteStr += b;
}
print(byteStr);
}`

L3文件流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#region 1.打开或创建指定文件
//方法-:new Filestream
//参数一:路径
//参数二:打开模式
//CreateNew: 创建新文件如果文件存在则报错
//Create: 创建文件,如果文件存在则覆盖
//Open: 打开文件,如果文件不存在报错
//OpenorCreate: 打开或者创建文件根据实际情况操作
//Append: 若存在文件,则打开并查找文件尾,或者创建一个新文件
//Truncate: 打开并清空文件内容
//参数三: 访问模式
//参数四: 共享权限
//None 谢绝共享
//Read 允许别的程序读取当前文件
//Write 允许别的程序写入该文件
//ReadWrite 允许别的程序读写该文件
FileStream fs = new FileStream(PeresistentURL("TestNew.Test"), FileMode.OpenOrCreate, FileAccess.ReadWrite);

//方法三:Fi1e.0pen
//参数一:路径
//参数二:打开模式
FileStream fs1 = File.Open(PeresistentURL("TestNew1.Test"), FileMode.Open);
fs1.Close();
fs1.Dispose();
//方法二:File.Create
//参数一路径
//参数二:缓存大小
//参数三:描述如何创建或覆盖该文件(不常用)
//Asynchronous 可用于异步读写
//DeleteOnclose 不在使用时,自动删除
//Encrypted 加密
//None 不应用其它选项
//RandomAccess 随机访问文件
//Sequentialscan 从头到尾顺序访问文件
//WriteThrough 通过中间缓存直接写入磁盘
FileStream fs2 = File.Create(PeresistentURL("TestNew1.Test"), 2048);
fs2.Close();
fs2.Dispose();
#endregion

#region 2.属性方法
//1. 是否可写
if (fs.CanRead) { }
//2.是否可读
if (fs.CanWrite) { }
//3.将字节写入文件当写入后一定执行一次
fs.Flush();
//4.关闭流当文件读写完毕后一定执行
fs.Close();
//5.缓存资源销毁回收
fs.Dispose();
#endregion

#region 3.写入字节
//方法:Write
//参数一:写入的字节数组
//参数二:数组中的开始索引
//参数三:写入多少个字节
using (fs = new FileStream(PeresistentURL("TestNew.Test"), FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
byte[] bytes = BitConverter.GetBytes(1 << 8);
fs.Write(bytes, 0, bytes.Length);

//写入字符串时
bytes = Encoding.UTF8.GetBytes("hello world");
//先写入长度
byte[] length = BitConverter.GetBytes(bytes.Length);
fs.Write(length, 0, length.Length);
//再写入字符串具体内容
fs.Write(bytes, 0, bytes.Length);

fs.Flush();//3.将字节写入文件当写入后一定执行一次
fs.Close(); //5.缓存资源销毁回收
};

#endregion

#region 4.读取字节
//方法一
using (fs = File.Open(PeresistentURL("TestNew.Test"), FileMode.Open, FileAccess.Read))
{
//参数一:用于存储读取的字节数组的容器
//参数二:容器中开始的位置
//参数三:读取多少个字节装入容器
//返回值:当前流索引前进了几个位置
byte[] bytes2 = new byte[4];
int index = fs.Read(bytes2, 0, 4);
int i = BitConverter.ToInt32(bytes2, 0);
print("取出来的第一个整数" + i);//999
print("索引向前移动" + index + "个位置");

//读取第二个字符串
//读取字符串字节数组长度
index = fs.Read(bytes2, 0, 4);
print("索引向前移动" + index + "个位置");
int length1 = BitConverter.ToInt32(bytes2, 0);
bytes2 = new byte[length1];
index = fs.Read(bytes2, 0, length1);
print("索引向前移动" + index + "个位置");
//得到最终的字符串打印出来
print(Encoding.UTF8.GetString(bytes2));

fs.Flush();
fs.Close();
}

//方法二 一次性读取长度
using (fs = File.Open(PeresistentURL("/TestNew.Test"), FileMode.Open))
{
print("=============================");
//一开始就申明一个和文件字节数组长度一样的容器
byte[] bytes3 = new byte[fs.Length];
fs.Read(bytes3, 0, bytes3.Length);
fs.Dispose();
//读取整数
print(BitConverter.ToInt32(bytes3, 0));
//读取字符串字节数组的长度
int length2 = BitConverter.ToInt32(bytes3, 4);
//得到字符串
print(Encoding.UTF8.GetString(bytes3, 8, length2));
}
#endregion

#region 知识点三更加安全的使用文件流对象
//using 关键字重要用法
//using (申明一个引用对象) I
//使用对象
//无论发生什么情况当 using 语句块结束后会自动调用该对象的销毁方法避免忘记销毁或关闭流
//using 是一种更安全的使用方法/强调:
//目前我们对文件流进行操作为了文件操作安全都用 using 来进行处理最好
#endregion


private String AssetURL(string url)
{
return Application.dataPath + "/" + url;
}

private String PeresistentURL(string url)
{
print(Application.persistentDataPath + "/" + url);
return Application.persistentDataPath + "/" + url;
}

L5类对象序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#region 知识点一序列化类对象第一步-申明类对象
//注意:如果要使用 C#自带的列化 2 进制方法
//申明类时需要添加[System. Serializable]特性
#endregion

#region 知识点二序列化类对象第二步-将对象进行 2 进制序列化
//方法一:使用内存流得到 2 进制字节数组
//主要用于得到字节数组可以用于网络传输
//新知识点
//1.内存流对象
//类名:MemoryStream
//命名空间:System.IO
//2.二进制格式化对象
//类名:BinaryFormatter
//命名空间:System.Runtime.Serialization.Formatters.Binary
//主要方法:序列化方法 Serialize
using (MemoryStream ms = new MemoryStream())
{
//二进制格式化程序
BinaryFormatter bf = new BinaryFormatter();
//序列化对象生成二进制字节数组写入到内存流当中
Person person = new Person();
bf.Serialize(ms, person);
//得到对象的二进制字节数组
byte[] bytes = ms.GetBuffer();
//存储字节
File.WriteAllBytes(Application.dataPath + "/Lesson/L5类对象序列化/TestBinary.Test", bytes);
//关闭内存流
ms.Close();
}

//方法二:使用文件流进行存储
//主要用于存储到文件中
using(FileStream fs = new FileStream(Application.dataPath + "/Lesson/L5类对象序列化/TestBinary1.Test",FileMode.OpenOrCreate,FileAccess.Write))
{
//二进制格式化
BinaryFormatter bf = new BinaryFormatter();
//序列化对象 生成二进制字节数组写入
Person person = new Person();
bf.Serialize(fs, person);
fs.Flush();
fs.Close();
}
#endregion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
string url = Application.dataPath + "/Lesson/L5类对象序列化/TestBinary1.Test";
#region 知识点一 反序列化
using (FileStream fs = File.Open(url, FileMode.Open, FileAccess.Read))
{
BinaryFormatter bf = new BinaryFormatter();
//反序列化
Person p = bf.Deserialize(fs) as Person;
fs.Flush();
fs.Close();
}
#endregion

#region 知识点二 反序列化之反序列化网络传输过来的2进制数据
//主要类
//MemoryStream内存流类
//BinaryFormatter 2进制格式化类
//主要方法
//Deserizlize
//目前没有网络传输我们还是直接从文件中获取
byte[] bytes = File.ReadAllBytes(url);
// 申明内存流对象一开始就把字节数组传输进去
using (MemoryStream ms = new MemoryStream(bytes))
{
//申明一个2进制格式化类
BinaryFormatter bf = new BinaryFormatter();
//反序列化
Person p = bf.Deserialize(ms) as Person;
ms.Close();
}
#endregion

L7异或加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Person person = new Person();
byte key = 51;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, person);
byte[] buffer = ms.GetBuffer();
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] ^= key;
}
File.WriteAllBytes(Application.dataPath + "/Lesson/L7异或加密/AddKey.Test", buffer);
}

byte[] bytes = File.ReadAllBytes(Application.dataPath + "/Lesson/L7异或加密/AddKey.Test");
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] ^= key;
}
using (MemoryStream ms = new MemoryStream(bytes))
{
BinaryFormatter bf = new BinaryFormatter();
person = bf.Deserialize(ms) as Person;
ms.Close();
}

LessonEditor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#region 知识点一 为编辑器菜单栏添加新的选项入口
//可以通过Unity提供我们的MenuItem特性在菜单栏添加选项按钮
//特性名:MenuItem
//命名空间:UnityEditor
//规则一:一定是静态方法
//规则二:我们这个菜单栏按钮 必须有至少一个斜杠 不然会报错它不支持只有一个菜单栏入口
//规则三:这个特性可以用在任意的类当中
[MenuItem("GameTool/Test")]
private static void Test()
{
Debug.Log("测试测试");
#region 知识点二刷新Project窗口内容
//类名:AssetDatabase
//命名空间:UnityEditor
//方法:Refresh
Directory.CreateDirectory(Application.dataPath + "/测试文件夹");
AssetDatabase.Refresh();
#endregion
}
#endregion

#region 知识点三 Editor文件夹
//Editor文件夹可以放在项目的任何文件夹下,可以有多个
//放在其中的内容,项目打包时不会被打包到项目中
//—般编辑器相关代码都可以放在该文件夹中
#endregion

#region 总结
//我们之后在学习上Excel表生成数据的功能时
//可以在菜单栏加一个按钮
//点击后就可以自动为我们生成对应数据了
#endregion

Execise1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class Student
{
public int age;
public string name;
public int number;
public bool sex;

public void Save(string fileName)
{
Debug.Log(GetPersistentURL(fileName));
if (!Directory.Exists(GetPersistentURL("Student")))
{
Directory.CreateDirectory(GetPersistentURL("Student"));
}
using (FileStream fs = new FileStream(GetPersistentURL("Student/" + fileName), FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] bytes;
//age
bytes = BitConverter.GetBytes(age);
fs.Write(bytes, 0, bytes.Length);
//name
bytes = Encoding.UTF8.GetBytes(name);
byte[] length = BitConverter.GetBytes(bytes.Length);
fs.Write(length, 0, sizeof(int));
fs.Write(bytes, 0, bytes.Length);
//number
bytes = BitConverter.GetBytes(number);
fs.Write(bytes, 0, bytes.Length);
//sex
bytes = BitConverter.GetBytes(sex);
fs.Write(bytes, 0, bytes.Length);

fs.Flush();
fs.Close();
}
}

public static Student Load(string fileName)
{
string url = Application.persistentDataPath + "/Student/" + fileName;
if (File.Exists(url))
{
Student s = new Student();
using (FileStream fs = File.Open(url, FileMode.Open, FileAccess.Read))
{
byte[] bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes.Length);
fs.Close();

int index = 0;
s.age = BitConverter.ToInt32(bytes, index);
index += sizeof(int);

int length = BitConverter.ToInt32(bytes, index);
index += sizeof(int);

s.name = Encoding.UTF8.GetString(bytes, index, length);
index += length;

s.number = BitConverter.ToInt32(bytes, index);
index += sizeof(int);

s.sex = BitConverter.ToBoolean(bytes, index);
index += sizeof(bool);
return s;
}
}
Debug.LogError("不存在" + url);
return null;
}

public string GetPersistentURL(string url)
{
return Application.persistentDataPath + "/" + url;
}
}

public class Execise1 : MonoBehaviour
{
private void Start()
{
Student s = new Student() { age = 0, name = "admin", number = 0, sex = true };
s.Save("admin.Test");
s= Student.Load("admin.Test");
}
}