引言

  最经关于基础C开发框架基本都搭建好了. 在研究githup,准备传上去. 可惜的是两会连githup 都登陆不进去.

三观很正的我也觉得, 这样不好. 双向标准, 共x党不是一个代表穷苦大众的党.当然我也恒感谢党国, 给我选举权,每次都是

人大代表帮我投了,好人. 谢谢了!

  后面可能没办法, 继续上传到 csdn 上. 会把使用手册,注意事项写清楚.这个框架,适合新手参考吧.大多库还是很复杂的

内力不足看多了容易走火入魔.我这里提供都比较浅显易懂. 适合使用. 感受简单,高效,能用,实在的设计.

  

  杀死那个石家庄人 http://music.163.com/#/song?id=386844

前言

  同样先介绍一写,这节要将的精华.首先说一下大白文,将读取文件的内容直到全部.

  1. /*
  2. * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
  3. * 需要事后使用 tstring_destroy(&ret); 销毁这个字符串对象
  4. * path : 文件路径
  5. * ret : 返回创建好的字符串内容,返回NULL表示读取失败
  6. */
  7. tstring file_malloc_readend(const char* path)
  8. {
  9. int c;
  10. tstring tstr;
  11. FILE* txt = fopen(path, "r");
  12. if (NULL == txt) {
  13. SL_NOTICE("fopen r path = '%s' error!", path);
  14. return NULL;
  15. }
  16.  
  17. //这里创建文件对象,创建失败直接返回
  18. if ((tstr = tstring_create(NULL)) == NULL) {
  19. fclose(txt);
  20. return NULL;
  21. }
  22.  
  23. //这里读取文本内容
  24. while ((c = fgetc(txt))!=EOF)
  25. if (_RT_OK != tstring_append(tstr, c)){ //出错了就直接销毁已经存在的内容
  26. tstring_destroy(&tstr);
  27. break;
  28. }
  29.  
  30. fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
  31. return tstr;
  32. }

一个细节是 加了字符串数据 返回判断,如果内存分配失败直接返回.

还有一个值得学习的细节是 只能在堆上分配的内存结构

  1. /*
  2. * 这里是一个解析 csv 文件的 简单解析器.
  3. * 它能够帮助我们切分文件内容,保存在数组中.
  4. */
  5. struct sccsv { //内存只能在堆上
  6. int rlen; //数据行数,索引[0, rlen)
  7. int clen; //数据列数,索引[0, clen)
  8. const char* data[]; //保存数据一维数组,希望他是二维的 rlen*clen
  9. };
  10.  
  11. typedef struct sccsv* sccsv_t;

上面是新语法, 以前的做法是data[0], data[1]等. 在结构体中声明可变数组.这种结构是不完全结构无法 直接 struct sccsv 在堆上声明.

这里基本上就是我们说的. 再扯一点当你使用inline语法在C中的时候. 一种是static inline 内联.一种如下内联声明

  1. /*
  2. * 获取某个位置的对象内容,这个函数 推荐声明为内联的, window上不支持
  3. * csv : sccsv_t 对象, new返回的
  4. * ri : 查找的行索引 [0, csv->rlen)
  5. * ci : 查找的列索引 [0, csv->clen)
  6. * : 返回这一项中内容,后面可以用 atoi, atof, str_dup 等处理了...
  7. */
  8. extern inline const char* sccsv_get(sccsv_t csv, int ri, int ci);

到这里基本C基础普及就这样了,等一下分析正文.

正文

  那就开始正题描述吧.首先什么是csv文件. 对比显差异.预览图

再看看实际的编码图

通过这个看应该就知道csv文件的编码规则了吧. 总结如下

  1.用 , 分割

  2.如果出现 , " 这种特殊字符, 会被用 "" 包裹起来, 并且 "" 表示一个 " 号

  3.每行用\r\n结束

这样语法问题都已经解决了.

再分析我们今天的 接口内容 sccsv.h

  1. #ifndef _H_SCCSV
  2. #define _H_SCCSV
  3.  
  4. /*
  5. * 这里是一个解析 csv 文件的 简单解析器.
  6. * 它能够帮助我们切分文件内容,保存在数组中.
  7. */
  8. struct sccsv { //内存只能在堆上
  9. int rlen; //数据行数,索引[0, rlen)
  10. int clen; //数据列数,索引[0, clen)
  11. const char* data[]; //保存数据一维数组,希望他是二维的 rlen*clen
  12. };
  13.  
  14. typedef struct sccsv* sccsv_t;
  15.  
  16. /*
  17. * 从文件中构建csv对象, 最后需要调用 sccsv_die 释放
  18. * path : csv文件内容
  19. * : 返回构建好的 sccsv_t 对象
  20. */
  21. extern sccsv_t sccsv_new(const char* path);
  22.  
  23. /*
  24. * 释放由sccsv_new构建的对象
  25. * pcsv : 由sccsv_new 返回对象
  26. */
  27. extern void sccsv_die(sccsv_t* pcsv);
  28.  
  29. /*
  30. * 获取某个位置的对象内容,这个函数 推荐声明为内联的, window上不支持
  31. * csv : sccsv_t 对象, new返回的
  32. * ri : 查找的行索引 [0, csv->rlen)
  33. * ci : 查找的列索引 [0, csv->clen)
  34. * : 返回这一项中内容,后面可以用 atoi, atof, str_dup 等处理了...
  35. */
  36. extern inline const char* sccsv_get(sccsv_t csv, int ri, int ci);
  37.  
  38. #endif // !_H_SCCSV

构建销毁获得指定内容. 很容易理解.

现在我们展示一下运行的结果, 测试代码是

  1. #include <schead.h>
  2. #include <sclog.h>
  3. #include <sccsv.h>
  4.  
  5. #define _STR_PATH "onetime.csv"
  6. // 解析 csv文件内容
  7. int main(int argc, char* argv[])
  8. {
  9. sccsv_t csv;
  10. int i, j;
  11. int rlen, clen;
  12.  
  13. INIT_PAUSE();
  14. sl_start();
  15.  
  16. // 这里得到 csv 对象
  17. csv = sccsv_new(_STR_PATH);
  18. if (NULL == csv)
  19. CERR_EXIT("open " _STR_PATH " is error!");
  20.  
  21. //这里打印数据
  22. rlen = csv->rlen;
  23. clen = csv->clen;
  24. for (i = ; i < rlen; ++i) {
  25. for (j = ; j < clen; ++j) {
  26. printf("<%d, %d> => [%s]\n", i, j, sccsv_get(csv, i, j));
  27. }
  28. }
  29.  
  30. //开心 测试圆满成功
  31. sccsv_die(&csv);
  32. return ;
  33. }

最后运行的预览图

运行起来可能复杂一点点, 这里摘录一下编译图,还是看代码吧,你自己找其中关于 test_csv.c 文件的编译过程吧

  1. C = gcc
  2. DEBUG = -g -Wall -D_DEBUG
  3. #指定pthread线程库
  4. LIB = -lpthread -lm
  5. #指定一些目录
  6. DIR = -I./module/schead/include -I./module/struct/include
  7. #具体运行函数
  8. RUN = $(CC) $(DEBUG) -o $@ $^ $(LIB) $(DIR)
  9. RUNO = $(CC) $(DEBUG) -c -o $@ $^ $(DIR)
  10.  
  11. # 主要生成的产品
  12. all:test_cjson_write.out test_csjon.out test_csv.out test_json_read.out test_log.out\
  13. test_scconf.out test_tstring.out
  14.  
  15. #挨个生产的产品
  16. test_cjson_write.out:test_cjson_write.o schead.o sclog.o tstring.o cjson.o
  17. $(RUN)
  18. test_csjon.out:test_csjon.o schead.o sclog.o tstring.o cjson.o
  19. $(RUN)
  20. test_csv.out:test_csv.o schead.o sclog.o sccsv.o tstring.o
  21. $(RUN)
  22. test_json_read.out:test_json_read.o schead.o sclog.o sccsv.o tstring.o cjson.o
  23. $(RUN)
  24. test_log.out:test_log.o schead.o sclog.o
  25. $(RUN)
  26. test_scconf.out:test_scconf.o schead.o scconf.o tree.o tstring.o sclog.o
  27. $(RUN)
  28. test_tstring.out:test_tstring.o tstring.o sclog.o schead.o
  29. $(RUN)
  30.  
  31. #产品主要的待链接文件
  32. test_cjson_write.o:./main/test_cjson_write.c
  33. $(RUNO)
  34. test_csjon.o:./main/test_csjon.c
  35. $(RUNO)
  36. test_csv.o:./main/test_csv.c
  37. $(RUNO)
  38. test_json_read.o:./main/test_json_read.c
  39. $(RUNO)
  40. test_log.o:./main/test_log.c
  41. $(RUNO) -std=c99
  42. test_scconf.o:./main/test_scconf.c
  43. $(RUNO)
  44. test_tstring.o:./main/test_tstring.c
  45. $(RUNO)
  46.  
  47. #工具集机械码,待别人链接
  48. schead.o:./module/schead/schead.c
  49. $(RUNO)
  50. sclog.o:./module/schead/sclog.c
  51. $(RUNO)
  52. sccsv.o:./module/schead/sccsv.c
  53. $(RUNO)
  54. tstring.o:./module/struct/tstring.c
  55. $(RUNO)
  56. cjson.o:./module/schead/cjson.c
  57. $(RUNO)
  58. scconf.o:./module/schead/scconf.c
  59. $(RUNO)
  60. tree.o:./module/struct/tree.c
  61. $(RUNO)
  62.  
  63. #删除命令
  64. clean:
  65. rm -rf *.i *.s *.o *.out __* log ; ls -hl
  66. .PHONY:clean

最后展示 实现的代码

  1. #include <schead.h>
  2. #include <sccsv.h>
  3. #include <sclog.h>
  4. #include <tstring.h>
  5.  
  6. //从文件中读取 csv文件内容
  7. char* __get_csv(FILE* txt, int* prl, int* pcl)
  8. {
  9. int c, n;
  10. int cl = , rl = ;
  11. TSTRING_CREATE(ts);
  12. while((c=fgetc(txt))!=EOF){
  13. if('"' == c){ //处理这里数据
  14. while((c=fgetc(txt))!=EOF){
  15. if('"' == c) {
  16. if((n=fgetc(txt)) == EOF) { //判断下一个字符
  17. SL_WARNING("The CSV file is invalid one!");
  18. free(ts.str);
  19. return NULL;
  20. }
  21. if(n != '"'){ //有效字符再次压入栈
  22. ungetc(n, txt);
  23. break;
  24. }
  25. }
  26. //都是合法字符 保存起来
  27. if (_RT_OK != tstring_append(&ts, c)) {
  28. free(ts.str);
  29. return NULL;
  30. }
  31. }
  32. //继续判断,只有是c == '"' 才会下来,否则都是错的
  33. if('"' != c){
  34. SL_WARNING("The CSV file is invalid two!");
  35. free(ts.str);
  36. return NULL;
  37. }
  38. }
  39. else if(',' == c){
  40. if (_RT_OK != tstring_append(&ts, '\0')) {
  41. free(ts.str);
  42. return NULL;
  43. }
  44. ++cl;
  45. }
  46. else if('\r' == c)
  47. continue;
  48. else if('\n' == c){
  49. if (_RT_OK != tstring_append(&ts, '\0')) {
  50. free(ts.str);
  51. return NULL;
  52. }
  53. ++cl;
  54. ++rl;
  55. }
  56. else {//其它所有情况只添加数据就可以了
  57. if (_RT_OK != tstring_append(&ts, c)) {
  58. free(ts.str);
  59. return NULL;
  60. }
  61. }
  62. }
  63.  
  64. if(cl % rl){ //检测 , 号是个数是否正常
  65. SL_WARNING("now csv file is illegal! need check!");
  66. return NULL;
  67. }
  68.  
  69. // 返回最终内容
  70. *prl = rl;
  71. *pcl = cl;
  72. return ts.str;
  73. }
  74.  
  75. // 将 __get_csv 得到的数据重新构建返回, 执行这个函数认为语法检测都正确了
  76. sccsv_t __get_csv_new(const char* cstr, int rl, int cl)
  77. {
  78. int i = ;
  79. sccsv_t csv = malloc(sizeof(struct sccsv) + sizeof(char*)*cl);
  80. if(NULL == csv){
  81. SL_FATAL("malloc is error one !");
  82. return NULL;
  83. }
  84.  
  85. // 这里开始构建内容了
  86. csv->rlen = rl;
  87. csv->clen = cl / rl;
  88. do {
  89. csv->data[i] = cstr;
  90. while(*cstr++) //找到下一个位置处
  91. ;
  92. }while(++i<cl);
  93.  
  94. return csv;
  95. }
  96.  
  97. /*
  98. * 从文件中构建csv对象, 最后需要调用 sccsv_die 释放
  99. * path : csv文件内容
  100. * : 返回构建好的 sccsv_t 对象
  101. */
  102. sccsv_t
  103. sccsv_new(const char* path)
  104. {
  105. FILE* txt;
  106. char* cstr;
  107. int rl, cl;
  108.  
  109. DEBUG_CODE({
  110. if(!path || !*path){
  111. SL_WARNING("params is check !path || !*path .");
  112. return NULL;
  113. }
  114. });
  115. // 打开文件内容
  116. if((txt=fopen(path, "r")) == NULL){
  117. SL_WARNING("fopen %s r is error!", path);
  118. return NULL;
  119. }
  120. // 如果解析 csv 文件内容失败直接返回
  121. cstr = __get_csv(txt, &rl, &cl);
  122. fclose(txt);
  123.  
  124. // 返回最终结果
  125. return cstr ? __get_csv_new(cstr, rl, cl) : NULL;
  126. }
  127.  
  128. /*
  129. * 释放由sccsv_new构建的对象
  130. * pcsv : 由sccsv_new 返回对象
  131. */
  132. void
  133. sccsv_die(sccsv_t* pcsv)
  134. {
  135. if (pcsv && *pcsv) { // 这里 开始释放
  136. free(*pcsv);
  137. *pcsv = NULL;
  138. }
  139. }
  140.  
  141. /*
  142. * 获取某个位置的对象内容
  143. * csv : sccsv_t 对象, new返回的
  144. * ri : 查找的行索引 [0, csv->rlen)
  145. * ci : 查找的列索引 [0, csv->clen)
  146. * : 返回这一项中内容,后面可以用 atoi, atof, str_dup 等处理了...
  147. */
  148. inline const char*
  149. sccsv_get(sccsv_t csv, int ri, int ci)
  150. {
  151. DEBUG_CODE({
  152. if(!csv || ri< || ri>=csv->rlen || ci< || ci >= csv->clen){
  153. SL_WARNING("params is csv:%p, ri:%d, ci:%d.", csv, ri, ci);
  154. return NULL;
  155. }
  156. });
  157. // 返回最终结果
  158. return csv->data[ri*csv->clen + ci];
  159. }

到这里原本要结束了,但是再扯一点,上面采用的内存模型是整体内存模型, 一共分配两次,一次为了所有字符.一次

为了保存分割串内容. 可以细细品味上面 new那段代码,还是很有意思的.

到这里简单的基础库的各个细节都已经实现完毕. 下一次会详细介绍怎么使用这个框架.再试试githup. 实在

不行再采用菜一点的做法.

后记

  错误是难免的,欢迎吐槽.... 以科幻的图结束吧 -------------------------------未来是不可知的---------------------------------

C 封装一个csv 解析库的更多相关文章

  1. .NET Core中的CSV解析库

    感谢 本篇首先特别感谢从此启程兄的<.NetCore外国一些高质量博客分享>, 发现很多国外的.NET Core技术博客资源, 我会不定期从中选择一些有意思的文章翻译总结一下. .NET ...

  2. [js高手之路] 跟GhostWu一起封装一个字符串工具库-架构篇(1)

    所谓字符串工具库就是利用javascript面向对象的知识封装一个常用的字符串处理方法库,首先给这个库起个名字,好吧就叫ghostwu.js. 看下ghostwu.js的整体架构: ; (functi ...

  3. 开源一个CSV解析器(附设计过程 )

    在ExcelReport支持csv的开发过程中,需要一个NETStandard的csv解析器.在nuget上找了几个试用,但都不太适合. 于是,便有了:AxinLib.IO.CSV. 先看看怎么用: ...

  4. 如何优雅的封装一个DOM事件库

    1.DOM0级事件和DOM2级事件 DOM 0级事件是元素内的一个私有属性:div.onclick = function () {},对一个私有属性赋值(在该事件上绑定一个方法).由此可知DOM 0级 ...

  5. [js高手之路] 跟GhostWu一起封装一个字符串工具库-扩展trim,trimLeft,trimRight方法(2)

    我们接着上一篇的继续,在上一篇我们完成了工具库的架构,本文扩展字符串去空格的方法, 一共有3个 1,trimLeft: 去除字符串左边的空格 2,trimRight: 去除字符串右边的空格 3,tri ...

  6. [js高手之路] 跟GhostWu一起封装一个字符串工具库-扩展字符串位置方法(4)

    本文,我们接着之前的框架继续扩展,这次扩展了一共有5个与字符串位置相关的方法 between( left, right ) 返回两个字符串之间的内容, 如果第二个参数没有传递,返回的是找到的第一个参数 ...

  7. [js高手之路] 跟GhostWu一起封装一个字符串工具库-扩展camelize与dasherize方法(3)

    在此之前,我们已经完成了4个方法: trimLeft, trimRight, trim, capitalize 本文,我们扩展驼峰式与下划线转化这两个对称的方法 camelize: 把空格,下划线,中 ...

  8. 一个非常棒的Go-Json解析库

    json是一种数据格式,经常被用作数据交换,页面展示,序列化等场景,基本每种语言都有对应的json解析框架,Go语言也不例外,并且内置了json库,基本能够满足一些普通开发场景,但有些复杂场景下就不太 ...

  9. C 封装一个简单二叉树基库

    引文 今天分享一个喜欢佩服的伟人,应该算人类文明极大突破者.收藏过一张纸币类型如下 那我们继续科普一段关于他的简介 '高斯有些孤傲,但令人惊奇的是,他春风得意地度过了中产阶级的一生,而  没有遭受到冷 ...

随机推荐

  1. 008 The Generics In JAVA

    泛型是JAVA的核心特型之一,我们先看一个例子: 没有使用泛型前,如下: import java.util.ArrayList; import java.util.List; public class ...

  2. python连mysql时避免出现乱码

    使用python连mysql时候,常常出现乱码,采取以下措施可以避免 1 Python文件设置编码 utf-8 (文件前面加上 #encoding=utf-8)2 MySQL数据库charset=ut ...

  3. opencv编程解决warning C4003: “max”宏的实参不足

    忘了把程序出错的代码附上了,运行修改好的程序才发现的.只好把问题的代码大致写一下了: warning C4003: “min”宏的实参不足 error C2589: “(”:“::”右边的非法标记 e ...

  4. 慕课网-安卓工程师初养成-4-14 Java 循环语句之多重循环

    来源:http://www.imooc.com/code/1497 循环体中包含循环语句的结构称为多重循环.三种循环语句可以自身嵌套,也可以相互嵌套,最常见的就是二重循环.在二重循环中,外层循环每执行 ...

  5. NOIP2004 虫食算

    描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子:43#9865#045+ 8468#6633= 44445506678其中#号代表 ...

  6. pb中读取大文本数据

    string ls_FileName,lb_FileDatas,lb_FileData  long ll_FileLen,ll_Handle,ll_Loop,ll_Bytes,ll_Loops,ll_ ...

  7. cordova ios --->OC 调用 js (一)

    1.在HTML中定义一个函数如OCcallJS() function OCcallJS(){ alert("OC 调用js 的 方法"); } 2.当webview 加载完成的时候 ...

  8. Yii cookie 的使用方法

    设置cookie: //首先新建cookie$cookie = new CHttpCookie('mycookie', 'this is my cookie');//定义cookie的有效期$cook ...

  9. 高效率的全组合算法(Java版实现)

    博客上看到的一个算法,用Java实现了一个 算法描述: 算法说明:当n大于2时,n个数的全组合一共有(2^n)-1种. 当对n个元素进行全组合的时候,可以用一个n位的二进制数表示取法. 1表示在该位取 ...

  10. Android窗口为弹出框样式

    1.XML android:theme="@android:style/Theme.Dialog <?xml version="1.0" encoding=&quo ...