(一)说明
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。
(二)特点与注意事项
优点: 1、结构简单,易于理解; 2、解析文本和还原文本的方式较为简洁高效; 3、可以轻松转换为Excel的 .xls 文件,亦可以利用 Excel 以表格的方式进行查阅。相比 .xls 文件,其本身由于只存储文本而不包含表格中的公式等其他附带信息,在相同的文件内容下 CSV 文件可以具有更小的文件体积。
缺点: 1、相比于二进制文件,由于是纯文本存储,体积会比较大; 2、虽然由于数据格式参差不齐,具备基本的安全性,但破解的风险依旧很高。
(三)注意事项
1、可以在Excel中创建保存为 CSV 文件,但是后续对 CSV 文件操作最好用 Notepad++ 等文本编辑器来打开,最好使用 Notepad++;
2、使用Notepad++打开 CSV 文件后,需要将其转码为 UTF-8 格式,这样才能保证文件中的中文被正确显示,而 Excel 存储的文件均不是 UTF-8 编码格式的;
3、在Notepad++中打开 CSV 文件后,会发现多了一个空行,这是 Excel 的存储所导致的。最好把这个空行删掉,以便于程序中计算文件中的真实行数。
(四)简单操作方法
1、读取方法:
/* CSV文件路径 */
string filePath = Application.streamingAssetsPath + "/CSVDemo.csv";
/* 读取CSV文件,一行行读取 */
string[] fileData = File.ReadAllLines(filePath);
/* CSV文件的第一行为Key字段,先读取key字段 */
string[] keys = fileData[0].Split(',');
/* 第二行开始是数据 */
for (int i = 1; i < fileData.Length; i++)
{
/* 每一行的内容都是逗号分隔,读取每一列的值 */
string[] lineData = fileData[i].Split(',');
for (int j = 0; j < lineData.Length; j++)
{
Debug.Log("key:" + keys[j] + ",value:" + lineData[j] + "\n");
}
}
2、写入方法:
/* CSV文件路径 */
string filePath = Application.streamingAssetsPath + "/CSVDemo.csv";
/* 读取CSV文件,一行行读取 */
string[] fileData = File.ReadAllLines(filePath);
/* 把CSV文件按行存放,每一行的ID作为key值,内容作为value值 */
Dictionary csvDataDic = new Dictionary();
/* CSV文件的第一行为Key字段,先读取key字段 */
string[] keys = fileData[0].Split(',');
/* 第二行开始是数据 */
for (int i = 1; i < fileData.Length; i++)
{
/* 每一行的内容都是逗号分隔,读取每一列的值 */
string[] lineData = fileData[i].Split(',');
/* CSVDemo类与CSVDemo.csv文件的key字段一一对应,用于保存每一行的数据内容 */
CSVDemo csvDemo = new CSVDemo();
for (int j = 0; j < lineData.Length; j++)
{
if (keys[j] == "ID")
{
csvDemo.ID = Convert.ToInt32(lineData[j]);
}
else if (keys[j] == "Name")
{
csvDemo.Name = lineData[j];
}
}
/* 保存每一行ID和数据对象的关系 */
csvDataDic[csvDemo.ID] = csvDemo;
}
3、反射:
/* 把CSV文件按行存放,每一行的ID作为key值,内容作为value值 */
Dictionary csvDataDic = new Dictionary();
/* CSV文件路径 */
string filePath = Application.streamingAssetsPath + "/CSVDemo.csv";
/* 从CSV文件读取数据 */
Dictionary> datasDic = LoadCsvFile(filePath);
/* 遍历每一行数据 */
foreach (string ID in datasDic.Keys)
{
/* CSV的一行数据 */
Dictionary 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;
}
4、泛型:
///
///读取CSV文件数据(利用反射)
///
/// CSV数据对象的类型
/// CSV文件路径
/// 用于缓存数据的字典
/// CSV文件所有行内容的数据对象
private Dictionary LoadCsvData(string csvFilePath)
{
Dictionary dic = new Dictionary();
/* 从CSV文件读取数据 */
Dictionary> result = LoadCsvFile(csvFilePath);
/* 遍历每一行数据 */
foreach (string ID in result.Keys)
{
/* CSV的一行数据 */
Dictionary datas = result[ID];
/* 读取Csv数据对象的属性 */
PropertyInfo[] props = typeof(T_CsvData).GetProperties();
/* 使用反射,将CSV文件的数据赋值给CSV数据对象的相应字段,要求CSV文件的字段名和CSV数据对象的字段名完全相同 */
T_CsvData obj = Activator.CreateInstance();
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 csvDataDic2 = LoadCsvData(filePath);
/* 测试读取ID为2的数据 */
CSVDemo csvDemo2 = csvDataDic2[2];
Debug.Log("ID=" + csvDemo2.ID + ",Name=" + csvDemo2.Name);