读写文件

思考并回答以下问题:

  • 用Unity读取外部的txt文件,把里面的内容显示在UI上。txt文件放在Resources目录下和StreamingAssets目录下读取的方式一样吗?
  • 用File读取有什么缺点?为什么Unity要用到File类读写文件?
  • Path.Combine(path1, path2)怎么使用?

Unity项目中经常需要读写外部文件(指Asset文件夹之外),比如配置文件、外部的图片、音频、模型等等。来看一下在Unity中如何使用C#读写文件。

读写文件

读写文件在C#中有多种不同的方法,我们先来看一段简单的代码,这段代码加载了两个txt文件,可以读到文件中的文本内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System.IO;
using UnityEngine;

public class LearnFile : MonoBehaviour
{
void Start()
{
// 读取到内存中
// 绝对路径
var str1 = File.ReadAllText(@"C:\Users\newuser\Desktop\1.txt");
// 相对路径
var str2 = File.ReadAllText("2.txt");

Debug.Log(str1);
Debug.Log(str2);
}
}

2.txt文件需要放到和Assets文件夹同级的位置。

上面是一段非常简单的读取文件的代码,但是里面涉及到了几个知识点:

  • C#读写文件的类是什么?怎么使用?
  • 文件的路径在哪?
  • 文件没有读到如何处理?

C#读写文件的类

C#中有多种方法来读写文件,上面用到的是一种最简单的方式。也就是C#中的File类。

File类

File类在读取文件时会一次性读取文件的所有内容,在写文件时也会一次性将所有内容写入文件。

此外File类还是一个同步操作,也就是执行完才会执行下一条语句。如果文件比较大,可能会阻塞程序。所以一般建议对小文件使用这个类来读取会比较合适。

常用的File类中的方法有:

1
2
3
4
5
6
7
8
// 读取文件中的所有字节,常用于二进制文件
public static byte[] ReadAllBytes(string path);
// 读取文件中所有文本内容,常用于文本文件
public static string ReadAllText(string path);
// 一次性将字节数组写入文件
public static void WriteAllBytes(string path, byte[] bytes);
// 一次性将文本写入文件
public static void WriteAllText(string path, string contents);

要注意引用File类的命名空间System.IO:

1
using System.IO;

Unity相关路径

Unity的API提供了几种常用的路径:

1
2
3
4
Application.dataPath
Application.persistentDataPath
Application.streamingAssetsPath
Application.temporaryCachePath

下面我们来分别看看每个路径的用途。

Application.dataPath

这个路径指向了游戏的数据文件夹。在不同的运行环境中所返回的结果不尽相同:

Unity Editor: <工程路径>/Assets
Mac player: /Contents
iOS player: \/Data(只读)
Win/Linux player: (Linux路径区分大小写)
WebGL: 数据目录的绝对路径url(不包含数据文件名)
Android: 一般是APK安装路径,如果是分包会指向OBB路径

这个路径一般只在Editor和Standalone的player中使用,一般用于配置文件等外部文件的读取。

Application.persistentDataPath

永久保存文件的路径
用于玩家存档,外部数据等,可以在程序之间共用
一般我们不用关心具体存在哪里
App升级时,这个路径内的文件不会删掉
路径基于Bundle Identifier,如果Bundle Identifier不修改,那么app升级后还能访问到相同的路径

Application.streamingAssetsPath

将一些配置文件、可以在外部替换的文件放到工程Assets目录下StreamingAssets目录后,这个目录中的数据,在打包时不会编译转码,会原封不动拷贝到对应位置。

在Win/Linux/Mac平台上,build之后可以修改这个目录中的文件,可用来放置配置文件等。
Android上会被打包到Apk中,该路径只读,需要使用UnityWebRequest来获取
在WebGL中该路径只读,不能使用File操作,需要使用UnityWebRequest来获取

Application.temporaryCachePath

临时路径,可用于临时文件的中转可能被各种XX管家视为垃圾文件,不要用于存储

不需要死记硬背

这么多路径在不同平台上的路径都不相同,不需要死记硬背,只需要了解每个路径应该什么时候使用即可,因为我们可以在程序中打印出来对应项目的实际路径。

那你可能会说,在Editor中我们可以使用log输出,那发布出来以后怎么办呢?

其实发布出来以后也还有对应的log,只不过查看起来不太方便。

这时候可以使用一个LogViewer的插件。可以从Github或者Assetstore上免费下载安装:

(https://github.com/aliessmael/Unity-Logs-Viewer)
(https://www.assetstore.unity3d.com/en/#!/content/12047)

使用方法:

在buildsettings的第一个场景中使用菜单(Reporter->Create)创建Reporter。

将Reporter.cs的“Scrip execution order ” (Edit -> Project Settings)设置为最高(最小值)。运行时在屏幕上画一个圆,即可调出这个插件,看到运行的log了。

读写文件的一些坑

读写文件的时候也有一些常见的容易犯的错误。

路径拼接

读写文件经常会涉及到路径拼接的问题,比如将目录的路径和文件名进行拼接类似的操作。经常需要判断有没有加分隔符/,或者不同平台的分割符还不同,如果使用string的加法操作可能很容易出错。

拼接的时候建议使用Path.Combine(path1, path2)这个C#的API,他会帮你拼接两个路径。

只读目录

上面我们看到有一些只读的目录,需要使用UnityWebRequest读取(也就是之前的WWW类),后面我们会学到。

总结

如果读写文件不成功,首先要确认路径是否正确,充分利用插件的调试能力。然后再确认路径的权限,是否可以读写,如果是只读路径,需要用到后面学习的UnityWebRequest的方式读取。

0%