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

news2024/9/23 1:28:51

目录

前言

整体的代码框架

如何读取数据文件的数据

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;
        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循环,程序结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2127047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一些硬件知识(二十二)

二极管&#xff08;Diode&#xff09;伏安特性、技术参数和项目中的应用 在正向偏置下&#xff0c;二极管呈现出良好的导电性能&#xff0c;可以允许电流通过&#xff1b;而在反向偏置下&#xff0c;二极管具有很高的阻断能力&#xff0c;几乎不允许电流通过。这是由构成二极管…

CC工具箱使用指南:【字段计算器学习版】

一、简介 这个工具算是Pro自带的字段计算器的扩展版。 工具预制了几种计算模式&#xff0c;通过可视化操作&#xff0c;帮你自动生成代码。 生成代码后&#xff0c;可以直接运行&#xff0c;也可以将代码复制到Pro自带的字段计算器中进行计算。 总之&#xff0c;这是给不会…

【CanMV K230 AI视觉】 人体检测

【CanMV K230 AI视觉】 人体检测 人体检测 动态测试效果可以去下面网站自己看。 B站视频链接&#xff1a;已做成合集 抖音链接&#xff1a;已做成合集 人体检测 人体检测是判断摄像头画面中有无出现人体&#xff0c;常用于人体数量检测&#xff0c;人流量监控以及安防监控等。…

研1日记9

1.理解conv1d和conv2d a. 1和2处理的数据不同&#xff0c;1维数据和图像 b. 例如x输入形状为(32,19,512)时&#xff0c;卷积公式是针对512的&#xff0c;而19应该变换为参数中指定的输出通道。 2.“SE块”&#xff08;Squeeze-and-Excitation Block&#xff09;它可以帮助模…

STM32-HAL库开发快速入门

注:本文主要记录一下STM32CubeMX软件的使用流程,记录内容以STM32外设&#xff08;中断、I2C、USART、SPI等配置&#xff09;在STM32CubeMX中的设置为主&#xff0c;对驱动代码编写不做记录&#xff0c;所以阅读本文最好有标准库开发经验。除第2节外&#xff0c;使用的都是韦东山…

鼠标hover过渡动画(已验证)

基于css中的grid方案实现的一个hover小动画&#xff0c;以下代码基于vue2&#xff0c;其他的可以直接复制html和css就可以&#xff0c;注意&#xff1a;以下代码在部分浏览器不兼容 注&#xff1a;部分代码来自我接入的GPT4o/Claude网站 <template><div class"h…

API安全测试 | Postman + BurpSuite 配置证书代理

img 在对使用基于证书的身份验证的 API进行安全你测试时&#xff0c;需要将证书添加到我们的工具&#xff08;如 Postman 和 Burp Suite&#xff09;中。这样我们才能够在客户端和服务器之间代理请求进行分析&#xff0c;以便评估 API 的安全性。 在本文我们将介绍将证书和私…

软硬链接 动静态库(深入地址空间)

文章目录 前言&#xff1a;软硬链接&#xff1a;动态库和静态库&#xff1a;如何制作静态库&#xff1f;如何制作动态库&#xff1f;动态库 VS 静态库 文本写入 && 二进制写入&#x1f680;动态库加载与可执行程序与地址空间的关系&#xff1a;&#x1f9db;‍♀️可执…

el-input设置type=‘number‘和v-model.number的区别

el-input设置typenumber’与设置.number修饰符的区别 1. 设置type‘number’ 使用el-input时想收集数字类型的数据&#xff0c;我们首先会想到typenumber&#xff0c;设置完type为number时会限制我们输入的内容只能为数字&#xff0c;不能为字符/汉字等非数字类型的数值&…

数据结构(7.2_3)——分块查找

分块查找的算法思想 分块查找&#xff0c;又称索引顺序查找&#xff0c;算法过程如下&#xff1a; 在索引表中确定待查记录所属的分块(可顺序、可折半)在块内顺序查找 "索引表"中保存每个分块的最大关键字和分块存储区间 特点&#xff1a;块内无序&#xff0c;块…

新的 PIXHELL 攻击从隔离系统中窃取机密

在一篇新发表的论文中&#xff0c;本古里安大学的 Mordechai Guri 博士揭露了“PIXHELL”攻击。 这是一种从隔离、音频隔离系统中窃取敏感数据的新方法。 此次攻击允许恶意攻击者通过 LCD 屏幕产生的声学信号泄露信息&#xff0c;利用线圈噪音和由操纵的像素模式引起的电容器…

【安全系列--处理挖矿】

现象&#xff1a;我们云上waf提示有台服务器存在挖矿行为 解决思路&#xff1a; 1、查看服务器的进程情况 top发现服务的CPU使用率非常高 2、使用性能分析工具perf查看占用的cpu进程 sudo apt install linux-tools-common发现一些kernel进程存在异常 3、使用find查一下这…

在广袤的数据通信旷野,“伙伴+华为”体系点亮星云

在浩浩荡荡的智能化变革中&#xff0c;从用户层面看&#xff0c;来自地市区县的各个行业与企业是真正的主体&#xff0c;以他们为主体的商业市场&#xff0c;提供了智能化进程中最为关键的广度。 而从技术角度看&#xff0c;数据通信是千行万业都需要的基本数字化能力。数据通信…

[Web安全 网络安全]-文件包含漏洞

文章目录&#xff1a; 一&#xff1a;前言 1.什么是文件包含漏洞 2.文件包含漏洞的成因 3.文件包含漏洞的分类 4.文件包含漏洞的防御策略 5.文件包含函数&#xff08;触发点Sink&#xff09; 6.环境 6.1 靶场 6.2 其他工具 二&#xff1a;文件包含LFI labs靶场实验…

组播 2024 9 11

PIM&#xff08;Protocol Independent Multicast&#xff09;是一种常用的组播路由协议&#xff0c;其独立于底层的单播路由协议&#xff0c;能够在多种网络环境中有效地实现多播路由功能。PIM主要有两种模式&#xff1a;PIM Sparse Mode (PIM-SM) 和 PIM Dense Mode (PIM-DM)&…

sqli-labs靶场自动化利用工具——第1关

文章目录 概要整体架构流程技术细节执行效果小结 概要 Sqli-Labs靶场对于网安专业的学生或正在学习网安的朋友来说并不陌生&#xff0c;或者说已经很熟悉。那有没有朋友想过自己开发一个测试脚本能实现自动化化测试sqli-labs呢&#xff1f;可能有些人会说不是有sqlmap&#xf…

【TCP】相关机制:异常处理

文章目录 1 . **进程崩溃**2 . **主机关机**&#xff08;正常流程的关机&#xff09;3 . **主机掉电**&#xff08;直接拔电源&#xff09;1. 接收方掉电2. 发送方掉电 4 . **网线断开** 1 . 进程崩溃 Java 中的体现就是抛出异常&#xff0c;但没人 catch&#xff0c;最终异常…

Apache SeaTunnel Zeta 引擎源码解析(二) Client端的任务提交流程

作者&#xff1a;刘乃杰 编辑整理&#xff1a;曾辉 引入 本系列文章是基于 Apache SeaTunnel 2.3.6版本&#xff0c;围绕Zeta引擎给大家介绍其任务是如何从提交到运行的全流程&#xff0c;希望通过这篇文档&#xff0c;对刚刚上手SeaTunnel的朋友提供一些帮助。 我们整体的文…

如何用 Scrapy 爬取网站数据并在 Easysearch 中进行存储检索分析

做过数据分析和爬虫程序的小伙伴想必对 Scrapy 这个爬虫框架已经很熟悉了。今天给大家介绍下&#xff0c;如何基于 Scrapy 快速编写一个爬虫程序并利用 Easysearch 储存、检索、分析爬取的数据。我们以极限科技的官网 Blog 为数据源&#xff0c;做下实操演示。 安装 scrapy 使…

Linux shell编程学习笔记79:cpio命令——文件和目录归档工具(下)

在 Linux shell编程学习笔记78&#xff1a;cpio命令——文件和目录归档工具&#xff08;上&#xff09;-CSDN博客https://blog.csdn.net/Purpleendurer/article/details/142095476?spm1001.2014.3001.5501中&#xff0c;我们研究了 cpio命令 的功能、格式、选项说明 以及 cpi…