Unity 关于安卓和各平台读写本地json文件,WWW读取本地文件,Unity各路径API(persistentDataPath,StreamingAssets文件夹),目前较完整的详解

关键点:

···C# API  (StreamReader\ StreamWriter \ System.Text.Encoding\String.Split)

···Unity API ( JsonUtility.fromjson \jsonUtility.tojson \ WWW )

··UnityAPI 提供的路径参考(常用的):

Application.dataPath 
Application.streamingAssetsPath 
Application.persistentDataPath

 

··· txt格式的json文本文件 , 编码(UTF-8\Unicode\Ascii),  Bom字符问题。

______________________________________________________________________________________________

 

研究背景: 因自己手头正好有一个安卓上的游戏应用,需要读取和写入本地的json文件,例如武器信息/人物属性/得分结算,所以研究了一下。虽然这一块网上的教程都挺多的,但是没找到特别详细和完整,而且针对不同平台会出现不同问题,所以自己研究了很久,也是挖了不少的坑,最后才摸的比较清楚这一块了,跑来写这一篇技术文档。

用的是windows的系统,以及安卓真机,亲测可用。Mac Liunx 和 IOS 还没有测试过,但是照着这个思路做是一样的。

说明一下,该教程没有用到任何插件,包括 读写json的用的也是Unity自带的API。 没有使用jsonLit。 一样很方便好用。

JsonUtility.fromjson(string json)    从字符串类型的json字符信息,转换成 字段  用于构造函数。

JsonUtility.tojson(obj object)   将obj 类型转化成 Json的字符信息  是string类型。

______________________________________________________________________________________________

进入正题:

首先我们要解决在编辑器Editor模式下,window系统环境上 读取和写入本地文件。

这边先介绍两种方法,开头提过。一种是用C#提供的API   StreamReader 和 StreamWriter,顾名思义就是读和写,这两个只要给一个字符串类型的路径参数就可以完成工作。(一般能用这两个API读写就尽量用它们, 少用WWW读写)

还有一种使用Unity提供的WWW方法,这个方法只能用来作读取,主要是用在服务器上的,可以从网络上 下载图片 文字 等其他信息,给一个http链接即可,  常用支持协议: ( http://   https://   ftp://    file:// )  顾名思义最后一个可以用来读本地文件。

关于以上API更详细的信息,推荐翻 官方手册。  针对不同平台,还会有些 小变化和问题,要注意。

以下示例:

 

 
  1. using System.Collections;

  2. using System.Collections.Generic;

  3. using UnityEngine;

  4.  
  5. //额外引用

  6. using System.IO;

  7. using System.text;

  8. public class blogtest : MonoBehaviour {

  9.  
  10. //一个测试类

  11. class testJson

  12. {

  13. public string Name;

  14.  
  15. public testJson(string m_name)

  16. {

  17. Name = m_name;

  18. }

  19. }

  20. List jsonList = new List();

  21.  
  22. void Awake()

  23. {

  24. //Editor模式 Window系统

  25. //先用C#的API做读写,需要引入 using system.IO的命名空间。

  26. //该路径是在Project视图下,Asset下的Resources文件夹里。可以往下再写路径。

  27. //这个文件是预先创建的,往里面写几行json信息或其他文本信息。

  28. StreamReader sr = new StreamReader(Application.dataPath + "/Resou/json1.txt");

  29.  
  30. //临时存储json信息

  31. string jsonInfo;

  32.  
  33. //可以看下包含的一些常用方法

  34. //包含文件从头到尾的信息,存在一个字符串里。

  35. jsonInfo = sr.ReadToEnd();

  36. //读取每一行的文件信息。

  37. jsonInfo = sr.ReadLine();

  38.  
  39. //写一个循环体 打印一下。

  40. while((jsonInfo = sr.ReadLine())!=null)

  41. {

  42. //按顺序打印出每行的文本信息 会包含“{ }”json的中括号字符

  43. //这里要注意一个编码问题,如果文本信息是全英文的就没事,如果有包含中文的要使用UTF-8的编码,否则就出现乱码.

  44. Debug.Log(jsonInfo);

  45.  
  46. //如果json信息里包含的是构造函数的参数,就写在这里。

  47. jsonList.Add(JsonUtility.FromJson(jsonInfo));

  48. }

  49.  
  50. //读完后关闭。

  51. sr.Close();

  52.  
  53. //接着是写入操作 格式是差不多的。写入通常就是创建,就算没有这个文件名,也会自动创建。

  54. //这里要注意指定编码格式 建议用UTF8支持中文,需要 using System.Text;

  55. StreamWriter sw = new StreamWriter(Application.dataPath + "/Resources/json2.txt",false,Encoding.UTF8);

  56. //用之前存储的testjson类列表信息

  57. for (int i = 0; i < jsonList.Count; i++)

  58. {

  59. //将列表中的每一行信息,转换成json格式然后写入文件。

  60. sw.WriteLine(JsonUtility.ToJson(jsonList[i]));

  61. }

  62. sw.Close()

  63. }

  64. }

以上是在windows系统上,这上面读取和写入都很方便的 而且能直接查看,所以比较简单,移动平台就比较复杂了, 写的较完整,所以字数较多。

——————————————————————————————————————————————

先说明以几个Unity特殊文件夹的作用。这边只提和本章有关的几个,其他的可以在百度了解,比如Editor Plugins。

Reousrces    (搭配API  :  Application.datapath +"/resources/***/***")

刚刚上文用到了Resources文件夹下。

这个文件夹里的所有资源(不管用不用)都会被build打包进apk或其他包里。它的这个文件夹不管放在最外面,还是在其他文件夹下面都会被识别到。 可以清理一些没有用的资源,减少包的大小。

Windows平台可以用这个包进行读写,有个方法是 Resources.Load() 无论在编辑还是运行都可以用,可以加载其中的资源图片 或者模型,  也可以搭配asset bundle用,这个方法很实用 这里不细说。

 

StreamingAssets   (搭配 Application.streamingAssetsPath + "/")

这个文件夹里的所有资源也一样会被build打包进去。但是它不会压缩,资源会占空间。而且这个文件夹最特殊一点是,它只支持读,不支持写。而且在不同平台上它的路径不一样,官方手册可查。

——Mac or Windows

path = Application.dataPath + "/StreamingAssets";

——IOS

 path = Application.dataPath + "/Raw";

——Android  (注意 )

 path = "jar:file://" + Application.dataPath + "!/assets/";

使用以上路径对应平台 ,就能读到这个文件夹里面的东西。

注意,该文件夹 在编译到安卓平台上时,会将资源 压缩成一个压缩包,这种时候就不能使用StreamReader读了,只能用WWW读

 

 

还剩下最后一个路径  Application.persistentDataPath   它没有对应的文件夹,应该说默认是不存在的。

这个路径比较特殊,它是只有在程序包安装完毕后,才会创建出来的。所以这个路径下面的文件,只能通过代码 在运行时创建,但是它是支持 可读可写的  还是很方便的 常用来做存档。

它在不同平台的路径:

——Mac

Path =  /Users/xxxx/Library/Caches/CompanyName/Product Name

——Windows

Path = C:/Users/xxxx/AppData/LocalLow/CompanyName/ProductName

——IOS

Path = Application/xxxxxxxx/Documents

——Android

Path =  /data/data/xxx.xxx.xxx/files   或者是   /Android/data/com.company.xxx/files

注意: 这些路径 最后都是没有带斜杠的,需要自己带上斜杠。

 

介绍完这些路径后,我们就开始写安卓平台的示例:

 

 
  1. using System.Collections;

  2. using System.Collections.Generic;

  3. using UnityEngine;

  4.  
  5. //额外引用

  6. using System.IO;

  7. using System.Text;

  8. using System;

  9.  
  10. public class blogtest : MonoBehaviour {

  11.  
  12. //一个测试类

  13. class testJson

  14. {

  15. public string Name;

  16.  
  17. public testJson(string m_name)

  18. {

  19. Name = m_name;

  20. }

  21. }

  22.  
  23. List jsonList = new List();

  24.  
  25. void Start()

  26. {

  27. //现在是用Windows系统环境开发的安卓平台使用WWW读取本地文件 (如果是Mac或者Liunx的开发环境,可能不太一样)

  28. //因为StreamingAssets文件夹会编译成压缩包,不能用StreamReader 只能用WWW读写。

  29. //但是StreamingAssets有限制只能读 不能写。

  30. //所以这里的设计模式是,先把提前写好的静态json文本文件放在StreamingAssets里,用WWW读取后转化成列表,在程序运行时计算。

  31. //在程序最后结算时,写入到另一个路径里, 也就是Application.persistentDataPath 这个路径是只有程序安装后才自动生成的,而且之后会一直存在,只要不删除程序,就可以一直存放在这里。

  32.  
  33.  
  34.  
  35. //首先程序运行时,先判断Application.persistentDataPath该路径下 有没有我们需要的json文件。如果没有就创建。

  36. //声明一个文件信息,包含它的路径。

  37. FileInfo m_file = new FileInfo(Application.persistentDataPath + "/newjson.txt");

  38.  
  39.  
  40.  
  41. //判断 Exists 会返回是否存在

  42. if (!m_file.Exists)

  43. {

  44. //这里是表示不存在 先创建出这个文件

  45. StreamWriter SWfile = m_file.CreateText();

  46.  
  47. //因为第一次运行程序是肯定不存在这个文件的,会进到这里 需要去读取 StreamingAssets 里的oldjson.txt

  48. //WWW的用法格式 (感觉没有StreamReader好用)

  49. //StreamingAssets的路径格式 建议用下面这个

  50. string url = "jar:file://" + Application.dataPath + "!/assets/" + "oldjson.txt";

  51. //关于这个SkipBom的意义 我放到后面博文里做详解,代码里先照写 这个是巨坑!!

  52. string SkipBom;

  53.  
  54. string[] jsondata;

  55.  
  56. //WWW 默认是把文件信息从头到尾全部读取出来,放在下面这个变量里。我们需要对它进行,换行操作 以及把 每一行存在一个数组里。

  57. //WWW 里存放的是一些字节信息 八进制或 十六进制。看文件编码格式

  58. WWW wread = new WWW(url);

  59. //这一句意思是,得到一个UTF8编码的字符串 ,从wread的字节信息里去读,但是跳过前三位字节,从第四位字节去读。 为什么是三位字节,后文解释。

  60. SkipBom = Encoding.UTF8.GetString(wread.bytes, 3, wread.bytes.Length - 3);

  61. //将上面得到的字符串,因为包含了全部json信息,要做一个Split换行,自动转换成数组。 这里有一个换行格式符 是C#里预定义的, {"\r\n"}

  62. jsondata = SkipBom.Split(new string[] { "\r\n" }, StringSplitOptions.None);

  63.  
  64. //到这里的操作后 就和StreamReader的思路一样了。 使用遍历得到每行的json信息。

  65. foreach (string item in jsondata)

  66. {

  67. jsonList.Add(JsonUtility.FromJson(item));

  68. }

  69.  
  70. }

  71. else

  72. {

  73. //这里表示json文件已存在 第二次启动程序的时候会进这里。

  74. //这时候就可以直接访问persistentDataPath的路径 用StreamReader直接读就好了。

  75.  
  76. StreamReader sr = new StreamReader(Application.persistentDataPath + "/newjson.txt");

  77. string nextLine;

  78. while ((nextLine = sr.ReadLine()) != null)

  79. {

  80. jsonList.Add(JsonUtility.FromJson(nextLine));

  81. }

  82.  
  83. sr.Close();

  84. }

  85.  
  86.  
  87. //接着就是程序运行最后结算的时候写入。 偷懒都写在一个函数里了。

  88. //后面的步骤 就都差不多了, 只要注意路径是对的就好了。

  89.  
  90. StreamWriter sw = new StreamWriter(Application.persistentDataPath + "/newjson.txt",false,Encoding.UTF8);

  91. for (int i = 0; i < jsonList.Count; i++)

  92. {

  93. sw.WriteLine(JsonUtility.ToJson(jsonList[i]));

  94. }

  95.  
  96. sw.Close();

  97.  
  98. }

  99.  
  100. }

注意1:在Windows和Windows Store应用程序上使用文件协议访问本地文件时,必须指定file:///(带有三个斜杠)。

举例   string url = “file:///” + Application.streamingAssetsPath + "xxx.txt";

 

注意2:这里讲解SkipBom的意义, 还有被跳过的三个字节。

在Windows环境上,我们一般制作json文件,用notepad++ 或者其他操作 新建一个txt的文本文件。

但是Windows的系统,通常情况下会给txt文件默认开头打上一个Bom字符,Bom字节用记事本是看不到的,它其实是三个 十六进制的字符,如果UTF—16格式通过某些操作会读取到, 它的作用是用来表示这个文件的编码信息, 如果是UTF8编码,就会通过Bom字符去表示,是一种很隐含的信息。

以下引用网上找到的解释:

BOM是“Byte Order Mark”的缩写,用于标记文件的编码。并不是所有的文本编辑工具都能识别BOM标记.在用记事本之类的程序将文本文件保存为UTF-8格式时,记事本会 在文件头前面加上几个不可见的字符(EF BB BF),就是所谓的BOM(Byte Order Mark)。
1) notepad : 可以自动识别出没有带 bom 的 utf-8 编码格式文件,但不可以控制保存文件时是否添加 bom , 如果保存文件,那么会统一添加 bom 。
2)editplus : 不能自动识别出没有 bom 的 utf-8 编码格式文件,保存文件为 utf-8 时会自动添加 bom
3) UltraEdit : 对于字符编码的功能最为强大, 可以自动识别带 bom 和不带 bom 的 utf-8 文件 (可以配置) ; 保存的时候可以通过配置选择是否添加 bom.

一开始没注意到这个问题的时候,我用WWW方法读取到字符串信息,死活转不成json,一直报错说这个json是无用的值。但是我打印出来的 文本信息, 是正确的,格式也正确,  而且和StreamReader读到的对比一下 是一摸一样的。也根本看不到Bom字节,前面也没有空格。但实际它就是存在的,而且Unity是可以识别到的,所以当你的json信息的第一个字符不是“{”中括号时,程序当然是会报错的,没有按照json语句的格式。这个真的坑了我很久,不断的百度,打开了十几个页面,查找了很久,很少有人遇到这个问题大概,没有人写,最后还是被我找到了一篇,来Unity官方 全球的讨论社区, 还是14年12月很早的。但是幸好解决了。

——————————————————————————————————————————

以上就是本节教程的全部内容,用了一个美好的周日,打了很久写完了这篇,我好像话太多了,真的说了很多。

如果有问题,望各位路过大牛一定要指明。说的不对的地方,请帮我挑出来。

还过还有不明白的,可以加我QQ : 593902339  欢迎交流

 

统一感谢以下技术帖子:

http://blog.csdn.net/musicvs/article/details/49657905 [笨木头Unity3D]杂记003·Unity在Android中读取文件

http://blog.csdn.net/zhaoguanghui2012/article/details/49871775  Unity中C# 文件本地读取,本地保存等实例

https://www.jianshu.com/p/31a41b8f1332 Unity工程 打包到Android平台文件读取路径

http://www.xuanyusong.com/archives/3229   雨松MOMO 2014年09月26日 于 雨松MOMO程序研究院 发表

 

转载请帮我注明,谢谢!

你可能感兴趣的:(C#)