基于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函数

cpp 复制代码
#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函数

cpp 复制代码
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

write函数

cpp 复制代码
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

整体的函数框架

打开数据文件

创建结果文件

在while循环里

读取一行的数据

处理数据

写入结果文件

最后关闭文件

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

cpp 复制代码
#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_lineprocess_data ,根据read_line函数的返回值决定是否对读取到的数据进行处理。


如何读取数据文件的数据

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

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

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

read_line

cpp 复制代码
/* 返回值: 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

cpp 复制代码
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;
        else
            level = 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循环,程序结束。

相关推荐
Biomamba生信基地1 小时前
Linux也有百度云喔~
linux·运维·服务器·百度云
无敌最俊朗@1 小时前
stm32学习之路——八种GPIO口工作模式
c语言·stm32·单片机·学习
new_abc1 小时前
Ubuntu 22.04 ftp搭建
linux·运维·ubuntu
EterNity_TiMe_2 小时前
【论文复现】STM32设计的物联网智能鱼缸
stm32·单片机·嵌入式硬件·物联网·学习·性能优化
flying robot2 小时前
RPM的使用
linux
理想不理想v2 小时前
[经典] 前端js将文件流导出为csv/excel文件
前端·javascript·excel
m0_589828872 小时前
Excel根据条件动态索引单元格范围
excel
匆匆整棹还2 小时前
已有账号,重装系统激活office后发现没有ppt,word,excel等
word·powerpoint·excel
spjhandsomeman2 小时前
EXCEL 或 WPS 列下划线转驼峰
excel·wps