定义:内存中的数据模型与存储模型的相互转化。
类比:将游戏数据存储到硬盘,从硬盘中读取游戏数据。
定义:JavaScript对象简谱,一种全国通用的轻量级的数据交换格式。主要在网络通信中传输数据,或本地数据的存储与读取。
特点:纯文本,层级结构,都具有描述性,键值对存储。
基本语法演示:
//大括号包裹的代表一个类
{
"name" : "Shawn",
"age" : 18,
"sex" : true,
"height" : 1.8,
//中括号代表数组
"Students" : [{"name" : "小红","sex" : false},
{"name" : "小名","sex" : false}],
"home":{"address" : "成都","street" : "春熙路"},
"son" : null
}
Excel表转Json:
访问网址:https://www.bejson.com/json/col2json
//存储字符串到指定路径文件中
//Param1:路径(可读写文件夹)
//Param2:文本内容
File.WriteAllText(Application.persistentDataPath + ”Test.json“,"hhhhhh");
//从指定路径中读取字符串
string str = File.ReadAllText(Application.persistentDataPath + "Test.json");
//将类对象序列化为Json字符串
Student s = new Student();
string str = JsonUtility.ToJson(s);
反序列化:
//读取文件中的Json字符串并将其转换为类对象
类名 变量 = JsonUtility.FromJson<类名>(jsonStr);
[System.Serializable]
[SerializeField]
定义:第三方库,由C#编写,体积小速度快易于使用。
使用步骤:
Student t = new Student();
//序列化(引入命名空间)
string jsonStr = JsonMapper.ToJson(t);
File.WriteAllText(Application.persistentDataPath + ”Test.json“,jsonStr);
反序列化:
jsonStr = File.WriteAllText(Application.persistentDataPath + ”Test.json“);
//反序列化
JsonData data = JsonMapper.ToObject(jsonStr);
//需要用索引器去访问对象内容
data["name"];
//也可以通过泛型直接返回对象
Student s = JsonMapper.ToObject<Student>(jsonStr);
//LitJson可以直接读取数组,字典类型
Student[] stus = JsonMapper.ToObject<Student[]>(jsonStr);
List<Student> stus1 = JsonMapper.ToObject<List<Student>>();
Dictionary<string,int> dic = JsonMapper.ToObject<Dictionary<string,int>>(jsonStr);
LitJson
命名空间。JsonUtility与LitJson对比:
相同点:
不同点:
///
/// 序列化和反序列化Json时 使用的是哪种方案
///
public enum JsonType
{
JsonUtlity,
LitJson,
}
///
/// Json数据管理类 主要用于进行 Json的序列化存储到硬盘 和 反序列化从硬盘中读取到内存中
///
public class JsonMgr
{
private static JsonMgr instance = new JsonMgr();
public static JsonMgr Instance => instance;
private JsonMgr() { }
//存储Json数据 序列化
public void SaveData(object data, string fileName, JsonType type = JsonType.LitJson)
{
//确定存储路径
string path = Application.persistentDataPath + "/" + fileName + ".json";
//序列化 得到Json字符串
string jsonStr = "";
switch (type)
{
case JsonType.JsonUtlity:
jsonStr = JsonUtility.ToJson(data);
break;
case JsonType.LitJson:
jsonStr = JsonMapper.ToJson(data);
break;
}
//把序列化的Json字符串 存储到指定路径的文件中
File.WriteAllText(path, jsonStr);
}
//读取指定文件中的 Json数据 反序列化
public T LoadData<T>(string fileName, JsonType type = JsonType.LitJson) where T : new()
{
//确定从哪个路径读取
//首先先判断 默认数据文件夹中是否有我们想要的数据 如果有 就从中获取
string path = Application.streamingAssetsPath + "/" + fileName + ".json";
//先判断 是否存在这个文件
//如果不存在默认文件 就从 读写文件夹中去寻找
if(!File.Exists(path))
path = Application.persistentDataPath + "/" + fileName + ".json";
//如果读写文件夹中都还没有 那就返回一个默认对象
if (!File.Exists(path))
return new T();
//进行反序列化
string jsonStr = File.ReadAllText(path);
//数据对象
T data = default(T);
switch (type)
{
case JsonType.JsonUtlity:
data = JsonUtility.FromJson<T>(jsonStr);
break;
case JsonType.LitJson:
data = JsonMapper.ToObject<T>(jsonStr);
break;
}
//把对象返回出去
return data;
}
}
写在前面:C#变量的本质即为二进制。二进制文件读写的本质即为 将各类型变量转换为字节数组,将其直接存储到文件中,这样的作法不仅可以节约存储空间,提升效率,也可以提高安全性。
各类型数据与二进制相互转化:
//将各类型转字节
byte[] bytes = BitConverter.GetBytes(256);
print(bytes[0]);//0
print(bytes[1]);//1(第二位的1代表二进制八位,即十进制的256)
//将字节数组转换为其他类型
//param2:从第几个索引开始
int i = BitConverter.ToInt32(bytes,0)
//以指定编码格式转换字节数组
byte[] bytes = Encoding.UTF8.GetBytes("我是猪");
//以指定编码格式转换其他类型
string s = Encoding.UTF8.GetString(bytes);
API简介:
//判断文件是否存在
//Param:文件路径
bool isExist = File.Exists(Application.dataPath + "/data.json");
//创建文件
FileStream fs = File.Create(Application.dataPath + "/data.json");
//写入文件
byte[] bytes = BitConverter.GetBytes(999);
File.WriteAllBytes(Application.dataPath + "/data.json",bytes);
//写入字符串数组文件(自动空行)
string[] strs = new string[]("123","我是猪");
File.WriteAllLines(Application.dataPath + "/data.json",strs);
//将指定字符串存入指定路径(支持转义字符)
File.WriteAllText(Application.dataPath + "/data.json","哈哈哈哈\n嘿嘿嘿");
//读取文件(类比上述API,将Write改写为Read)
//删除文件
File.Delete(Application.dataPath + "/data.json");
//复制文件
//Param1:源文件
//Param2:目标文件
//Param3:如果已存在是否要覆盖原文件
File.Copy(Application.dataPath + "/data.json",Application.dataPath + "/data.guan",true);
//文件替换
//Param3:备份文件路径
File.Replace(Application.dataPath + "/data.json",Application.dataPath + "/data.guan",Application.dataPath + "/data备份.txt");
//打开文件并写入或读取
//Param2:文件模式,如果没有自动创建
//Param3:访问模式,只读只写
FileStream fs = File.Open(Application.dataPath + "/data.json",FileMode.OpenOrCreate,FileAccess.ReadWrite);
//打开或创建文件
FileStream fs = new FileStream(Application.dataPath + "/data.json",FileMode.CreateOrCreate,FileAccess.ReadWrite);
//文本字节长度
print(fs.Length);
//将字节写入文件,写入后,一定要执行一次
fs.Flush();
//关闭流,文件读写完毕执行
fs.Close();
//缓存资源的销毁回收
fs.Dispose();
//写入字节
FileStream fs = new FileStream(Application.persistentDataPath + "/Lesson.txt",FileMode.OpenOrCreate,FileAccess.Write);
byte[] test = new byte[1024];
byte[] bytes = BitConverter.GetBytes(999);
//Param2:开始字节索引
//Param3:写入多少字节
fs.Write(bytes,0,bytes.Length);
//写入字符串时,先写入长度
bytes = Encoding.UTF8.GetBytes("猪猪猪猪");
fs.Write(BitConverter.GetBytes(bytes.Length),0,4);
fs.Write(bytes,0,bytes.Length);
fs.Flush();
fs.Dispose();
//====================一个一个读取=======================
//读取字节
FileStream fs2 = File.Open(Application.persistentDataPath + "/Lesson.txt",FileMode.Open,FileAccess.Read);
//挨个读取字节数组
byte[] bytes2 = new byte[4];
int index = fs2.Read(bytes2,0,bytes.Length);
int i = BitConverter.ToInt32(bytes2,0);
print("取出来的第一个整数" + i);
print("索引下标" + index);
//读取字符串
index = fs2.Read(bytes2,0,4);
int length = BitConverter.ToInt32(bytes2,0);
//重新声明一个字节数组
bytes2 = new byte[length];
index = fs.Read(bytes2,0,length);
print(Encoding.UTF8.GetString(bytes2));
fs.Dispose();
//====================一次性读取========================
FileStream fs3 = File.Open(Application.persistentDataPath + "/Lesson.txt",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 length3 = BitConverter.ToInt32(bytes3,4);
//得到字符串
print(Encoding.UTF8.GetString(bytes3,8,length3));
//===============通过using改进IO===================
using(FileStream fs3 = File.Open(Application.persistentDataPath + "/Lesson.txt",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 length3 = BitConverter.ToInt32(bytes3,4);
//得到字符串
print(Encoding.UTF8.GetString(bytes3,8,length3));
}
//判断文件夹是否存在
Directory.Exists(Application.dataPath + "/test");
//创建文件夹并返回
DirectoryInfo dirInfo = Directory.CreateDirectory(Application.dataPath + "/test");
//删除文件夹
//Param2:是否删除非空目录
Directory.Delete(Application.dataPath + "/test",true);
//得到指定路径下的所有文件夹名
string[] strs = Directory.GetDirectories(Application.dataPath);
//得到指定路径下所有文件名
strs = Directory.GetFiles(Application.dataPath);
//移动文件夹
Directory.Move(Application.dataPath + "/test",Application.dataPath + "/123");
//===============DirectoryInfo类==================
//目录信息类DirectoryInfo
//文件夹的全路径
dirInfo.FullName;
//文件夹名
dirInfo.Name;
//上级文件夹信息
dirInfo = Directory.GetParent(Application.dataPath + "/test")
//查找子文件夹以及文件信息
DirectoryInfo[] dirInfos = dirInfo.GetDirectories();
FileInfo[] fileInfos = dirInfo.GetFiles();
补充知识1:Unity编辑器添加选项入口:
//必须为静态方法,通过类名点方式调用
//必须存在两个或以上斜杠
//这个类可以用在任何类中
[MenuItem("GameTool/Test/GenerateExcelInfo")]
private static void Test(){
Debug.Log("测试");
}
补充知识2:刷新Project窗口内容:
AssetDatabase.Refresh();
补充知识3:Editor文件夹
项目打包时,该文件夹下的文件无法打包。通常可以把编辑器相关代码放置其中。
导入Excel的Dll文件。
打开Excel表:
[MenuItem("GameTool/OpenExcel")]
private static void OpenExcel()
{
//文件流打开Excel
using(FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx",FileMode.Open,FileAccess.Read)){
//通过我们文件流获取Excel
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
//将表中数据转换为DataSet数据类型,方便获取其中内容
DataSet result = excelReader.AsDataSet();
//得到Excel文件中所有信息
for (int i = 0;i < result.Tables.Count;i++)
{
print(result.Tables[i].TableName);//表名
print(result.Tables[i].Rows.Count);//行数
print(result.Tables[i].Columns.Count);//列数
}
fs.Close();
}
}
获取Excel表中单元格的信息:
[MenuItem("GameTool/读取具体信息")]
private static void ReadExcel()
{
using(FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx",FileMode.Open,FileAccess.Read)){
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
DataSet result = excelReader.AsDataSet();
for (int i = 0;i < result.Tables.Count;i++)
{
//得到一张表的具体数据
DataTable table = result.Tables[i];
//得到其中一行数据
DataRow row = table.rows[0];
//得到某一个单元格的信息
string ans = row[1].ToString();
//遍历所有行的数据
for (int j = 0;j < table.Rows.Count;j++){
row = table.Rows[j];
//得到每一个单元格的内容信息
for (int k = 0;k < table.Columns.Count;k++){
print(row[k].ToString());
}
}
}
}
}
需求分析:通过一张Excel表格生成以下信息:
(放入Editor下,无需打包)
public class ExcelTool
{
//文件目录中Excel的路径
public static string EXCEL_PATH = Application.dataPath + "/ArtRes/Excel/";
//生成的数据结构类的路径
public static string DATA_CLASS_PATH = Application.dataPath + "/Scripts/ExcelData/DataClass/";
//生成的容器类的路径
public static string DATA_CONTAINER_PATH = Application.dataPath + "/Scripts/ExcelData/DataContainer/";
//生成的二进制文件路径
public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Binary/";
//在编辑器中添加按钮
[MenuItem("GameTool/GenerateExcel")]
private static void GenerateExcelInfo()
{
//得到指定路径下所有文件信息(Excel表)
FileInfo[] infos = Directory.CreateDirectory(EXCEL_PATH).GetFiles();;
//数据表容器
DataTableCollection tableCollection;
foreach(FileInfo info in infos)
{
//处理excel文件
if (info.Extension != ".xlsx" && info.Extension != ".xls")
continue;
//使用流的相关知识获取表内数据
using(FileStream fs = info.Open(FileMode.Open,FileAccess.Read))
{
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
tableCollection = excelReader.AsDataSet().Tables;
fs.Close();
}
//得到每一张表
foreach(DataTable table in tableCollection)
{
//生成数据结构类
GenerateExcelDataClass(table);
//生成容器类
GenerateExcelContainer(table);
//生成二进制数据
GenerateExcelBinary(table);
}
}
}
}
private static void GenerateExcelDataClass(DataTable table)
{
//字段名字
DataRow rowName = GetRowInfo(table,0);
//字段类型
DataRow rowType = GetRowInfo(table,1);
if (!Directory.Exists(DATA_CLASS_PATH))
Directory.CreateDirectory(DATA_CLASS_PATH);
//字符串拼接生成代码
string code = "public class " + table.TableName + "\n{\n";
//拼接变量
for (int i = 0;i < table.Columns.Count;i++)
{
code += " public " + rowType[i].ToString() + " " + rowName[i].ToString() + ";\n";
}
code += "}";
File.WriteAllText(DATA_CLASS_PATH + table.TableName + ".cs", code);
//刷新Projects窗口
AssetDatabase.Refresh();
}
//获得指定行所在行信息
//获得变量名所在行信息
private static DataRow GetRowInfo(DataTable table,int row)
{
return table.Rows[row];
}
private static void GenerateExcelContainer(DataTable table)
{
int keyIndex = GetKeyIndex(table);
DataRow rowType = GetRowInfo(table,1);
//没有路径则创建路径
if (!Directory.Exists(DATA_CONTAINER_PATH))
Directory.CreateDirectory(DATA_CONTAINER_PATH);
//字符串拼接生成类
string code = "using System.Collections.Generic;\n";
code += "public class " + table.TableName + "Container" + "\n{\n";
code += " public Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + "> dataDic = new Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">();\n";
code += "}";
File.WriteAllText(Data_CONTAINER_PATH + table.TableName + "Container.cs", code);
AssetDatabase.Refresh();
}
//找到主键所在的索引
private static int GetKeyIndex(DataTable table)
{
DataRow row = table.Rows[2];
for (int i = 0;i < table.Columns.Count; i++)
{
if (row[i].ToString() == "key")
{
return i;
}
}
return default(int);
}
(放在StreamingAssets包中,打包出去也要读取数据(只读))
private static void GenerateExcelBinary(DataTable table)
{
//判断路径下是否存在文件夹
if (!Directory.Exists(DATA_BINARY_PATH))
Directory.CreateDirectory(DATA_BINARY_PATH);
//创建一个二进制文件进行写入
using(FileStream fs = new FileStream(DATA_BINARY_PATH + table.TableName + ".shawn",FileMode.OpenOrCreate,FileAccess.Write))
{
//1.优先存储我们要写多少行
fs.Write(BitConverter.GetBytes(table.Rows.Count - 4),0,4);
//2.存储主键的变量名(id)
string keyName = GetRowInfo(table,0)[GetKeyIndex(table)].ToString();
byte[] bytes = Encoding.UTF8.GetBytes(keyName);
//存储字符串字节数组的长度
fs.Write(BitConverter.GetBytes(bytes.Length),0,4);
//存储字符串字节数组
fs.Write(bytes,0,bytes.Length);
//数据开始索引可定义为全局变量,后续可直接修改变量
DataRow row;
//得到类型
DataRow rowType = GetRowInfo(table,1);
for (int i = BEGIN_INDEX;i < table.Rows.Count;i++)
{
row = table.Rows[i];
for (int j = 0;j < table.Columns.Count;j++)
{
switch(rowType[j].ToString())
{
case "int":
fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())),0,4);
break;
case "float":
fs.Write(BitConverter.GetBytes(float.Parse(row[j].ToString())),0,4);
break;
case "bool":
fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())),0,1);
break;
case "string":
bytes = Encoding.UTF8.GetBytes(row[j].ToString());
fs.Write(BitConverter.GetBytes(bytes.Length),0,4);
fs.Write(bytes,0,bytes.Length);
break;
}
}
}
fs.Close();
}
AssetDatabase.Refresh();
}
概念:PlayerPrefs是可以用于读取玩家数据的公共类。
存储相关:
//存储三种数据类型
PlayerPrefs.SetInt("Age",18);
PlayerPrefs.SetFloat("Height",177.5f);
PlayerPrefs.SetString("Name","Shawn");
//调用Save会把数据存入硬盘
PlayerPrefs.Save();
读取相关:
//运行时,不需要Save也能读取到
string name = PlayerPrefs.GetString("Name");
//如果找不到对应值,函数会返回第二个参数
int age = PlayerPrefs.GetInt("age",100);
删除相关:
//删除指定键
PlayerPrefs.DeleteKey("age");
//删除所有信息
PlayerPrefs.DeleteAll();
Windows:
存储路径:HKCU\Software\[公司名称]\[产品名称] 下的注册表中
。
打开方式:
Android:
存储路径:/data/data/包名/shared_prefs/pkg-name.xml
IOS:
存储路径:/Libraray/Preferences/[应用ID].plist