使用open、read、write处理表格数据

 一、需求

  • 在下面的excel表格中,计算每一个人的总分及评价。当总分大于270时评价为A,当总分位于[240,270]之间时评价为B,当总分低于240分时评价为C。
  • 表格名为score.csv(保存为csv是为了将内容以逗号隔开)
语文数学英语总分评价
张三909192
李四808182
王五707172

 二、解决方案

1.命令行输入指令:./xxxx  score.csv  result.csv。其中xxxx为可执行文件,score.csv作为原数据文件,result.csv作为处理后的输出文件。当result.csv不存在时则创建,存在时截断以防数据覆盖不全。
2.逐行读取,每读取一行原数据文件内容就存入缓冲区数组中,再进行处理,处理后存入输出文件中。

3.read_line为读取一行的函数,逐个读取1个字符,若遇到'\r'或'\n'则说明该行结束。

4.采用sscanf函数在指定格式中提取关键数据,%[^,] 可从当前字符串位置开始读取所有不是逗号的字符,直到遇到逗号或字符串的结尾。

5.使用sprintf可以将格式化数据写入一个字符串中。

 三、代码实现

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <string.h>

/*返回值:
*-1:表示错吴或者读到尾部
*i:表示读到的数据个数
*/
int read_line(int fd, unsigned char *buf)
{
    int len = 0;
    unsigned char c;
    int i = 0;
    int wei = 0;//尾部判断

    while (1)
    {
        len = read(fd, &c, 1);
        if (len < 0)
        {
            perror("read_line");
            return -1;
        }
        else if (len == 0) // 尾部或者回车后的换行,换行读取的数据是0
        {
            wei = -1;
            break;
        }
        else
        {
            if (c != '\n' && c != '\r')
            {
                buf[i] = c;
                i++;
                wei  = 0;
            }
            else
            {
                wei = 0;
                break; // 行末跳出
            }
        }
    }
    buf[i] = '\0';
    if(wei == -1 && i== 0)
        return -1;
    else
        return i;
}

void process_data(unsigned char *data_buf, unsigned char *result_buf)
{
    if (data_buf[0] == 0xef) /* 对于UTF-8编码的文件,它的前3个字符是0xef 0xbb 0xbf */
    {
        strcpy(result_buf, data_buf);
        strcat(result_buf,"\r\n"); //在尾部加入回车换行
    }
    else
    {
        char name[100];
        int score[3];
        int sum = 0;
        int level;
        char *levels[] = {"A", "B", "C"};
        sscanf(data_buf, "%[^,],%d,%d,%d", name, &score[0], &score[1], &score[2]);
        //printf("result: %s --->get name---> %s\n\r", data_buf, name);
        sum = score[0] + score[1] + score[2];
        if (sum > 270)
        {
            level = 0;
        }
        else if ( sum >= 240 && sum <=270)
        {
            level = 1;
        }
        else
            level = 2;
        sprintf(result_buf,"%s,%d,%d,%d,%d,%s\r\n",name, score[0], score[1], score[2],sum,levels[level]);
        
    }
    //printf("%s",result_buf);
}

/* argc=3
 * argv[0]=./process_excel
 * argv[1]=data.csv
 * argv[2]=result.csv
*/
int main(int argc, char **argv)
{
    int fd_data;
    int fd_result;
    unsigned char data_buf[1000];
    unsigned char result_buf[1000];
    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)
    {
        perror("open");
        return -1;
    }
    else
    {
        printf("fd = %d\n", fd_data);
    }

    // 创建结果文件
    fd_result = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR,0664);
    if (fd_result < 0)
    {
        perror("creat");
        return -1;
    }
    else
    {
        printf("fd = %d\n", fd_result);
    }

    int len;
    while (1)
    {
        len = read_line(fd_data, data_buf);
        if (len == -1)
            break;
        if (len != 0) //读到回车换行,len=0,result_buf还是上一次的内容
        {
            //printf("%s\n", data_buf);
            process_data(data_buf, result_buf);
            write(fd_result, result_buf, strlen(result_buf));
        }
    }
    close(fd_data);
    close(fd_result);
}

四、%[^,]

#include <stdio.h>

char data_buf[] = ",Alice,lina,85,90,78";
char name1[50], name2[50];
int score[3];

int main() {
    // 使用 sscanf 解析数据
    sscanf(data_buf, ",%[^,],%[^,],%d,%d,%d", name1, name2, &score[0], &score[1], &score[2]);

    // 输出结果
    printf("%s,%s,%d,%d,%d\n", name1, name2, score[0], score[1], score[2]);

    return 0;
}

// 此时,name1 的值为 "Alice"
// name2 的值为 "lina"
// score[0] 的值为 85
// score[1] 的值为 90
// score[2] 的值为 78

//输出:Alice,lina,85,90,78

五、read_line函数

写法一:

因为行末\r\n,读到\n再退出,\r也写入buf

int read_line(int fd, char *buf)
{
    int len = 0;
    char c;
    int i = 0;
    while(1)
    {
        len = read(fd, &c, 1);
        if(len <= 0)
            return -1;
        else
        {
            if(c != '\n')
            {
                buf[i] = c;
                i++;
            }
            else
            {
                break;
            }
        }
    }
    buf[i] = '\0';
    return i;
}

 写法二:

读到\r,用lseek函数跳过\n字符

int read_line(int fd, char *buf)
{
    int len = 0;
    char c;
    int i = 0;
    while(1)
    {
        len = read(fd, &c, 1);
        if(len <= 0)
            return -1;
        else
        {
            if(c != '\r' )
            {
                buf[i] = c;
                i++;
            }
            else
            {
                lseek(fd, 1, SEEK_CUR); 
                break;
            }
        }
    }
    buf[i] = '\0';
    return i;
}

 写法三:

考虑是unix还是windows系统

\r就是"回到行首",\n就是"到下一行"

即:\r回车使光标到行首,\n换行使光标下移一格

通常用的Enter是两个加起来的,即\r\n

  • Unix系统里,每行结尾只有“<换行>”,即“\n”;
  • Windows系统里面,每行结尾是“<换行><回车>”,即“\n\r”;
  • Mac系统里,每行结尾是“<回车>”。
printf aaaa \r\n bbbbbb
print ccccc \n ddddddd
print eeeeeeeee \r ffffff

运行结果

aaaa
bbbbbb
ccccc
ddddddd
ffffff

 \r 就是return 回到本行行首,这就会把这一之前的输出覆盖掉,因此ffffff会覆盖eeeeeeeee
\n 是换行+回车,把光标先移到下一行,然后光标回到下一行的行首

代码具体实现 :

int read_line(int fd, char *buf)
{
    int len = 0;
    char c;
    int i = 0;
    while (1)
    {
        len = read(fd, &c, 1);
        if (len <= 0)
            return -1;
        else
        {
            if (c == '\r')
            {
                // 读取到 '\r',检查下一个字符是否是 '\n'
                len = read(fd, &c, 1);
                if (len > 0 && c != '\n')
                {
                    lseek(fd, -1, SEEK_CUR); // 如果不是 '\n',回退一个字节
                }
                break; // 行结束
            }
            else if (c == '\n')
            {
                // 读取到 '\n',行结束
                break;
            }
            buf[i] = c;
            i++;
        }
    }
    buf[i] = '\0';
    return i;
}

这个逻辑确保了无论是 Windows 风格的换行符(\r\n)还是 Unix 风格的换行符(\n),都能被正确处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值