使用.NET提供的System.IO库中的File静态类,或者FileInfo类等。删除移动拷贝什么的比较简单就不写了,这两个类都能完成,都提供了对文件的基本操作,只不过FileInfo类提供的信息更加详细,还有文件的名称,所属文件夹等。
值得一提的是使用IO库读取文件使用斜杠'/'还是反斜杠'\'只在Windows平台没有区别,两者是可以互相转化的,用斜杠和反斜杠都能读取。但在移动平台必须使用斜杠。
void Start()
{
string path = Application.dataPath + "/object.txt";
path=path.Replace("/","\\");
//File静态类方式
if (File.Exists(path))
{
Debug.Log("FileExists");
}
else
{
Debug.Log("FileNotExists");
File.CreateText(path);
}
//FileInfo方式
FileInfo fileInfo = new FileInfo(path);
if (fileInfo.Exists)
{
Debug.Log("fileInfoExists");
}
else
{
Debug.Log("fileInfoNotExists");
fileInfo.Create();
}
}
使用DirectoryInfo类和FileInfo类,这两个类都直接继承自FileSystemInfo类,本质是树的遍历。
void Start()
{
string path = Application.dataPath;
path=path.Replace("/","\\");
DirectoryInfo directoryInfo = new DirectoryInfo(path);
Foreach(directoryInfo);
}
void Foreach(DirectoryInfo directoryInfo)
{
DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories();
for (int i = 0; i < directoryInfos.Length; i++)
{
Debug.Log(directoryInfos[i].Name);
Foreach(directoryInfos[i]);
}
FileInfo[] fileInfos = directoryInfo.GetFiles();
for (int i = 0; i < fileInfos.Length; i++)
{
Debug.Log(fileInfos[i].Name);
}
}
文件在物理上只有二进制一种存储方式。
文件在逻辑上分为二进制文件和文本文件,文本文件就是在二进制上多了一个编码解析,比如最常见的使用UTF-8编码格式的文本文件。
文件在物理上只有二进制一种存储方式,但我们在读取写入使用时仍然要用逻辑文件的方式。
这句话怎么理解呢,先问一个问题,为什么.NET库会提供这么多的读写API呢,什么文件流啊,什么二进制读写器,什么文本流读写器等等?不都是写个文件吗,最终不都是二进制吗,为啥还要搞这么多不同类型的API。
答案很简单,编码方式,即便在硬盘上存储了一个二进制值是0100 0001(65),我们可以认为这个一字节的二进制是直接按int存的,那么读出来就是65的int类型数据,也可以认为是按ASCII编码存的,那么65对应的字符值为'A'。
再举一个例子,比如我现在有一个数字1要写入硬盘,我下次加载游戏时要使用,那么就有两种方式存储,一,直接以数字1对应的二进制0000 0001写入,二,以字符数据的形式存入,字符'1'的ASCII值写入硬盘,也就是整数49,0011 0001。
我们虽然在物理上只有这一种纯二进制的方式,但是我们存的时候可能是直接存数字,也可能是存数字对应的字符。往绝对方向去讲,.NET完全可以只提供一种读写器,只读写字节流,数据什么的额外做转化就行,这些API本质是一样的,底层都是读取的字节流,只是省去了你手动转化字节数据的过程。你要是用的好,完全可以混着读写。不过最好还是配套读写。
二进制文件可以保存元数据,文本文件不可以,二进制位文件相较于文本文件更节省空间,更多区别自行百度。
使用.NET提供的System.IO库。只演示最基本的写法,不涉及异步等操作。
只演示File类读写。File类还可以协助创建FileStream流对象,然后用流的方式去读写数据。
void Start()
{
string path = Application.dataPath+"\\object.txt";
path=path.Replace("/","\\");
//直接以文本的方式读取
string content=File.ReadAllText(path);
Debug.Log(content);
//使用字节数组读取
byte[] bytes = File.ReadAllBytes(path);
//做解码,当然不止这一个类可以做解码
content=System.Text.Encoding.UTF8.GetString(bytes);
Debug.Log(content);
//File文件协助创建文件流的方式读取
FileStream fileStream= File.Open(path,FileMode.Open);
fileStream.Read(bytes, 0, bytes.Length);
content=System.Text.Encoding.UTF8.GetString(bytes);
fileStream.Close();
Debug.Log(content);
File.WriteAllText(path,"Hello world!",Encoding.Default);
}
FileStream用于以字节流的方式(以字节为单位)读取文件,读出来的数据是字节数组,写入同理。
既支持同步读写操作,也支持异步读写操作,适合读取大文件,同时也可以读取音频以及图片等资源,不过音频什么的一般用Unity的API直接读取了,省的再去解析数据。使用.NET主要是做一些配置文件的读取,至于其他资源Unity有自己可用的资源加载方案。
同一时间只允许对一个文件进行读写,文件流用完之后要及时关闭并释放资源。
文件流的读写不要同时进行,除非你清楚这么做。文件流内部维护了一个文件指针用于读写文件,文件流读完后指针指向文末,而在这一次流内要是写文件就从当前流的文件指针位置即文末开始写。变成了以追加的方式写入数据。
void Start()
{
string path = Application.dataPath+"\\object.txt";
path=path.Replace("/","\\");
FileStream fileStream = new FileStream(path,FileMode.Open);
byte[] bytes = new byte[1024];
fileStream.Read(bytes,0,bytes.Length);
string content=System.Text.Encoding.UTF8.GetString(bytes);
Debug.Log(content);
//以Append的方式写入数据,不能在同一次流内读和写,除非你清楚这么做。
string content1 = "hello world!";
bytes=System.Text.Encoding.Default.GetBytes(content1);
//fileStream.Write(bytes);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close();
//从头写入数据,要是想从文末追加内容,只需设置为FileMode.Append
FileStream fileStream1 = new FileStream(path, FileMode.Open);
fileStream1.Write(bytes);
fileStream1.Close();
}
StreamReader以一种特定的编码从字节流中读取字符,或者说以字符流的方式读取文件,不同于FileStream,StreamReader直接返回解码后的字符串,省去了程序员手动转换字节数组的过程。写入同理。
void Start()
{
string path = Application.dataPath+"\\object.txt";
StreamReader streamReader = new StreamReader(path);
string content=streamReader.ReadToEnd();
Debug.Log(content);
streamReader.Close();
StreamWriter streamWriter = new StreamWriter(path);
string content1 = "hello world!";
streamWriter.Write(content1);
streamWriter.Close();
}
BinaryReader直接读取二进制值,官网的话来说就是用特定的编码将基元数据类型读作二进制值。这个理解起来有点困难,官网举的代码可以很好的说明问题。
BinaryReader 类 (System.IO) | Microsoft Learn
使用这个要注意的是,你要是用BinaryReader读的话,那么这个文件最好是用BinaryWriter写入的,否则BinaryReader无法知道怎么读的数据,很容易读超过文件流的范围,或者读错数据。
比如你用BinaryWriter写入非记事本默认编码的数据,然后用记事本打开,就会出现奇奇怪怪的数据,你用二进制编辑器打开才会显示正确数据,你可以理解你存进去一个int值,但是你用记事本默认读取时把int数当成对应的字符编码读取,自然出现其他怪怪的数据。
BinaryReader一次读取多少字节的数据由你给出的数据类型或者说调用的API指出,读出的二进制字节会直接转成你需要的数据类型。
文件流使用完后要及时调用Dispose接口释放资源,但很多时候我们可能会忘记关闭,using就可以帮助我们。
using (FileStream stream=File.Open(path,FileMode.Open))
{
stream.Write(null,0,0);
}
使用using会帮我们自动释放流资源。
读写字符数据(比如Json字符串)的时候使用StreamReader,不读写字符数据时(比如大量数字)可以考虑使用 BinaryReader。
单纯读写字节数组使用FileStream,FileStream返回的是纯粹的字节数组(直接从硬盘读取的二进制流),需要自己根据存储时用的编码做额外转化。使用字节数组也可以根据某种算法实现文件加密,比如在字节序列中随机插入几个0和1等等。
还有就是你是怎么写入文件时,最好也用配套的读取,当然如果你很熟悉各个读写API的功能和你写入时的方式,也可以混着读写,毕竟物理本质是二进制文件。
UnityAPI大多都提供了对文件的读取,但无法动态写入数据。Unity的API必须使用正斜杠'/'读取。官网原话:Unity 中的所有资源名称和路径都使用正斜杠,即使是在 Windows 上。
这个功能不多,不再赘述。
UnityWebRequest类也可用于访问本地文件。
void Start()
{
string path = Application.dataPath + "/object.txt";
UnityWebRequest webRequest = UnityWebRequest.Get(path);
webRequest.SendWebRequest();
while (true)
{
if (webRequest.downloadHandler.isDone)
{
string content=webRequest.downloadHandler.text;
webRequest.Dispose();
Debug.Log(content);
break;
}
}
}
Resources读取的文件必须处于Resources下,且一旦打包会被加密无法通过IO库写入。并且文件路径不需要扩展名。路径直接从Resources下出发,不含Resources。
void Start()
{
string path = "object";
TextAsset textAsset = Resources.Load(path);
Debug.Log(textAsset.text);
}
文本文件必须位于AB包内。
void Start()
{
string path =Application.dataPath+ "/object.ab";
AssetBundle assetBundle=AssetBundle.LoadFromFile(path);
TextAsset textAsset=assetBundle.LoadAsset("object.txt");
Debug.Log(textAsset.text);
}
只适用于编辑器下,文件路径从Assets下出发,包含Assets。
void Start()
{
string path ="Assets/object.txt";
TextAsset textAsset=AssetDatabase.LoadAssetAtPath(path);
Debug.Log(textAsset.text);
}
文件被读取之后一般都是字节流或者直接字符串,我们需要按照序列化时的格式进行反序列化后才能使用。