Unity 關於安卓和各平台讀寫本地json文件,WWW讀取本地文件,Unity各路徑API目前較完整的詳解
關鍵點:
···C# API (StreamReader StreamWriter System.Text.EncodingString.Split)
···Unity API ( JsonUtility.fromjson jsonUtility.tojson WWW )
··UnityAPI 提供的路徑參考(常用的):
Application.dataPath
Application.streamingAssetsPath
Application.persistentDataPath
··· txt格式的json文本文件 , 編碼(UTF-8UnicodeAscii), Bom字元問題。
____________________________________________________________________________________________
研究背景: 因自己手頭正好有一個安卓上的遊戲應用,需要讀取和寫入本地的json文件,例如武器信息/人物屬性/得分結算,所以研究了一下。雖然這一塊網上的教程都挺多的,但是沒找到特別詳細和完整,而且針對不同平台會出現不同問題,所以自己研究了很久,也是挖了不少的坑,最後才摸的比較清楚這一塊了,跑來寫這一篇技術文檔。
用的是windows的系統,以及安卓真機,親測可用。Mac Liunx 和 IOS 還沒有測試過,但是照著這個思路做是一樣的。
說明一下,該教程沒有用到任何插件,包括 讀寫json的用的也是Unity自帶的API。 沒有使用jsonLit。 一樣很方便好用。
JsonUtility.fromjson<T>(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更詳細的信息,推薦翻 官方手冊。 針對不同平台,還會有些 小變化和問題,要注意。
以下示例:
using System.Collections;using System.Collections.Generic;using UnityEngine;//額外引用using System.IO;using System.text;public class blogtest : MonoBehaviour { //一個測試類 class testJson { public string Name; public testJson(string m_name) { Name = m_name; } } List<testJson> jsonList = new List<testJson>(); void Awake() { //Editor模式 Window系統 //先用C#的API做讀寫,需要引入 using system.IO的命名空間。 //該路徑是在Project視圖下,Asset下的Resources文件夾里。可以往下再寫路徑。 //這個文件是預先創建的,往裡面寫幾行json信息或其他文本信息。 StreamReader sr = new StreamReader(Application.dataPath + "/Resou/json1.txt"); //臨時存儲json信息 string jsonInfo; //可以看下包含的一些常用方法 //包含文件從頭到尾的信息,存在一個字元串里。 jsonInfo = sr.ReadToEnd(); //讀取每一行的文件信息。 jsonInfo = sr.ReadLine(); //寫一個循環體 列印一下。 while((jsonInfo = sr.ReadLine())!=null) { //按順序列印出每行的文本信息 會包含「{ }」json的中括弧字元 //這裡要注意一個編碼問題,如果文本信息是全英文的就沒事,如果有包含中文的要使用UTF-8的編碼,否則就出現亂碼. Debug.Log(jsonInfo); //如果json信息里包含的是構造函數的參數,就寫在這裡。 jsonList.Add(JsonUtility.FromJson<testJson>(jsonInfo)); } //讀完後關閉。 sr.Close(); //接著是寫入操作 格式是差不多的。寫入通常就是創建,就算沒有這個文件名,也會自動創建。 //這裡要注意指定編碼格式 建議用UTF8支持中文,需要 using System.Text; StreamWriter sw = new StreamWriter(Application.dataPath + "/Resources/json2.txt",false,Encoding.UTF8); //用之前存儲的testjson類列表信息 for (int i = 0; i < jsonList.Count; i++) { //將列表中的每一行信息,轉換成json格式然後寫入文件。 sw.WriteLine(JsonUtility.ToJson(jsonList[i])); } sw.Close() }}
以上是在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/http://xxx.xxx.xxx/files 或者是 /Android/data/http://com.company.xxx/files
注意: 這些路徑 最後都是沒有帶斜杠的,需要自己帶上斜杠。
介紹完這些路徑後,我們就開始寫安卓平台的示例:
using System.Collections;using System.Collections.Generic;using UnityEngine;//額外引用using System.IO;using System.Text;using System;public class blogtest : MonoBehaviour { //一個測試類 class testJson { public string Name; public testJson(string m_name) { Name = m_name; } } List<testJson> jsonList = new List<testJson>(); void Start() { //現在是用Windows系統環境開發的安卓平台使用WWW讀取本地文件 (如果是Mac或者Liunx的開發環境,可能不太一樣) //因為StreamingAssets文件夾會編譯成壓縮包,不能用StreamReader 只能用WWW讀寫。 //但是StreamingAssets有限制只能讀 不能寫。 //所以這裡的設計模式是,先把提前寫好的靜態json文本文件放在StreamingAssets里,用WWW讀取後轉化成列表,在程序運行時計算。 //在程序最後結算時,寫入到另一個路徑里, 也就是Application.persistentDataPath 這個路徑是只有程序安裝後才自動生成的,而且之後會一直存在,只要不刪除程序,就可以一直存放在這裡。 //首先程序運行時,先判斷Application.persistentDataPath該路徑下 有沒有我們需要的json文件。如果沒有就創建。 //聲明一個文件信息,包含它的路徑。 FileInfo m_file = new FileInfo(Application.persistentDataPath + "/newjson.txt"); //判斷 Exists 會返回是否存在 if (!m_file.Exists) { //這裡是表示不存在 先創建出這個文件 StreamWriter SWfile = m_file.CreateText(); //因為第一次運行程序是肯定不存在這個文件的,會進到這裡 需要去讀取 StreamingAssets 里的oldjson.txt //WWW的用法格式 (感覺沒有StreamReader好用) //StreamingAssets的路徑格式 建議用下面這個 string url = "jar:file://" + Application.dataPath + "!/assets/" + "oldjson.txt"; //關於這個SkipBom的意義 我放到後面博文里做詳解,代碼里先照寫 這個是巨坑!! string SkipBom; string[] jsondata; //WWW 默認是把文件信息從頭到尾全部讀取出來,放在下面這個變數里。我們需要對它進行,換行操作 以及把 每一行存在一個數組裡。 //WWW 里存放的是一些位元組信息 八進位或 十六進位。看文件編碼格式 WWW wread = new WWW(url); //這一句意思是,得到一個UTF8編碼的字元串 ,從wread的位元組信息里去讀,但是跳過前三位位元組,從第四位位元組去讀。 為什麼是三位位元組,後文解釋。 SkipBom = Encoding.UTF8.GetString(wread.bytes, 3, wread.bytes.Length - 3); //將上面得到的字元串,因為包含了全部json信息,要做一個Split換行,自動轉換成數組。 這裡有一個換行格式符 是C#里預定義的, {"
"} jsondata = SkipBom.Split(new string[] { "
" }, StringSplitOptions.None); //到這裡的操作後 就和StreamReader的思路一樣了。 使用遍歷得到每行的json信息。 foreach (string item in jsondata) { jsonList.Add(JsonUtility.FromJson<testJson>(item)); } } else { //這裡表示json文件已存在 第二次啟動程序的時候會進這裡。 //這時候就可以直接訪問persistentDataPath的路徑 用StreamReader直接讀就好了。 StreamReader sr = new StreamReader(Application.persistentDataPath + "/newjson.txt"); string nextLine; while ((nextLine = sr.ReadLine()) != null) { jsonList.Add(JsonUtility.FromJson<testJson>(nextLine)); } sr.Close(); } //接著就是程序運行最後結算的時候寫入。 偷懶都寫在一個函數里了。 //後面的步驟 就都差不多了, 只要注意路徑是對的就好了。 StreamWriter sw = new StreamWriter(Application.persistentDataPath + "/newjson.txt",false,Encoding.UTF8); for (int i = 0; i < jsonList.Count; i++) { sw.WriteLine(JsonUtility.ToJson(jsonList[i])); } sw.Close(); } }
注意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 時會自動添加 bom3) 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程序研究院 發表
轉載請幫我註明,謝謝!
推薦閱讀:
※Weex SDK Android 源碼解析
※OnePlus 一加3T:距離不「將就」,還要更「講究」一點 | 肉身評測
※如何學習 Android 應用開發?
※優化 listview 有哪些方法?
※智能手機操作系統的簡短現狀總結與未來趨勢的個人觀點