当前位置: 首页 > news >正文

基于Linux文件编程实现处理Excel表格的数据

目录

前言

整体的代码框架

如何读取数据文件的数据

read_line

如何处理读取到的数据

process_data

运行结果

总结


前言

        本文是基于Linux文件编程的一个小实验,用文件IO来读取Excel表格的数据,处理后写入另一个文件,本文只是对文件IO的基本应用,巩固文件编程的知识。Linux一切皆文件。本文的侧重点是对于数据的处理。

(图1) 

来看这个简单的表格,要计算三人的总分,对于熟练应用Excel表格的人来说很简单,在 E2 输入计算公式 =sum(b2:d2) 然后下拉就能计算三人的总分;如果再加上一个评分栏,根据总分给每个人评级,对于不熟悉Excel的人来说就有点难了,但我们可以通过编程实现这个功能。

(上图为最终实现的效果,将图1的Excel文件另存为 .csv 格式) 

 .csv格式是用逗号分割的文件,用记事本打开可以看到第一行的数据为",语文,数学,英语,总分,评价",之所以第一个字符是逗号,是因为表格的第一行第一列为空,csv格式会用逗号分隔每个数据,“总分”和“评价”那一列也没有数据,但是空出来的数据之间也有逗号分隔开,将score.csv文件上传到虚拟机,就可以开始编写代码了。


整体的代码框架

对于Linux文件编程不了解的,可以看我之前的博客Linux文件编程,里面有详细的函数介绍和代码示例。

我们只需要用三个函数:
open函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

read函数

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);

write函数

#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);

整体的函数框架

\bullet 打开数据文件

\bullet 创建结果文件

\bullet 在while循环里
        \circ 读取一行的数据
        \circ 处理数据
        \circ 写入结果文件

\bullet 最后关闭文件

以上就是整个程序的大致框架,现在来写程序,难点还是数据的处理

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>/*打印错误信息所要包含的头文件*//* 最终编译出来的可执行文件的用法,以及main函数的参数* ./process_excel data.csv result.csv* argc    = 3* argv[0] = "./process_excel"* argv[1] = "data.csv"* argv[2] = "result.csv"*/int main(int argc, char **argv)
{/*数据文件和结果文件的文件描述符*/int fd_data, fd_result;/*记录用户自己编写的函数的返回值*/int len;/*存放从数据文件读到的数据*/unsigned char data_buf[1000];/*存放处理好即将写入结果文件的数据*/unsigned char result_buf[1000];  /*如果main函数传入的参数不对,则打印用法*/if (argc != 3){printf("Usage: %s <data.csv> <result.csv>\n", argv[0]);return -1;}/*以只读的方式打开数据文件*/fd_data = open(argv[1], O_RDONLY);if (fd_data < 0){/*打开失败就打印失败信息*/printf("can not open file %s\n", argv[1]);perror("open");return -1; }else{/*成功打开打印数据文件的文件描述符*/printf("data file fd = %d\n", fd_data);}/*可读可写方式打开(不存在就创建),截断文件,权限为0644(文件所有者具有读写权限,其他用户具有读权限)*/fd_result = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);if (fd_result < 0){printf("can not creat file %s\n", argv[2]);perror("creat");return -1; }else{printf("result file fd = %d\n", fd_result);}/*------------------------------------重点------------------------------------*/while(1){/*从数据文件里读取1行*/len = read_line(fd_data, data_buf);if(len == -1){	break;	}if(len != 0){/*处理数据,将处理好的数据存入result_buf*/process_data(data_buf, result_buf);/*写入结果文件,要写入的每一行数据都是不带回车换行的,需要我们自己写入*/ write(fd_result, result_buf, strlen(result_buf));write(fd_result, "\r\n", 2);}}/*养成好习惯,关闭打开的文件,避免造成损坏*/close(fd_data);close(fd_result);return 0;
}

整个看下来,思路还是比较清晰的,就是文件IO的基本应用,重点就在于main函数的while循环里,有两个函数需要我们自己编写,read_line 和 process_data ,根据read_line函数的返回值决定是否对读取到的数据进行处理。


如何读取数据文件的数据

        读取数据文件后直接拿来判断显然不现实,在每一行数据的结尾都有一个回车换行才能从下一行开始写新的数据,但这些回车换行我们看不到,我们可以将这些内容转换成十六进制的数据

在终端输入以下命令 hexdump -C score.csv 后面的是要转换的文件名,可以看到十六进制的数据

可以看到,回车换行的十六进制是 0x0d0x0a ,就是我们常用的 '\r''\n' ,现在来编写读取每一行数据的函数

read_line

/* 返回值: n 表示读到了一行的数据的个数(n >= 0)*        -1 表示读到了文件的尾部或者出错*/
static int read_line(int fd, unsigned char *buf)
{/*循环读入一个字符*//*如何判断已经读完一行?读到 0x0d , 0x0a*/	/*读到一个字符放入c*/unsigned char c;/*记录read函数的返回值*/int len;/*传入的字符串的元素指针*/int i = 0;/*根据err变量判断该函数的返回值*/int err = 0; while(1){/*read函数读取成功会返回读取到的字节数,失败返回-1*/len = read(fd, &c, 1);if(len <= 0){/*读到文件末尾或发生错误*/err = -1;break;	}else{if(c != '\n' && c != '\r'){buf[i++] = c;}else{/*碰到回车换行*/err = 0;break;}}} /*表示字符串的结束*/buf[i] = '\0'; if(err && (i == 0)){/*读到文件尾部并且一个数据都没有读到*/return -1;}else{/*返回读取到的数据的个数+1,因为我们手动添加了结束符 '\0'*/return i;}
}

这个函数还是需要点时间看的,建议结合转换后的十六进制表示的文件来看,思路会更加清晰,一直循环读取数据文件,一个字节一个字节的读

读到0x0d的时候,跳出while循环,执行 buf[i] = '\0'; 这句代码,构造出一个字符串存入data_buf里,然后去处理数据。读到0x0a也立马跳出循环,构造出一个空的字符串。


如何处理读取到的数据

直接看代码

process_data

void process_data(unsigned char *data_buf, unsigned char *result_buf)
{/*	示例1:data_buf=",语文,数学,英语,总分,评价" *  	  result_buf=",语文,数学,英语,总分,评价" *	示例2:data_buf="张三,90,91,92" *  	  result_buf="张三,90,91,92,273,A+" */ char name[100];int scores[3];int sum;char *levels[] = {"A+", "A", "B"};int level;if (data_buf[0] == 0xef) /* 对于UTF-8编码的文件,它的前3个字符是0xef 0xbb 0xbf */{/*进入这个分支是因为我们不需要处理第一行的数据*//*将data_buf文件的数据拷贝到result_buf*/strcpy(result_buf, data_buf);}else{/*用sscanf拆分字符串,第一个为%[^,],是因为如果用%s会分割失败*/sscanf(data_buf, "%[^,],%d,%d,%d,", name, &scores[0], &scores[1], &scores[2]);/*计算总分*/sum = scores[0] + scores[1] + scores[2];if (sum >= 270)level = 0;else if (sum >= 240)level = 1;elselevel = 2;/*构造字符串*/sprintf(result_buf, "%s,%d,%d,%d,%d,%s", name, scores[0], scores[1], scores[2], sum, levels[level]);}
}

处理数据就比较简单了,第一行照搬不用处理,之后用sscanf拆分数据,分类再组合就可以写入结果文件了。


运行结果

编译运行后cat一下 result.csv ,查看文件的内容,可以看到正是我们想要的效果,传回Windows看看Excel表格,非常完美。


总结

因为是一行一行的处理数据,有些小伙伴可能会疑惑,数据为什么不会被覆盖,为什么不会得到同样的数据?

我们要知道read函数的原理,你读一个字符,文件的光标就会移动一个位置,你下次再打开这个文件,他的光标位置是不变的,除非你用lseek函数来改变光标的位置,所以我们每读一行,再加上结束符'\0'就相当于一个字符串,然后立马处理数据并且写入结果文件;下次再去读数据文件,是从上一次读的位置的下一个开始读,所以才能得到一行一行的数据。

什么时候读完?
读到文件尾部就读完了,就会跳出main函数的while循环,程序结束。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《深入理解 Java 中的继承与方法重写》
  • JavaScript 中 new Function() 和 new function() 的区别
  • 51. 数组中的逆序对
  • HTML中的文字与分区标记
  • Docker高级管理之compose容器编排与私有仓库的部署
  • UE5 对象池
  • 【研赛论文】数学建模2024华为杯论文word/latex模板
  • 希捷,AI时代的存储“破壁者”
  • R语言数据整理和分析(1)
  • 对接开源大模型应用开发平台最佳实践
  • B3635 硬币问题
  • vue3 指定元素全屏 screenfull(可直接粘贴使用)
  • 【Python机器学习】循环神经网络(RNN)——对RNN进行预测
  • Ansible自动化部署kubernetes集群
  • PowerShell收集信息及绕过PowerShell权限
  • 【Leetcode】101. 对称二叉树
  • 【刷算法】求1+2+3+...+n
  • ES10 特性的完整指南
  • If…else
  • js
  • JS+CSS实现数字滚动
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • SpriteKit 技巧之添加背景图片
  • SwizzleMethod 黑魔法
  • tab.js分享及浏览器兼容性问题汇总
  • Terraform入门 - 1. 安装Terraform
  • 复杂数据处理
  • 开发基于以太坊智能合约的DApp
  • 我感觉这是史上最牛的防sql注入方法类
  • 新书推荐|Windows黑客编程技术详解
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • ​字​节​一​面​
  • #LLM入门|Prompt#3.3_存储_Memory
  • #QT(串口助手-界面)
  • #stm32驱动外设模块总结w5500模块
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (学习日记)2024.01.09
  • (一)认识微服务
  • (转)Scala的“=”符号简介
  • .NET Framework 3.5安装教程
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET MVC之AOP
  • .Net多线程总结
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • .net知识和学习方法系列(二十一)CLR-枚举
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • [<MySQL优化总结>]
  • [2016.7.Test1] T1 三进制异或