【先说点废话】
哈哈哈哈好久没发文章不知道大家有没有想我,这一大段时间鬼知道我经历了什么,弄比赛、备战考研、各种求职各种做简历、弄毕业设计、租房子。。。等等等,做了一大堆都没做太好哈哈哈,不过好在找到了心仪的工作,之后会继续保持更新,把一些技术分享给大家,一起进步一起学习。以后如果有时间也特别想分享一下我这段日子的经历,十分的难能可贵。好了话不多说,接下来进入正题吧!
【需要了解的知识基础】
什么是CSV文件?
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据。—— 百度百科
简单来说,CSV 文件可以直接通过文件内容来获取表格数据。那逗号分割值是什么意思呢?比如我们需要学生的数据表,包含学生的学号,姓名,性别等级这些数据,通常我们会用 Excel 来记录,它的表现形式类似于下表:
学号 | 姓名 | 性别 |
---|---|---|
1001 | 张三 | boy |
1002 | 李四 | girl |
1003 | 王五 | none |
(请不要问我none是什么性别(●ˇ∀ˇ●))
那 CSV 文件的形式又是什么样子呢?我们在 Excel 中做一个如上表格,然后另存为 CSV 格式文件,用 NotePad++ 打开,会发现他是如下样式
学号,姓名,性别
1001,张三,boy
1002,李四,girl
1003,王五,none
可以看到文件中所有的值都主要以逗号分隔开,每一行数据携带换行符。(当然,逗号不一定是唯一的分割方式,但是是比较常用。这里以“,”这个字符分割为例)
为什么要使用CSV文件?
目前总结出来的优点就有以下三点,但是在认识优点的同时也要认识到这种方式所存在的不足,权衡利弊后进行选择。
优点:
1. 结构简单,易于理解;
2. 解析文本和还原文本的方式较为简洁高效;
3. 可以轻松转换为 Excel 的 .xls 文件,亦可以利用 Excel 以表格的方式进行查阅。相比 .xls 文件,其本身由于只存储文本而不包含表格中的公式等其他附带信息,在相同的文件内容下 CSV 文件可以具有更小的文件体积。
缺点:
1. 相比于二进制文件,由于是纯文本存储,体积会比较大;
2. 虽然由于数据格式参差不齐,具备基本的安全性,但破解的风险依旧很高。
使用CSV文件要注意什么?
- 可以在 Excel 中创建保存为 CSV 文件,但是后续对 CSV 文件操作最好用 Notepad++ 等文本编辑器来打开,最好使用 Notepad++,详细原因看了之后的几点就懂啦;
- 使用 Notepad++ 打开 CSV 文件后,需要将其转码为 UTF-8 格式,这样才能保证文件中的中文被正确显示,而 Excel 存储的文件均不是 UTF-8 编码格式的;
- 在 Notepad++ 中打开 CSV 文件后,会发现多了一个空行,这是 Excel 的存储所导致的。我个人习惯把这个空行删掉,以便于我程序中计算文件中的真实行数。
【又到了紧张刺激的分析环节了】
没有勇气看代码? 可能分析你都没有勇气看完。。。
表的结构分析
在准备环节中已经对 CSV 文件有了初步的介绍,简短的总结下 CSV 文件的结构:
1. 第一行是表中数据对象包含的所有属性的属性名称,即键名;
2. 从第二行开始,每一行都是一个独立的数据对象,包含了所有属性的属性值;
3. 值与值之间使用逗号分隔。
数据模型分析
主键:用于唯一标识一个对象的键值,比如学号可以唯一找到一条学生数据,假设这个学生的学号是12345,那么“学号”就是主键的名称,而“12345”就是主键值,一般简称为主键。——by 亦泽
数据对象类:
- 数据对象类应提供所有属性值的读取功能,而除主键之外还应提供所有的属性值的写入功能,以便对数据对象的读写操作。
- 我们需要通过属性名称来获取对应的属性值。基于这样的键值关系,我们需要使用 Dictionary(即字典)类型来存储所有的属性名称和对应的属值;
- 构造对象的三个参数:
(1) 由于主键值为只读模式,所以需要在构造数据对象时来设置主键值,并提供一个属性供外部访问其主键;
(2) 由于所有的键值(除主键)可以修改,而键名不可以修改或者增加删除,所以所有的键值对儿(除主键)也需要在构造对象时初始化;
(3) 由于第二个参数中不包含主键的键名,但是要保证数据对象的标签(用所有的键名有序构造)是完整的,所以第三个参数要传入所有的键名组成的数组来构造标签。 - 为了使数据的读写更简单高效,可改写
this
关键来构建与 Dictionary 类类似的读写方式,其应隐式包含 GetValue 和 SetValue 两个方法; - 重写
ToString()
方法,提供对象的数据内容便于测试; - 继承 IEnumerable 接口,实现遍历数据对象中所有键值对的方法。
数据表类:
- 数据表类应该提供所有数据对象的读取功能,所有的数据对象应该都是只读的,但是以数据对象为单位添加或者删除。
- 可以通过每条数据的主键来访问到具体的数据对象,从而访问该数据对象的其他属性值;
- 与数据对象类相似的,可使用改写
this
关键字来提供更简单高效的读写模式; - 数据表对象应该具有但不限于增、删、改、查四个功能;
- 数据表对象应具有获取标签的方法,即在构造数据表对象时记录数据表所有的键名,并提供获取签名的方法。该方法的算法应与数据对象签名的算法保持一致;
- 数据表对象应具有将抽象数据结构转换为文本字符串的方法共外部使用,方便外部直接获取数据对象的所对应的文本值而无须考虑其中的算法;
- 数据表类应提供静态方法通过表名和文件内容用于构造数据表对象,方法内实现将文本内容解析为数据表对象的算法并返回;
- 基于上述的需求,构造数据表对象时应该提供两个参数:表名和键名数组;
- 重写
ToString()
方法,提供数据表的字符串形式用于测试; - 继承 IEnumerable 接口,实现遍历数据表中所有数据对象的方法。
读、写算法分析
好了,写了一大推,有点累,但是还没完,我们还要分析一下主要算法是如何实现的,其他方法就不分析了主要是一些逻辑校验之类的,到时候直接看代码就好啦。废话不多说,继续继续~
我们再来重新温习一下所涉及到的文件格式,一般策划给的都是基于 Excel 的类似于下面这种格式(从这里开始的表需要记住,在代码中我会使用这个表来进行测试)
编号 | 姓名 | 年龄 | 性别 |
---|---|---|---|
1001 | 张三 | 20 | 男 |
1002 | 李四 | 20 | 女 |
1003 | 王五 | 12 | 不详 |
1004 | ABC | 100 | male |
当我们改存为 CSV 文件时,就会变成这个样子(别忘了之前讲过的几点注意事项!)
编号,姓名,年龄,性别
1001,张三,20,男
1002,李四,40,女
1003,王五,12,不详
1004,ABC,100,male
文本字符串转化为数据表对象
首先我们观察下我们的文本内容,一共有五行,第一行是所有的键名,第二行开始每一行都是一个数据对象,对应着键名都有四个属性。那么我们可以先把他们以行为单位进行拆分,得到一个关于行的数组,第一行单独用作键名数组,从第二行开始,每一行构造一个数据对象,然后存储在数据表对象当中。
数据表对象转化为文本字符串
相当于反过来思考,首先键名行是比较特殊的,单独拿出来写;从第二行开始,我们可以循环去将所有数据对象的所有属性值拼接出来写入。
文件的读写
可以使用 System.IO 中的方法来操作。我这里采用的是 Stream 的方式来操作,因为文本中含有中文,所以使用 Stream 搭配 File 类中静态方法的方式时较为简单。
【代码来啦!】
数据对象类 CSVDataObject
// ------------------------------ //
// Product Name : CSV_Read&Write
// Company Name : MOESTONE
// Author Name : Eazey Wang
// Create Data : 2017/12/16
// ------------------------------ //
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CSVDataObject : IEnumerable
{
/// <summary>
/// 此值作为数据对象的唯一标识,只能通过此属性获取到唯一标识
/// 无法通过 '数据对象[主键名]' 的方式来获取
/// </summary>
public string ID { get { return _major; } }
private readonly string _major;
/// <summary>
/// 一条数据应包含的所有的键名
/// </summary>