//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
}
|