[Unity3D·CSV篇]02.CSV高手級讀取
阿新 • • 發佈:2019-02-12
在上一篇中,儲存CSV檔案每一行的資料為CSVDemo物件時,總是要用if條件判斷當前是哪個欄位,然後再給物件賦值。這樣太煩人了,要是在開發的過程中不斷地有新的想法,不斷地新增修改欄位,那真是改死人了。甚至完全失去了改動的興趣,於是遊戲的創意就大打折扣,後果十分嚴重,嗯嗯。(旁白:說了這麼多,接下來就要宣傳你的“產品”了吧?)不過,大家不用怕,有木頭在,木頭有個好招,大家看招吧。
1. 臨時的字典結構
為了以後更方便,我們在搭建程式碼的時候,通常都會麻煩一點,但是,一勞永逸的事情,我是不會抗拒的。為了實現我們的目的,我們需要先用一個字典結構把CSV檔案儲存起來,如下程式碼:
- /// <summary>
- /// 讀取CSV檔案
- /// 結果儲存到字典集合,以ID作為Key值,對應每一行的資料,每一行的資料也用字典集合儲存。
- /// </summary>
- /// <param name="filePath"></param>
- /// <returns></returns>
- public static Dictionary<string, Dictionary<string, string>> LoadCsvFile(string filePath)
- {
- Dictionary<string
- string[] fileData = File.ReadAllLines(filePath);
- /* CSV檔案的第一行為Key欄位,第二行開始是資料。第一個欄位一定是ID。 */
- string[] keys = fileData[0].Split(',');
- for (int i = 1; i < fileData.
- {
- string[] line = fileData[i].Split(',');
- /* 以ID為key值,建立一個新的集合,用於儲存當前行的資料 */
- string ID = line[0];
- result[ID] = new Dictionary<string, string>();
- for (int j = 0; j < line.Length; j++)
- {
- /* 每一行的資料儲存規則:Key欄位-Value值 */
- result[ID][keys[j]] = line[j];
- }
- }
- return result;
- }
2. 強大的反射
你是否曾經有這樣的感覺,那些看起來明明一樣的程式碼,僅僅只是型別不一樣;那些看起來明明是一樣的程式碼,僅僅只是呼叫的屬性不一樣。總之,那些看起來明明不應該那麼重複的程式碼,它卻總是重複了。大多數開發者都會有這樣的感覺,於是,逆天的反射和泛型出現在我們的面前。沒錯,木頭要用的就是反射+泛型這個強力的組合。看看優化後的程式碼:- /* 把CSV檔案按行存放,每一行的ID作為key值,內容作為value值 */
- Dictionary<int, CSVDemo> csvDataDic = new Dictionary<int, CSVDemo>();
- /* CSV檔案路徑 */
- string filePath = Application.streamingAssetsPath + "/CSVDemo.csv";
- /* 從CSV檔案讀取資料 */
- Dictionary<string, Dictionary<string, string>> datasDic = LoadCsvFile(filePath);
- /* 遍歷每一行資料 */
- foreach (string ID in datasDic.Keys)
- {
- /* CSV的一行資料 */
- Dictionary<string, string> datas = datasDic[ID];
- /* 讀取Csv資料物件的屬性 */
- PropertyInfo[] props = typeof(CSVDemo).GetProperties();
- /* 使用反射,將CSV檔案的資料賦值給CSV資料物件的相應欄位,要求CSV檔案的欄位名和CSV資料物件的欄位名完全相同 */
- CSVDemo obj = new CSVDemo();
- foreach (PropertyInfo pi in props)
- {
- pi.SetValue(obj, Convert.ChangeType(datas[pi.Name], pi.PropertyType), null);
- }
- /* 按ID-資料的形式儲存 */
- csvDataDic[obj.ID] = obj;
- }
- /* 測試讀取ID為1的資料 */
- CSVDemo csvDemo1 = csvDataDic[1];
- Debug.Log("ID=" + csvDemo1.ID + ",Name=" + csvDemo1.Name);
3. 泛型呢?
大家一定還有疑問,每個CSV檔案都要寫這樣一段程式碼來讀取嗎?不,這種重複的事情木頭是絕對不會做的,我一做重複的事情就會頭暈,於是,泛型救了我。最終完美的讀取並儲存CSV檔案的函式是這樣的:- /// <summary>
- /// 讀取CSV檔案資料(利用反射)
- /// </summary>
- /// <typeparam name="CsvData">CSV資料物件的型別</typeparam>
- /// <param name="csvFilePath">CSV檔案路徑</param>
- /// <param name="csvDatas">用於快取資料的字典</param>
- /// <returns>CSV檔案所有行內容的資料物件</returns>
- private Dictionary<int, T_CsvData> LoadCsvData<T_CsvData>(string csvFilePath)
- {
- Dictionary<int, T_CsvData> dic = new Dictionary<int, T_CsvData>();
- /* 從CSV檔案讀取資料 */
- Dictionary<string, Dictionary<string, string>> result = LoadCsvFile(csvFilePath);
- /* 遍歷每一行資料 */
- foreach (string ID in result.Keys)
- {
- /* CSV的一行資料 */
- Dictionary<string, string> datas = result[ID];
- /* 讀取Csv資料物件的屬性 */
- PropertyInfo[] props = typeof(T_CsvData).GetProperties();
- /* 使用反射,將CSV檔案的資料賦值給CSV資料物件的相應欄位,要求CSV檔案的欄位名和CSV資料物件的欄位名完全相同 */
- T_CsvData obj = Activator.CreateInstance<T_CsvData>();
- foreach (PropertyInfo pi in props)
- {
- pi.SetValue(obj, Convert.ChangeType(datas[pi.Name], pi.PropertyType), null);
- }
- /* 按ID-資料的形式儲存 */
- dic[Convert.ToInt32(ID)] = obj;
- }
- return dic;
- }
- /* 把CSV檔案按行存放,每一行的ID作為key值,內容作為value值 */
- Dictionary<int, CSVDemo> csvDataDic2 = LoadCsvData<CSVDemo>(filePath);
- /* 測試讀取ID為2的資料 */
- CSVDemo csvDemo2 = csvDataDic2[2];
- Debug.Log("ID=" + csvDemo2.ID + ",Name=" + csvDemo2.Name);