using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using UnityEngine; using UnityEngine.Networking; namespace AibisDream.Utility { public class CsvUtil { // 默认编码 private static readonly Encoding DefaultEncode = Encoding.UTF8; // 默认分隔符 private const char FieldSeparator = ','; /// /// 根据平台读取文件内容 /// private static string ReadFileText(string filePath) { #if UNITY_ANDROID && !UNITY_EDITOR // Android 平台需要使用 UnityWebRequest 读取 StreamingAssets if (filePath.Contains(Application.streamingAssetsPath)) { // Android 上 StreamingAssets 在 APK 中,需要构建正确的 URL string url = filePath; // 确保使用正确的路径分隔符 url = url.Replace("\\", "/"); // 如果路径不包含协议前缀,添加 file:// if (!url.StartsWith("file://") && !url.StartsWith("jar:file://")) { // 对于 Android APK 中的文件,使用 jar:file:// 协议 string relativePath = url.Replace(Application.streamingAssetsPath, "").TrimStart('/'); url = "jar:file://" + Application.dataPath + "!/assets/" + relativePath; } using (UnityWebRequest www = UnityWebRequest.Get(url)) { www.SendWebRequest(); // 等待请求完成(同步等待) while (!www.isDone) { // 在主线程中等待 } if (www.result == UnityWebRequest.Result.Success) { return www.downloadHandler.text; } else { throw new IOException($"无法读取文件: {filePath}, URL: {url}, 错误: {www.error}"); } } } else { // 非 StreamingAssets 路径,使用普通文件读取 return File.ReadAllText(filePath, DefaultEncode); } #else // 其他平台直接使用 File 类 return File.ReadAllText(filePath, DefaultEncode); #endif } /// /// 根据平台创建 StreamReader /// private static StreamReader CreateStreamReader(string filePath) { #if UNITY_ANDROID && !UNITY_EDITOR // Android 平台需要使用 UnityWebRequest 读取 StreamingAssets if (filePath.Contains(Application.streamingAssetsPath)) { string fileText = ReadFileText(filePath); MemoryStream stream = new MemoryStream(DefaultEncode.GetBytes(fileText)); return new StreamReader(stream, DefaultEncode); } else { // 非 StreamingAssets 路径,使用普通文件读取 return new StreamReader(filePath, DefaultEncode); } #else // 其他平台直接使用 File 类 return new StreamReader(filePath, DefaultEncode); #endif } public static List Read(string filePath) { List dataList = new List(); StreamReader reader = null; try { using (reader = CreateStreamReader(filePath)) { while (!reader.EndOfStream) { string line = reader.ReadLine(); if (line == null) break; string[] data = line.Split(FieldSeparator); dataList.Add(data); } } } catch (Exception ex) { foreach (Process process in Process.GetProcesses()) { if (process.ProcessName.ToUpper().Equals("EXCEL")) process.Kill(); } GC.Collect(); Thread.Sleep(50); Console.WriteLine(ex.StackTrace); using (reader = CreateStreamReader(filePath)) { while (!reader.EndOfStream) { string line = reader.ReadLine(); if (line == null) break; string[] data = line.Split(FieldSeparator); dataList.Add(data); } } } finally { reader?.Close(); } return dataList; } public static void WriteCsv(string filePath, List dataList) { StreamWriter writer = new StreamWriter(filePath, false, DefaultEncode); foreach (string[] data in dataList) { writer.WriteLine(string.Join(FieldSeparator, data)); } writer.Close(); } public static List ReadAsBean(string filePath) where T : class { // 反射部分 var targetType = typeof(T); var props = targetType.GetProperties(); // 读取CSV using var reader = CreateStreamReader(filePath); // 第一行是注释 reader.ReadLine(); // 第二行作为title var titles = reader.ReadLine()?.Split(FieldSeparator); if (titles == null) { System.Diagnostics.Debug.WriteLine("csv无标题"); throw new Exception("csv无标题"); } var res = new List(); // 第二行开始读配置内容 while (!reader.EndOfStream) { string newLine = reader.ReadLine(); if (string.IsNullOrEmpty(newLine)) break; string[] rowData = newLine.Split(FieldSeparator); if (rowData == null || rowData.Length == 0) continue; // 转为Bean var obj = Activator.CreateInstance(targetType); foreach (var prop in props) { string name = prop.Name; if (!titles.Contains(prop.Name)) continue; int idx = Array.IndexOf(titles, prop.Name); prop.SetValue(obj, GetDefaultValue(prop, rowData[idx])); } res.Add(obj as T); } return res; } public static bool ReadAsDataTable(ref DataTable myCsvDt, string filepath) { var strPath = filepath; //csv文件的路径 try { bool blnFlag = true; StreamReader reader = CreateStreamReader(strPath); myCsvDt = new DataTable(); while (reader.ReadLine() is { } strLine) { var aryLine = strLine.Split(FieldSeparator); //第一行是列的名字,给datatable加上列名, if (blnFlag) { blnFlag = false; var intColCount = aryLine.Length; #region 序号作为列头 //int col = 0; //for (int i = 0; i < intColCount; i++) //{ // col = i + 1; // mydc = new DataColumn(col.ToString()); // mycsvdt.Columns.Add(mydc); //} #endregion #region 第一行作为列头 for (int i = 0; i < intColCount; i++) { myCsvDt.Columns.Add(new DataColumn(aryLine[i])); } continue; #endregion } //填充数据并加入到datatable中 myCsvDt.Rows.Add(aryLine); } reader.Close(); return true; } catch (Exception) { return false; } } public static void ExportAsCsv(DataTable dt, string savaPath, string strName) { string strPath = savaPath + "\\" + strName; //保存到指定目录下 if (File.Exists(strPath)) { File.Delete(strPath); } //先打印标头 StringBuilder strColu = new StringBuilder(); StringBuilder strValue = new StringBuilder(); StreamWriter sw = new StreamWriter(new FileStream(strPath, FileMode.CreateNew), DefaultEncode); try { for (var i = 0; i <= dt.Columns.Count - 1; i++) { strColu.Append(dt.Columns[i].ColumnName); strColu.Append(FieldSeparator); } strColu.Remove(strColu.Length - 1, 1); //移出掉最后一个,字符 sw.WriteLine(strColu); foreach (DataRow dr in dt.Rows) { strValue.Remove(0, strValue.Length); //移出 for (var i = 0; i <= dt.Columns.Count - 1; i++) { strValue.Append(dr[i]); strValue.Append(FieldSeparator); } strValue.Remove(strValue.Length - 1, 1); //移出掉最后一个,字符 sw.WriteLine(strValue); } sw.Close(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); sw.Close(); } } #region 类型转换 private static object GetDefaultValue(PropertyInfo prop, string value) { return prop.PropertyType.Name switch { "String" => ToNormalString(value), "Int32" => ToInt32(value), "Decimal" => ToDecimal(value), "Single" => ToFloat(value), "Boolean" => ToBoolean(value), "DateTime" => ToDateTime(value), "Double" => ToDouble(value), _ => ToEnum(value, prop) }; } private static object ToEnum(string value, PropertyInfo prop) { return prop.PropertyType.BaseType == typeof(Enum) ? Enum.Parse(prop.PropertyType, value) : null; } /// /// 转换为Int32 /// private static int ToInt32(string obj) { if (string.IsNullOrEmpty(obj)) return 0; try { if (obj.Contains(".")) return (int)Convert.ToSingle(obj); if (int.TryParse(obj, out var tmp)) return tmp; } catch { return 0; } return 0; } /// /// 转换为字符串 /// private static string ToNormalString(string obj) { if (obj == null) return string.Empty; try { return Convert.ToString(obj); } catch { return string.Empty; } } /// /// 转换为日期 /// private static DateTime ToDateTime(string obj) { if (string.IsNullOrEmpty(obj)) { return Convert.ToDateTime("1970-01-01 00:00:00"); } try { return Convert.ToDateTime(obj); } catch { return Convert.ToDateTime("1970-01-01 00:00:00"); } } /// /// 转换为布尔型 /// private static bool ToBoolean(string obj) { if (obj == null) return false; return obj.ToLower() == "true" || obj == "1"; } /// /// 转换为十进制数值 /// private static Decimal ToDecimal(string obj) { if (obj == null) { return 0M; } var resultString = Regex.Replace(obj, "[^0-9.]", ""); var result = resultString.Length == 0 ? 0M : decimal.Parse(resultString, CultureInfo.CurrentCulture); if (obj.StartsWith("-")) { result *= -1M; } return result; } /// /// 转换为双精度. /// private static double ToDouble(string obj) { if (double.TryParse(obj, out var num)) return num; else return 0; } /// /// 转换为单精度. /// private static float ToFloat(string value) { var normalStr = ToNormalString(value); if (string.IsNullOrEmpty(normalStr)) { return 0; } if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) return result; else return 0; } #endregion } }