小伙伴解析Json解的66的,但是老板甩给他了一个txt,那么就看如何用Unity解析txt文档

2022-08-07 09:02:40 浏览数 (1)

一、前言

小伙伴跟我说,自从看了我这篇【Unity3D日常】Unity写Json数据,以及在Unity读取、解析Json数据,潜心学习,解析Json那解析的66的,没啥压力,但是领导给了他一个txt不知道怎么解析。

我说,那你把文档发给我,我整理一篇文章,让小伙们下次遇到这个问题知道总体流程了。

二、需求分析

需求

要从文档中读取所有的地铁线路和每条线路的站点,然后可以点击不同的线路切换显示不同的站点。

流程步骤

数据文档→读取数据→解析数据→保存数据

分析

(1)要有线路和站点的对应关系数据文档,这个已经有了:

(2)读取数据 读取数据没啥好说的,无论是json文件还是xml文件还是txt文件 使用File FileStream SteamReader StreamWriter 读取文件 都可以 每种写法都能百度出来

(3)解析数据 解析数据,方式也很多,以JSON为类,可以使用插件,也可以使用Unity自带的JSON解析类

插件:LitJSON解析、Newtonsoft.Json.dll

Unity自带解析JSON的类:JsonUtility

用那个都可以。

如果是txt数据,那么就要分析每一行的数据,如何切分,然后进行保存,比如:

代码语言:javascript复制
1	1号线	103	苹果园		
1	1号线	104	古城		
1	1号线	105	八角游乐园		
1	1号线	106	八宝山		
1	1号线	107	玉泉路		
1	1号线	108	五棵松		
1	1号线	109	万寿路		
1	1号线	110	公主坟		
1	1号线	111	军事博物馆		
1	1号线	112	木樨地		
1	1号线	113	南礼士路	

这段数据,最后是rn表示跳到下一行,中间以空格切分也就是t

那么就可以先用rn切分每一行,然后用t切换每一列,这样就将数据分析出来了。

(4)保存数据

最后一步,是保存,JSON解析完之后,需要保存到特定的跟JSON解析后匹配的类中,比如:

代码语言:javascript复制
{
    "name":"小明",
    "age":20,
    "interests":["sing","run"]
}

那么,解析的类也要有这几个值,而且要一一对应,不能有错,包括字母大小写:

代码语言:javascript复制
[System.Serializable]
public class ModelTest
{
    public string name;
    public int age;
    public string[] interests;
}

那么,接收到的JSON数据怕生成类的时候出错怎么办,这时候就可以使用JSON数据转实体的功能: https://www.json.cn/json/json2csharp.html

一键生成实体类,妈妈再也不用担心我出错了。

然后,用这个实体去接收解析好的JSON数据就可以了:

代码语言:javascript复制
 ModelTest obj = JsonUtility.FromJson<ModelTest>(jsonTest);

jsonTest是JSON数据,ModelTest就是跟JSON数据匹配的实体。

那如果是txt一行一行读取的数据呢,比如:

代码语言:javascript复制
1	1号线	103	苹果园		
1	1号线	104	古城		
1	1号线	105	八角游乐园		
1	1号线	106	八宝山		
1	1号线	107	玉泉路		
1	1号线	108	五棵松		
1	1号线	109	万寿路		
1	1号线	110	公主坟		
1	1号线	111	军事博物馆		
1	1号线	112	木樨地		
1	1号线	113	南礼士路	

这个,就需要分析结构,然后使用数组或者List或者字典去保存了。

这个是一条地铁线,包含多个站点,每个站点有多个属性,可以使用自定义类,然后使用List去接收这个数据,比如:

代码语言:javascript复制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ReadData : MonoBehaviour
{
    List<StationInfo> Stations;

    void Start()
    {
        
    }
}

public struct StationInfo
{
    public int lineid;          //线路编号
    public string linename;     //线路名称
    public string stationid;    //车站编号
    public string stationname;  //车站名称
}

三、实现

3-1 读取数据

代码语言:javascript复制
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class ReadData : MonoBehaviour
{
    List<StationInfo> Stations;
    string Content;

    void Start()
    {
        //读取数据
        ReadFileData();
    }

    void ReadFileData()
    {
        string pathstations = Application.streamingAssetsPath   "/Stations_2018.txt";
        using (StreamReader SR = new StreamReader(pathstations))
        {
            //将所有的数据从文件以流形式保存到 Content
            Content = SR.ReadToEnd();
            SR.Close();
            SR.Dispose();
        }
    }
}

public struct StationInfo
{
    public int lineid;          //线路编号
    public string linename;     //线路名称
    public string stationid;    //车站编号
    public string stationname;  //车站名称
}

3-2 解析数据

代码语言:javascript复制
    //解析数据 
    List<StationInfo> ParseStations()
    {
        if (!string.IsNullOrEmpty(Content))
        {
            List<StationInfo> info = new List<StationInfo>();
            string[] strarray = Content.Split(new char[] { 'r', 'n' }, System.StringSplitOptions.RemoveEmptyEntries);
            for (int i = 1; i < strarray.Length; i  )
            {
                string[] Columns = strarray[i].Split(new char[] { 't' }, System.StringSplitOptions.RemoveEmptyEntries);
                StationInfo stationInfo = new StationInfo();
                stationInfo.lineid = int.Parse(Columns[0]);
                stationInfo.linename = Columns[1];
                stationInfo.stationid = Columns[2];
                stationInfo.stationname = Columns[3];
                info.Add(stationInfo);
            }
            return info;
        }
        return null;
    }

3-3 保存数据

代码语言:javascript复制
//保存数据
Stations = ParseStations();//线路编号	线路名称	车站编号	车站名称

完整代码:

代码语言:javascript复制
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class ReadData : MonoBehaviour
{
    List<StationInfo> Stations;
    string Content;

    void Start()
    {
        //读取数据
        ReadFileData();
    }

    void ReadFileData()
    {
        string pathstations = Application.streamingAssetsPath   "/Stations_2018.txt";
        using (StreamReader SR = new StreamReader(pathstations))
        {
            //将所有的数据从文件以流形式保存到 Content
            Content = SR.ReadToEnd();
            SR.Close();
            SR.Dispose();
            //保存数据
            Stations = ParseStations();//线路编号	线路名称	车站编号	车站名称
        }
    }

    //解析数据 
    List<StationInfo> ParseStations()
    {
        if (!string.IsNullOrEmpty(Content))
        {
            List<StationInfo> info = new List<StationInfo>();
            string[] strarray = Content.Split(new char[] { 'r', 'n' }, System.StringSplitOptions.RemoveEmptyEntries);
            for (int i = 1; i < strarray.Length; i  )
            {
                string[] Columns = strarray[i].Split(new char[] { 't' }, System.StringSplitOptions.RemoveEmptyEntries);
                StationInfo stationInfo = new StationInfo();
                stationInfo.lineid = int.Parse(Columns[0]);
                stationInfo.linename = Columns[1];
                stationInfo.stationid = Columns[2];
                stationInfo.stationname = Columns[3];
                info.Add(stationInfo);
            }
            return info;
        }
        return null;
    }
}

public struct StationInfo
{
    public int lineid;          //线路编号
    public string linename;     //线路名称
    public string stationid;    //车站编号
    public string stationname;  //车站名称
}

3-4 生成地铁线

先摆个UI:

将这两个按钮分别做成预制体,命名为MetroLinePar、MetroSitePar,放到Resources文件夹下面:

编写代码:

代码语言:javascript复制
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class ReadData : MonoBehaviour
{
    List<StationInfo> Stations;
    string Content;
    public GameObject MetroLinePanel;
    private GameObject MetroLinePar;
    public GameObject MetroSitePanel;
    private GameObject MetroSitePar;

    void Start()
    {
        //读取数据
        ReadFileData();

        MetroLinePar = Resources.Load<GameObject>("MetroLinePar");
        MetroLinePar = Resources.Load<GameObject>("MetroSitePar");

        //生成UI
        CreateUI();
    }
    void CreateUI()
    {
        //找出所有地铁线
        Dictionary<int, string> MetroLine = new Dictionary<int, string>();
        for (int i = 0; i < Stations.Count; i  )
        {
            if (!MetroLine.ContainsKey(Stations[i].lineid))
            {
                MetroLine.Add(Stations[i].lineid, Stations[i].linename);
            }
        }
        //生成地铁线
        foreach (var item in MetroLine)
        {
            GameObject MetroLineItem = Instantiate(MetroLinePar, Vector3.zero, Quaternion.identity, MetroLinePanel.transform);
            MetroLineItem.GetComponentInChildren<Text>().text = item.Value;
        }
    }

    void ReadFileData()
    {
        string pathstations = Application.streamingAssetsPath   "/Stations_2018.txt";
        using (StreamReader SR = new StreamReader(pathstations))
        {
            //将所有的数据从文件以流形式保存到 Content
            Content = SR.ReadToEnd();
            SR.Close();
            SR.Dispose();
            //保存数据
            Stations = ParseStations();//线路编号	线路名称	车站编号	车站名称
        }
    }


    //解析数据 
    List<StationInfo> ParseStations()
    {
        if (!string.IsNullOrEmpty(Content))
        {
            List<StationInfo> info = new List<StationInfo>();
            string[] strarray = Content.Split(new char[] { 'r', 'n' }, System.StringSplitOptions.RemoveEmptyEntries);
            for (int i = 1; i < strarray.Length; i  )
            {
                string[] Columns = strarray[i].Split(new char[] { 't' }, System.StringSplitOptions.RemoveEmptyEntries);
                StationInfo stationInfo = new StationInfo();
                stationInfo.lineid = int.Parse(Columns[0]);
                stationInfo.linename = Columns[1];
                stationInfo.stationid = Columns[2];
                stationInfo.stationname = Columns[3];
                info.Add(stationInfo);
            }
            return info;
        }
        return null;
    }
}

public struct StationInfo
{
    public int lineid;          //线路编号
    public string linename;     //线路名称
    public string stationid;    //车站编号
    public string stationname;  //车站名称
}

运行:

所有的地铁线有,下面就需要将每条地铁线的每个站点对应起来。

3-5 生成站点

代码语言:javascript复制
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class ReadData : MonoBehaviour
{
    List<StationInfo> Stations;
    string Content;
    public GameObject MetroLinePanel;
    private GameObject MetroLinePar;
    public GameObject MetroSitePanel;
    private GameObject MetroSitePar;

    void Start()
    {
        //读取数据
        ReadFileData();

        MetroLinePar = Resources.Load<GameObject>("MetroLinePar");
        MetroSitePar = Resources.Load<GameObject>("MetroSitePar");

        //生成UI
        CreateMetroLine();
    }
    void CreateMetroLine()
    {
        //找出所有地铁线
        Dictionary<int, string> MetroLine = new Dictionary<int, string>();
        for (int i = 0; i < Stations.Count; i  )
        {
            if (!MetroLine.ContainsKey(Stations[i].lineid))
            {
                MetroLine.Add(Stations[i].lineid, Stations[i].linename);
            }
        }
        //生成地铁线
        foreach (var item in MetroLine)
        {
            GameObject MetroLineItem = Instantiate(MetroLinePar, Vector3.zero, Quaternion.identity, MetroLinePanel.transform);
            MetroLineItem.GetComponentInChildren<Text>().text = item.Value;
            //生成站点
            MetroLineItem.GetComponent<Button>().onClick.AddListener(()=> CreateMetroSite(item.Key));
        }
    }

    void CreateMetroSite(int _MetroLine)
    {
        //找到传递过来的地铁线包含的所有站点
        List<StationInfo> MetroSite = new List<StationInfo>();
        for (int i = 0; i < Stations.Count; i  )
        {
            if (Stations[i].lineid == _MetroLine)
            {
                MetroSite.Add(Stations[i]);
            }
        }
        //清理原来的站点
        foreach (Transform transform in MetroSitePanel.transform)
        {
            Destroy(transform.gameObject);
        }
        //生成站点
        foreach (var item in MetroSite)
        {
            GameObject MetroLineSiteItem = Instantiate(MetroSitePar, Vector3.zero, Quaternion.identity, MetroSitePanel.transform);
            MetroLineSiteItem.GetComponentInChildren<Text>().text = item.stationname;
        }
    }

    void ReadFileData()
    {
        string pathstations = Application.streamingAssetsPath   "/Stations_2018.txt";
        using (StreamReader SR = new StreamReader(pathstations))
        {
            //将所有的数据从文件以流形式保存到 Content
            Content = SR.ReadToEnd();
            SR.Close();
            SR.Dispose();
            //保存数据
            Stations = ParseStations();//线路编号	线路名称	车站编号	车站名称
        }
    }


    //解析数据 
    List<StationInfo> ParseStations()
    {
        if (!string.IsNullOrEmpty(Content))
        {
            List<StationInfo> info = new List<StationInfo>();
            string[] strarray = Content.Split(new char[] { 'r', 'n' }, System.StringSplitOptions.RemoveEmptyEntries);
            for (int i = 1; i < strarray.Length; i  )
            {
                string[] Columns = strarray[i].Split(new char[] { 't' }, System.StringSplitOptions.RemoveEmptyEntries);
                StationInfo stationInfo = new StationInfo();
                stationInfo.lineid = int.Parse(Columns[0]);
                stationInfo.linename = Columns[1];
                stationInfo.stationid = Columns[2];
                stationInfo.stationname = Columns[3];
                info.Add(stationInfo);
            }
            return info;
        }
        return null;
    }
}

public struct StationInfo
{
    public int lineid;          //线路编号
    public string linename;     //线路名称
    public string stationid;    //车站编号
    public string stationname;  //车站名称
}

效果如图:

有两个小问题: 1)1号线显示两次,这是因为场景中的预制体没有隐藏

2)在生成的时候,让程序默认显示1号线的站点,这样合理一些。

代码语言:javascript复制
void Start()
    {
        //读取数据
        ReadFileData();

        MetroLinePar = Resources.Load<GameObject>("MetroLinePar");
        MetroSitePar = Resources.Load<GameObject>("MetroSitePar");

        //生成UI
        CreateMetroLine();

        //默认生成一号线的站点
        CreateMetroSite(1);
    }

效果图:

好,结束了。

四、后言

这个数据读取显示的整个步骤就完成了,万变不离其宗:

数据文档→读取数据→解析数据→保存数据→使用数据

明白了这几个步骤,剩下的难点就在于分析结构,然后如何保存数据,才能让自己去使用数据的时候方便一些。

比如,我想要保存所有的地铁线和地铁线的信息,那么就可以新建一个自定义类跟数据对应,然后使用List去保存数据,这样在后面的生成地铁线和地铁站点的时候就方便了许多。

这个实例演示的是如何从txt中读取数据,但是其他比如JSON、XML也是适用的,只是解析数据、保存数据的时候略微有些差别。

还有一点,就是List、字典、Resources、 StreamingAssets、Instantiate实例化,这些基本知识的运行要熟练。

0 人点赞