Unity 社区
2
1

从零开始的Unity学习_15. 文件和流(上)

M
Mr.He
日复一日,必有精进!
阅读 472
2023年1月10日
这次是C#对文件的操作。

#前言

程序中创建的变量无法持久的保存,下一次打开程序获得上一次变量的值。为了实现游戏中的存档操作,就需要将数据存储为文件,下一次打开时去读取文件中的内容,以此来实现数据的存储。
这篇文章参考的是《C#高级编程》第二十三章:
下面是《C#高级编程 第10版》的pdf文件分享
链接:https://pan.baidu.com/s/1DfNICYLJ-zy17flRpzB7Cw?pwd=2233 提取码:2233

#正文

#概述

这次是上篇,我们先来看看基本的操纵文件系统的几个类和方法,有了它们可以轻松的进行文本文件的读取和写入,配上上一篇讲的Json之类的,就可以轻松实现游戏存档的功能了。

#管理文件系统

首先介绍一下文件系统相关的类:
FileSystemInfo:这是所有文件系统对象的基类。
FileInfoFile:它们表示文件系统上的文件(对文件进行操作的类。)。
DirectoryInfoDirectory:它们表示文件系统上的文件夹(对文件夹进行操作的类。)。
Path:这个类用于处理路径名。
DriveInfo:它的属性和方法提供了指定驱动器的信息。
Directory类和File类只包含静态方法,不能被实例化。而DirectoryInfo类和File类则可以被实例化。
Directory和DirectoryInfo(File和FileInfo同理)方法功能类似,最大的区别是Directory类是静态方法,使用时不需要创建对象,而DirectoryInfo类则需要在一开始创建对象才能使用。
所以在只对文件进行一次操作的时候使用Directory显然更加有利,在对同一个文件进行多次操作时,DirectoryInfo会比Diretory更好用,因为Directory每次操作都需要将文件整个读入,而Directory只需要读一次(创建对象的时候进行读取)。
  • 查看驱动器信息(DriveInfo)
没啥好说的,直接看代码。
DriveInfo[] drives = DriveInfo.GetDrives(); foreach (DriveInfo drive in drives) { if (drive.IsReady) { Debug.Log($"Drive Name:{drive.Name}"); Debug.Log($"Format:{drive.DriveFormat}"); Debug.Log($"Type:{drive.DriveType}"); Debug.Log($"Root directory:{drive.RootDirectory}"); Debug.Log($"Volume label:{drive.VolumeLabel}"); Debug.Log($"Free space:{drive.TotalFreeSpace}"); Debug.Log($"Available space:{drive.AvailableFreeSpace}"); Debug.Log($"Total Size:{drive.TotalSize}"); } }
  • Path
主要的就是Path.Combine(string, string) 这个方法可以查找第一个参数文件夹下面的名为第二个参数的文件,并返回文件的完整地址。
比如Path.Combine(@"D:\Projects", "ReadMe.txt") 这就是寻找D:\Projects文件夹下面名为ReadMe.txt的文件,并返回ReadMe.txt的完整路径。(@”“表示双引号中的转移字符按照原本的输出,不再有转义的功能。)
在Unity中常常看到第一个参数是一些Unity的特殊文件夹,比如Application.persistentDataPath之类的。
  • 创建文件和文件夹
接下来就是介绍File,FileInfo,Directory和DirectoryInfo类了。
首先是创建文件:
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); File.WriteAllText(fileName, "Hello World");
这段代码可以在你Unity的项目列表下面创建一个名为MyText.txt的文本文件,里面的内容是Hello Wrold。(运行之后并不会直接在你Unity的Project视图中显示,需要你刷新一下视图,Projects和文件管理器并不是完全同步的。)还有就是注意第一个参数的文件夹必须是存在的,不然会报错。
然后是创建文件夹:
Directory.CreateDirectory(Application.dataPath + "//TestDirectory");
创建文件夹的时候并没有要求前面的文件夹必须要存在,如果不存在会被一并创建。
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); FileInfo fileInfo = new FileInfo(fileName); fileInfo.CopyTo(Path.Combine(Application.dataPath + "\\ReadMe.txt")); File.Copy(fileName, Path.Combine(Application.dataPath + "\\ReadMe.txt"));
复制首先要求第一个参数的文件是存在的,然后要求第二个参数的文件夹是存在的。
接下来我们来讲讲使用FileInfo和DirectoryInfo的注意事项:
这两个类使用一个string的路径创建。但它们在创建时并不会检测这个传入的路径是否真的存在文件或者文件夹,就算不存在文件或者文件夹,也不会报错,而是在后面使用内部的方法时报错。
所以我们最好在使用两个类型的对象时,先判断一下文件或者文件夹是否是存在的:
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); FileInfo fileInfo = new FileInfo(fileName); if (fileInfo.Exists) { fileInfo.CopyTo(Path.Combine(Application.dataPath + "\\ReadMe.txt")); }
也就是使用fileInfo.Exists来判断一下,路径是否真的存在文件。(DirectoryInfo也是同理。)
额外说一点,如果你将文件夹的路径用于创建FileInfo,那么部分属性是可以用的,因为文件夹刚好也有这个属性。
还有就是移动和删除文件和文件夹的方法:FileInfo和DirectoryInfo是MoveTo()和Delete(),File和Directory是Move()和Delete()。
没有可以直接复制完整文件夹的方法,如果有需求只能一份份的复制原来文件夹中的文件。
  • 访问和修改文件的属性
我们简单一下使用FileInfo访问文件的信息。
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); FileInfo fileInfo = new FileInfo(fileName); if (fileInfo.Exists) { Debug.Log($"Name:{fileInfo.Name}"); Debug.Log($"Directory:{fileInfo.DirectoryName}"); Debug.Log($"Read only:{fileInfo.IsReadOnly}"); Debug.Log($"Extension:{fileInfo.Extension}"); Debug.Log($"Length:{fileInfo.Length}"); Debug.Log($"Creation time:{fileInfo.CreationTime.ToString("F")}"); Debug.Log($"Access time:{fileInfo.LastAccessTime:F}"); Debug.Log($"File attributes:{fileInfo.Attributes}"); }
发现了一个奇怪的语法糖,就是LastAcessTime:F,这个:F就和上一行的ToString("F")效果一样,不建议这么写,免得出现除了自己之外没人知道的情况。(奇怪的知识增加了。)
其中创建时间和最后一次访问时间是可以修改的。
  • 使用File执行读写操作
首先是读取操作:
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); //全部读取出来,空格换行符这些都会被读出来 string fileText = File.ReadAllText(fileName); //把每行读取入字符串数组 string[] fileLines = File.ReadAllLines(fileName);
然后是写入:
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); //是一种覆盖式的写入,删掉原来的文本内容,直接替换为新的文本 File.WriteAllText(fileName, "UNITY"); string[] a = { "UNITY", "Hello" }; //同样是覆盖式的写入,第二个参数需要是IEnumerable<string>迭代器接口的类型 File.WriteAllLines(fileName, a);
最后是插入:
string fileName = Path.Combine(Application.dataPath, "MyText.txt"); //插入在文件结尾 File.AppendAllText(fileName, "新年快乐");

#结束语

  • 建议
File的读取难度较低,但挺好用的,基本上敲一遍都可以搞定。
  • 杂谈
年末了,想想去年的这个时候,我应该已经放寒假在家……不对,去年我这个时间也在工作,好像没啥好回忆的诶。
感觉这一年发生了好多事,也没啥好回忆的,重要的是当下和未来嘛。马上要过年了,我只想要躺在沙发上,盖着被子看电视,然后吃橘子,今年要多买点橘子吃。今年我打死也不去走人夫了,我去年一直在走人夫就没有停下来过,而且那些亲戚我一个都不认识!
这是年前的最后一期了,年后也很长时间不会更新了,我不会带电脑回老家,没法写,而且大家过年也不会想看这些东西嘛。(可能平时也没人想看(╬▔皿▔)╯。)(如果你过年还在学习,那我敬你是个卷王。)
那我给大家拜个早年。(如果过年看到就是拜个正常年,如果年后看到那就是拜个晚年。)
谢谢大家的观看,拜拜,年后再见。
发布于技术交流
1条评论

AI

全新AI功能上线

1. 基于Unity微调:专为Unity优化,提供精准高效的支持。

2. 深度集成:内置于团结引擎,随时查阅与学习。

3. 多功能支持:全面解决技术问题与学习需求。

AI