返回首页DA系统C#IDE文件同步服务屏保 今天是: 2026-05-05    "立夏"  夏季的第一个节气,表示盛夏时节的正式开始

搜索
热搜: linux 技术
Hi~登录注册
查看: 2288|回复: 0

[原创] 【原创】文件夹及文件处理基本讲解及案例演示

[复制链接]
发表于 2023-3-30 14:57:18 | 显示全部楼层 |阅读模式

少侠不来段修仙之旅吗~

您需要 登录 才可以下载或查看,没有帐号?注册成为修仙之旅的少年~

x

//File_Script.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class File_Script : MonoBehaviour
{
    /*
     *文件系统与IO
     *  文件系统对应类(需要加载的命名空间) System.IO
     *      DriveInfo:有关驱动器信息的类
     *          - 主要方法:GetDrives 用于取得本计算机中所有驱动器(C盘、D盘。。)信息对象
     *      File:有关对文件整体操作及内容读写的类
     *          整体操作:
     *              Create:创建文件
     *              Delete:删除文件
     *              Move:移动文件(剪切)
     *              Copy:复制文件
     *              Exists:检查设备中是否有该文件
     *              
     *          读写文件:
     *              ReadAllText:一次将文件中所有内容读入内存,并转为字符串(适用于文本文件)
     *              ReadAllLines:一次将文件中所有内容读入内存,转为字符串,将字符串按行形成一个字符串数组(适用于文本文件)
     *              ReadLines:并不会主动文件中的内容,只有再迭代时才读一行,每迭代一次向下读一行
     *              ReadAllBytes:将文件中的内容一次读入内存,以字节数组的方式保存。(适用于其他非文本文件)
     *              
     *              WriteAllText:一次写入大段文本(适用于文本文件)
     *              WriteAllLines:每写一行之后写一个换行符(适用于文本文件)(适合配置文件的读取)
     *              (配置文件都存放在工程文件夹下的StreamingAssets文件夹中,因为StreamingAssets文件夹在游戏发布后不会被压缩和加密,所以可以随时更改配置)
     *              
     *              WriteAllBytes:一次写入大量字节(适用于其他非文本文件)
     *              
     *       FileInfo:有关对文件整体操作及内容读写的类
     *          与FIle类不同的地方:比File类多一个Length属性,通过该属性可以了解一个文件所占有的字节
     *         
     *       Directory:有关对文件夹整体操作的类
     *          常用方法:
     *              CreateDirectory:创建文件夹
     *              Delete:删除文件夹
     *              Move:移动文件夹(不能跨驱动器移动)
     *              GetFile:取得文件夹下所有的文件名
     *              GetDirectoies:取得文件夹下所有的子文件夹目录名称
     *              Exists:检查某文件夹在设备中是否存在
     *              
     *       DirectoryInfo类功能同Directory类,只是所有成员为实例成员
     *      
     *       Path类,专用于对路径字符串的处理
     *          常用方法:
     *              GetFileName:取得文件名及扩展名
     *              GetFileNameWithOutExtension:取得文件名不要扩展名
     *              GetDirectoryName:取得路径中目录的名称
     *              Combine:将多个字符串合并成为一个路径
     *              
     *              
     *
     *
    */

    #region 文件夹的处理

    //注意:路径的字符串写法为前面要有个@,为: @"路径的绝对路径"
    //注意:路径如是windows系统,可以使用“\”和“/”这两种代表路径分割
    private string DirPath = @"F:\Unity-Pro\UnityPro-M2-Unity-Pro\data";

    private string DirPath1 = @"F:/Unity-Pro/UnityPro-M2-Unity-Pro/data";

    //最推荐还是使用这种,直接在项目根目录创建,这样就不会对其他系统文件造成影响!!
    private string DirPath2 = @"./data";

    //DirectoryInfo初始化的参数只能给静态参数
    private static string DirInfoPath = @"F:\Unity-Pro\UnityPro-M2-Unity-Pro\data";

    //DirectoryInfo初始化的参数只能给静态参数,初始化即提供路径
    DirectoryInfo DirInfo = new DirectoryInfo(DirInfoPath);



    private void OnGUI1()
    {
        if (GUILayout.Button("创建项目data文件夹"))
        {
            //使用Directory类需要引用System.IO类才能正常使用!
            //先判断是否文件夹是否已存在
            if (Directory.Exists(DirPath2))
            {
                print("已经存在目录啦,不要再次创建啦~");
            }
            else
            {
                //创建文件夹
                Directory.CreateDirectory(DirPath2);

                //使用DirectoryInfo类的实例创建目录,删除文件夹也是同样的用法,判断文件夹是个属性不是方法,所以后面不需要()!!
                //DirInfo.Create();

                print("创建成功,目录为:" + DirPath2);
            }


        }
        if (GUILayout.Button("删除项目data文件夹"))
        {
            //先判断是否文件夹是否已存在,有则删除文件夹
            if (Directory.Exists(DirPath2))
            {
                //删除文件夹
                //注意Directory.Delete有两个重载,第一个是只有字符串的,第二个是有布尔值的
                //只能删除路径的文件夹本身,如果这个文件夹还有嵌套子文件夹,则会报错无法删除
                Directory.Delete(DirPath2);

                //可以删除包括子文件夹的重载
                //Directory.Delete(DirPath,true);

                print(DirPath2 +" 目录删除成功");
            }
            else
            {
                print("无法删除文件夹,原因为没有那个目录: " + DirPath2);
            }
        }
    }

    #endregion

    #region 文件的处理

    private string FileTxtPath = @"./data/test.txt";

    private string DirectoryDATA2Path = @"./data2/";

    private void OnGUI2()
    {
        if (GUILayout.Button("创建一个文本文件"))
        {
            //判断文件是否存在,然后新建文件,步骤和上方文件夹操作大同小异!
            if (!File.Exists(FileTxtPath))
            {
                File.Create(FileTxtPath);
                print("文件创建成功 " + FileTxtPath);
            }
            else
            {
                print("文件已经存在了~");
            }
        }
        if (GUILayout.Button("移动文本文件至data2"))
        {
            if (!Directory.Exists(DirectoryDATA2Path))
            {
                Directory.CreateDirectory(DirectoryDATA2Path);
            }

            if (!File.Exists(DirectoryDATA2Path + "test.txt"))
            {
                //移动文件夹操作,实参提供源地址和目标地址,注意,仔细查看路径是否包含文件名,如只有文件夹层级的路径,后面则需要添加文件名的字符串,否则会IO报错!!
                //File.Move(FileTxtPath, DirectoryDATA2Path + "test.txt");

                //使用Path类整合路径字符串,这里使用的是两个方法:
                //Path.Combine([第一个路径字符串],[第二个路径字符串])合并路径的字符串方法
                //Path.GetFileName([需要获取文件名及扩展名的路径字符串])获取这个路径的文件名及扩展名
                //通过Path.Combine组合过后的路径为:@"./data2/test.txt"
                File.Move(FileTxtPath, Path.Combine(DirectoryDATA2Path,Path.GetFileName(FileTxtPath)));
                print("移动成功");
            }
            else
            {
                print("这个文件已存在需要剪切的路径中");
            }


        }
        if (GUILayout.Button("复制文本文件至data2"))
        {
            if (!Directory.Exists(DirectoryDATA2Path))
            {
                Directory.CreateDirectory(DirectoryDATA2Path);
            }

            //使用Path类整合路径字符串,这里使用的是两个方法:
            //Path.Combine([第一个路径字符串],[第二个路径字符串])合并路径的字符串方法
            //Path.GetFileName([需要获取文件名及扩展名的路径字符串])获取这个路径的文件名及扩展名
            if (!File.Exists(Path.Combine(DirectoryDATA2Path,Path.GetFileName(FileTxtPath))))
            {
                //判断源路径是否有文件可复制,如果没有文件可复制则打印报错
                if (File.Exists(FileTxtPath))
                {
                    //复制文件夹操作,实参提供源地址和目标地址,注意,仔细查看路径是否包含文件名,如只有文件夹层级的路径,后面则需要添加文件名的字符串,否则会IO报错!!
                    File.Copy(FileTxtPath, Path.Combine(DirectoryDATA2Path, Path.GetFileName(FileTxtPath)));
                    print("复制成功");
                }
                else
                    print("源文件丢失,无法复制!");

            }
            else
            {
                print("这个文件已存在需要复制的路径中");
            }
        }
        if (GUILayout.Button("删除data2下的文本文件"))
        {
            //使用Path类整合路径字符串,这里使用的是两个方法:
            //Path.Combine([第一个路径字符串],[第二个路径字符串])合并路径的字符串方法
            //Path.GetFileName([需要获取文件名及扩展名的路径字符串])获取这个路径的文件名及扩展名
            if (File.Exists(Path.Combine(DirectoryDATA2Path, Path.GetFileName(FileTxtPath))))
            {
                File.Delete(Path.Combine(DirectoryDATA2Path, Path.GetFileName(FileTxtPath)));
                print("文件已删除");
            }
            else
            {
                print("文件不存在");
            }
        }

    }

    #endregion


    #region 小练习1

    //练习:
    //1.在D盘创建一个名字为Test的文件夹,并创建一个test.txt文件

    //2.(先在D盘放一个Test02.txt文件)检查D盘是否存在Test02.txt文件
    //若存在,则复制到D盘Test文件夹

    //3.(先在D盘创建一个Test03文件夹,并随便创建几个子文件夹)
    //通过本脚本获取D盘Test03文件夹下所有子文件夹的目录名称


    private string Work1FilePath = @"D:/Test/test.txt";
    private static string Work1FIleStaticPath = @"D:/Test/test.txt";

    //取得字符串中路径中目录的名称,这里需要的是静态的string类型字符串
    //最终取得的字符串为:D:/Test/
    private string Work1DirPath = Path.GetDirectoryName(Work1FIleStaticPath);

    private string Work2FilePath= @"D:/Test02.txt";

    private string Work3DirPath = @"D:/Test03/";
    private string[] Work3DirPaths;

    private void OnGUI3()
    {
        if (GUILayout.Button("WORK1"))
        {
            if (!Directory.Exists(Work1DirPath))
            {
                Directory.CreateDirectory(Work1DirPath);
                print("DIR done");
            }
            else
            {
                print("目录已存在");
            }

            if (!File.Exists(Work1FilePath))
            {
                File.Create(Work1FilePath);
                print("File done");
            }
            else
            {
                print("文件已存在");
            }
        }
        if (GUILayout.Button("WORK2"))
        {
            if (File.Exists(Work2FilePath))
            {
                if (!File.Exists(Path.Combine(Work1DirPath,Path.GetFileName(Work2FilePath))))
                {
                    //文件从源目录复制到新目录,最后一个实参是“是否覆盖”,这里填写true!如新目录已有存在的这个文件,则直接覆盖
                    //如果不使用最后一个实参是“是否覆盖”这个重载,如新目录已有存在的这个文件,则会直接报错,无法复制过去!
                    File.Copy(Work2FilePath, Path.Combine(Work1DirPath, Path.GetFileName(Work2FilePath)),true);

                    print("文件复制成功");
                }
            }
        }
        if (GUILayout.Button("WORK3"))
        {
            Work3DirPaths = Directory.GetDirectories(Work3DirPath);

            foreach (string item in Work3DirPaths)
            {
                print("子文件夹路径: " + item);
            }
        }
    }




    #endregion

    #region 文件的读写操作讲解

    //指定读写操作的文件路径
    private string RWFileDir = @"./data/test.txt";

    private void OnGUI4()
    {
        if (GUILayout.Button("写入文本至文件中"))
        {
            if (!Directory.Exists(@"./data"))
                Directory.CreateDirectory(@"./data");
            if (!File.Exists(RWFileDir))
                File.Create(RWFileDir);

            //一次写入大段文本
            //File.WriteAllText方法写入文件是覆盖写入,注意,是覆盖写入,不是追加写入!
            File.WriteAllText(RWFileDir,"123123");

            //每写一行之后写一个换行符,使用的是string数组类型,可以直接初始化数组并且赋值
            //File.WriteAllLines方法写入文件是覆盖写入,注意,是覆盖写入,不是追加写入!
            File.WriteAllLines(RWFileDir, new string[]
            {
            "aa",
            "bb",
            "cc"
            }
            );
        }
        if (GUILayout.Button("将文本文件内容读取至内存中"))
        {
            //一次将文件中所有内容读入内存,并转为字符串
            //print(File.ReadAllText(RWFileDir));

            //一次将文件中所有内容读入内存,转为字符串,将字符串按行形成一个字符串数组
            //遍历数组,逐个元素打印出来
            foreach (var item in File.ReadAllLines(RWFileDir))
            {
                print(item);
            }           
        }
    }

    #endregion

    #region 配置文件的读取案例

    //如配置文件存放在工程文件夹下的StreamingAssets文件夹中,则可以使用Application类的streamingAssetsPath方法直接指定StreamingAssets文件夹路径
    //然后将配置文件名及扩展名使用Path.Combine拼接即可。
    //注意:新版Unity的streamingAssetsPath路径在:项目/Assets/StreamingAssets/ 中!
    private string ConfigPath = Path.Combine(Application.streamingAssetsPath,"Config.txt");

    private string[] ConfigSZ;

    //创建嵌套一个子字典的字典ConfigDictonary
    private Dictionary<string,Dictionary<string,string>> ConfigDictonary = new Dictionary<string, Dictionary<string, string>>();

    //声明主键字符串
    private string PKey;

    private char[] CkeySplitChar = new char[] { '=' };           //声明切割字符串的关键切割字符

    private int CkeyJSNum=0;

    //声明子健字符串数组,这里因为方便区分,所以使用数组的方式存放子健和子健的值
    public string[] CKey = new string[2];

    private void OnGUI()
    {
        if (GUILayout.Button("读取配置文件"))
        {
            //字典已经赋值,再赋值就会报错,所以需要先判断
            if (ConfigDictonary.Count != 0)
            {
                print("配置已经读取完毕,无需再读取");
                return;
            }

            //按行获取配置文件,方便遍历执行操作
            ConfigSZ = File.ReadAllLines(ConfigPath);

            foreach (var item in ConfigSZ)
            {
                //print(item);
                //判定字符串开头是否是以"["开头,是的话处理[]内的内容
                if (item.StartsWith("["))
                {
                    //print("处理[]内容"); //调试行
                    //PKey = item;    //确定外层字典的键,注意,这样赋值的话,中括号也在赋值的范围内!

                    //如不需要中括号,只需要中括号内的内容的话,可以用如下方法:
                    //item.LastIndexOf 为截取部分字符至末尾指定的字符索引
                    PKey = item.Substring(1, item.LastIndexOf(']') - 1);

                    CkeyJSNum = 1;  //在外层字典的键确定后,内层需要初始化类型一次,这里使用变量计数
                }
                //判定字符串是否存在 "=",是的话切割=号两边的内容
                else if (item.IndexOf("=") >0)
                {
                    //print("处理=内容"); //调试行

                    //确定内层字典的键值,通过使用CkeySplitChar分隔符切割item字符串,分解为两个元素的数组
                    //使用 Split 切割重载,char数组和 StringSplitOptions.RemoveEmptyEntries 去除 切割字符 两边的 空白字符 后,赋值字符串数组切割后的两段字符串!
                    CKey = item.Split(CkeySplitChar,StringSplitOptions.RemoveEmptyEntries);   

                    //在外层字典的键确定后,内层需要初始化类型一次,这里使用变量计数,大于0才初始化
                    while (CkeyJSNum>0)
                    {
                        //添加外层键并初始化内层的字典类型
                        ConfigDictonary.Add(PKey, new Dictionary<string, string>());

                        //初始化完毕后禁止再初始化
                        CkeyJSNum = 0;
                    }

                    //每次切割确定内层字典的键值,就会在外层add添加对应的内层键值
                    //注意切割一定要注意切割后的空白字符,如赋值字典时有空白字符,之后要寻找字典的值就困难了
                    //ConfigDictonary[PKey].Add(CKey[0],CKey[1]);

                    //子健和值都去除空白字符,使用 string.Trim() 方法,但有些特殊情况的字符串需要保留空格的,建议CKey数组再单独声明string变量赋值判断!!
                    ConfigDictonary[PKey].Add(CKey[0].Trim(), CKey[1].Trim());
                }
                //其余判定为空格,不予执行,也可以使用item.Trim() 方法去除空白行!!
                else
                {
                    //print("空格不处理,不放入字典"); //调试行
                }
            }
        }
        if (GUILayout.Button("打印字典的键值"))
        {
            //字段没有赋值时,直接结束当前GUI
            if (ConfigDictonary.Count==0)
            {
                print("配置读取失败");
                return;
            }

            //这里注意,切割一定要注意切割后的空白字符,如赋值字典时有空白字符,之后要寻找字典的值就困难了!!
            print(ConfigDictonary["Job"]["Description1"]);

            //需要两层遍历才能完全读取嵌套字典的数据!!
            foreach (var ForPKey in ConfigDictonary)
            {
                //外层全部遍历显示
                //print("外层键: " + ForPKey.Key);

                foreach (var ForCKey in ForPKey.Value)
                {
                    //根据查找对应的键值内层和外层都打印显示
                    if (ForCKey.Key.Contains("Password"))
                    {
                        print("外层键: " + ForPKey.Key);
                        print("内层键: " + ForCKey.Key + " 对应值: " + ForCKey.Value);
                    }

                    //内层全部遍历显示
                    //print("内层键: " + ForCKey.Key + " 对应值: " + ForCKey.Value);
                }
            }

        }
    }

    #endregion
}


 楼主| 发表于 2023-3-30 15:00:34 | 显示全部楼层
配置文件案例中的配置文件:



[Game]  
Version=1

[Login]
Account = 账号不输入,怎么给你保存数据呀
Password = 没有密码,无法进入游戏
Success = 账号注册成功,赶紧登陆游戏爽去吧
Faild = 账号创建失败,赶快重新创建去吧,一会服务器满了,你就不能和其他小伙伴愉快玩耍啦
Successful = 登陆成功
LoginFaild = 账号或密码错误,请重新输入!!!

[Job]
Job1 = 战士
Job2 = 法师
Description1 = "    具有强大的近战攻击力,一直活跃在战斗的最前方。强大的力量和充沛的体力让他可以摧毁敌人的一切防御。(推荐新手使用)
Description2 = "    将所有的精力都用在魔法奥义上,每当他向空中举手的时候,不可预测的灾难即将到来。由于自身体力较弱,法师靠法术远距离攻打敌人。
Weapon1 = 砍刀
Weapon2 = 法杖

[Money]
Gold = 100
Diamond = 11.2

[Scene]
LoadFailed = 场景加载失败,重新加载~


回复 支持 反对

使用道具 举报

游客
回复
*滑块验证:

DA论坛飞机票来了~
快速回复 返回顶部 返回列表