• 代码设计:C++ 一个CSV功能类(源码)


            CSV格式是逗号分隔的文本文件,几十年前挺常用,现在几乎没人用,但是因为很多工具都支持,可以作为各种数据迁移的中介,所以始终没有退出历史舞台。

            CSV格式非常简单,就是一行一条记录、逗号分隔字段而已。

            下面是个代码,主要是为了用excel编辑数据而做的,不保证和非execl的兼容性。

            主要的细节是对不同语言编码的处理和文本转换。因为用的只是UTF-8,所以只检测了UTF-8的BOM。

    1. //csv格式文件处理
    2. #include
    3. //基本csv功能
    4. class CCSV
    5. {
    6. private:
    7. CStdOSFile m_file;
    8. vector m_Heads;//头标,如果no_head则没有头标
    9. public:
    10. //根据路径名创建所有目录,不包括最后的文件名
    11. static bool CreateDir(char const* filename)
    12. {
    13. string dirname;
    14. char const* p = filename;
    15. for (; '\0' != *p; ++p)
    16. {
    17. dirname += *p;
    18. if ('/' == *p)
    19. {
    20. //thelog<
    21. mkdir(dirname.c_str(), S_IRWXU | S_IRWXG);
    22. }
    23. }
    24. return true;
    25. }
    26. //csv格式,双引号内为普通文本,文本内双引号要用两个双引号表达,简单起见全部加上了双引号
    27. static string& CSVEncode(char const* in, string& out)
    28. {
    29. out = "";
    30. out += '\"';
    31. for (char const* p = in; *p != '\0'; ++p)
    32. {
    33. if ('\"' == *p)out += '\"';
    34. out += *p;
    35. }
    36. out += '\"';
    37. return out;
    38. }
    39. //csv格式,双引号内为普通文本,文本内双引号要用两个双引号表达,简单起见全部加上了双引号
    40. static string CSVEncode(string const& in)
    41. {
    42. string out;
    43. return CSVEncode(in.c_str(), out);
    44. }
    45. //打开文件
    46. bool OpenCSV(char const* filepatnname, bool no_head = false)
    47. {
    48. m_Heads.clear();
    49. if (!CreateDir(filepatnname))return false;
    50. if (!m_file.OpenR(filepatnname))return false;
    51. if (!no_head)
    52. {
    53. bool isLineEnd;
    54. string str;
    55. while (_ReadField(isLineEnd, str))
    56. {
    57. if (isLineEnd)
    58. {
    59. break;
    60. }
    61. if (0 == m_Heads.size())
    62. {//对第一个消除可能存在的UTF-8签名(EF BB BF)
    63. if (0 == strncmp("\xEF\xBB\xBF", str.c_str(), 3))
    64. {
    65. str.erase(0, 3);
    66. }
    67. }
    68. //thelog << str << endi;
    69. m_Heads.push_back(str);
    70. }
    71. thelog << "获得列头 " << m_Heads.size() << " 个" << endi;
    72. }
    73. return true;
    74. }
    75. //获得字段序号
    76. long GetFieldIndex(char const* field)const
    77. {
    78. vector::const_iterator it;
    79. for (it = m_Heads.begin(); it != m_Heads.end(); ++it)
    80. {
    81. if (*it == field)return it - m_Heads.begin();
    82. }
    83. return -1;
    84. }
    85. //获得字段数
    86. long GetFieldCount()const
    87. {
    88. return m_Heads.size();
    89. }
    90. //获得字段名
    91. string GetFieldName(long field)const
    92. {
    93. return m_Heads[field];
    94. }
    95. //读取字段,或者是一个字段,或者是行结束
    96. bool _ReadField(bool& isLineEnd, string& ret)
    97. {
    98. bool tmp = __ReadField(isLineEnd, ret);
    99. TrimAll(ret);
    100. return tmp;
    101. }
    102. bool __ReadField(bool& isLineEnd, string& ret)
    103. {
    104. isLineEnd = false;
    105. ret = "";
    106. char m_c;
    107. string str;
    108. bool isFirstChar = true;
    109. bool isInStr = false;
    110. bool isInStrQuote = false;//是否在字符串内出现了一个双引号,这意味着字符串结束,或者一个内嵌双引号的开始
    111. while (1 == m_file.Read(&m_c, 1))
    112. {
    113. if (isFirstChar && ('\r' == m_c || '\n' == m_c))
    114. {//仅当第一个字符就是行结束才认为是行结束而没有获得数据
    115. isLineEnd = true;
    116. if ('\r' == m_c)
    117. {
    118. if (1 == m_file.Read(&m_c, 1))
    119. {
    120. if ('\n' != m_c)
    121. {
    122. m_file.SeekCur(-1);//不是连续\r\n,吐回去
    123. }
    124. }
    125. }
    126. return true;
    127. }
    128. else
    129. {
    130. isFirstChar = false;
    131. }
    132. if (isInStr && isInStrQuote && '\"' == m_c)
    133. {//内嵌双引号
    134. ret = +m_c;
    135. isInStrQuote = false;
    136. continue;
    137. }
    138. if (isInStr && isInStrQuote && ',' == m_c)
    139. {//字符串结束,逗号标记字段结束
    140. return true;
    141. }
    142. if (isInStr && isInStrQuote && ('\r' == m_c || '\n' == m_c))
    143. {//字符串结束,行结束
    144. m_file.SeekCur(-1);//吐回行结束,否则永远不能发现单独的行结束
    145. return true;
    146. }
    147. if (isInStr && isInStrQuote)
    148. {//字符串结束
    149. isInStr = false;
    150. continue;
    151. }
    152. if (isInStr && !isInStrQuote && '\"' == m_c)
    153. {//遇到内部第一个双引号
    154. isInStrQuote = true;
    155. continue;
    156. }
    157. if (isInStr && !isInStrQuote)
    158. {//字符串内容,除了双引号都不用特殊判断
    159. ret += m_c;
    160. continue;
    161. }
    162. if (!isInStr && '\"' == m_c)
    163. {//字符串开始
    164. isInStr = true;
    165. continue;
    166. }
    167. if (!isInStr && ',' == m_c)
    168. {//字段结束
    169. return true;
    170. }
    171. if (!isInStr && ('\r' == m_c || '\n' == m_c))
    172. {//行结束
    173. m_file.SeekCur(-1);//吐回行结束,否则永远不能发现单独的行结束
    174. return true;
    175. }
    176. if (!isInStr)
    177. {//非字符串的其余
    178. ret += m_c;
    179. continue;
    180. }
    181. }
    182. return false;
    183. }
    184. bool ReadField(string& ret)
    185. {
    186. bool isLineEnd;
    187. while (_ReadField(isLineEnd, ret))
    188. {
    189. if (!isLineEnd)return true;
    190. }
    191. return false;
    192. }
    193. //关闭文件
    194. bool CloseCSV()
    195. {
    196. return m_file.Close();
    197. }
    198. //文件到表格
    199. bool LoadFromFile(char const* file, CHtmlDoc::CHtmlTable2 & table)
    200. {
    201. table.Clear();
    202. CCSV csv;
    203. if (!csv.OpenCSV(file))
    204. {
    205. return false;
    206. }
    207. long i;
    208. for (i = 0; i < csv.GetFieldCount(); ++i)
    209. {
    210. table.AddCol(csv.GetFieldName(i));
    211. }
    212. bool isLineEnd;
    213. string str;
    214. bool need_new_line = false;
    215. table.AddLine();
    216. while (csv._ReadField(isLineEnd, str))
    217. {
    218. if (isLineEnd)
    219. {
    220. need_new_line = true;
    221. }
    222. else
    223. {
    224. if (need_new_line)
    225. {
    226. table.AddLine();
    227. need_new_line = false;
    228. }
    229. table.AddData(str);
    230. }
    231. }
    232. table.Fill();
    233. return csv.CloseCSV();
    234. }
    235. };
    236. class CText
    237. {
    238. public:
    239. //删除两端的空白,连续回车换行缩减为一个换行
    240. static string FormatText(char const* text)
    241. {
    242. string ret = text;
    243. TrimAll(ret);
    244. string::size_type pos = 0;
    245. bool isInNewLine = false;
    246. while (pos < ret.size())
    247. {
    248. if ('\r' == ret[pos] || '\n' == ret[pos])
    249. {
    250. if (!isInNewLine)
    251. {
    252. isInNewLine = true;
    253. ret[pos] = '\n';
    254. ++pos;
    255. }
    256. else
    257. {//丢弃连续的换行,pos不变
    258. ret.erase(pos, 1);
    259. }
    260. }
    261. else
    262. {
    263. isInNewLine = false;
    264. ++pos;
    265. }
    266. }
    267. return ret;
    268. }
    269. };

            这段代码中用到一些别的东西,基本上属于看到名字就能猜到功能的:

    CStdOSFile 就是一个文件类,可以用任何别的文件类替换掉

    CHtmlDoc::CHtmlTable2 这是一个表格类,可以输出为文本表格、HTML表格、变更JS,以后我可能会把这个类整理出来

    TrimAll 删除两端空白字符

            这个代码是在UNIX、Linux上运行的。

            看注释应该能理解这个代码了,有问题也可以来问我。

    (这里是结束)

  • 相关阅读:
    前端技术知识(含八股)总结 - 持续更新中
    【回眸】牛客网刷刷刷!(八)——中断专题
    web3再牛 也没能逃出这几个老巨头的手掌心
    TS编程——面向对象随手录——接口_命名空间——重构javaScrip 3D引擎代码部分——函数
    字节首席架构师整合面试痛点,成就399页Java框架核心宝典
    Redis 哈希表操作实战(全)
    设计数据密集型应用的主要关注点
    12、FPGA程序的固化和下载
    【C++】-- STL之用红黑树模拟实现map和set
    oracle学习42-增加表空间
  • 原文地址:https://blog.csdn.net/2301_77171572/article/details/133900988