Linux_fileio学习

news2025/1/11 17:09:31

参考韦东山老师教程:https://www.bilibili.com/video/BV1kk4y117Tu?p=12

目录

    • 1. 文件IO函数分类
    • 2. 函数原型
      • 2.1 系统调用接口
      • 2.2 标准IO接口
    • 3. fileio内部机制
      • 3.1 系统调用接口内部流程
      • 3.1 dup函数使用
      • 3.2 dup2函数使用
    • 4. open file
      • 4.1 open实例
      • 4.2 open函数分析
    • 5. create file
      • 5.1 create实例
      • 5.2 create分析
    • 6. write file
      • 6.1 write实例
      • 6.2 write分析
    • 7. read file
      • 7.1 read实例
      • 7.2 read分析
    • 8.简单实例——处理.csv表格

1. 文件IO函数分类

在 Linux 上操作文件时,有两套函数:标准 IO、系统调用 IO。
标准 IO 的相关函数是:fopen/fread/fwrite/fseek/fflush/fclose 等 。
系统调用 IO 的相关函数是:open/read/write/lseek/fsync/close。
这 2 种 IO 函数的差别如下图所示:

在这里插入图片描述

2. 函数原型

2.1 系统调用接口

open/read/write/lseek/fsync/close 这几个函数的用法:

函数名函数原型描述
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);
作用:打开或者创建一个文件
pathnam:文件路径名,或者文件名
flags:表示打开文件所采用的操作
O_RDONLY:只读模式
O_WRONLY:只写模式
O_RDWR:可读可写
O_APPEND 表示追加,如果原来文件里面有内容,则这次写入会写在文件的最末尾
O_CREAT 表示如果指定文件不存在,则创建这个文件
O_EXCL 表示如果要创建的文件已存在,则出错,同时返回 -1,并且修改 errno 的值
O_TRUNC 表示截断,如果文件存在,并且以只写、读写方式打开,则将其长度截断为 0
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O 设置为非阻塞模式(nonblocking mode)
O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新
O_RSYNC read 等待所有写入同一区域的写操作完成后再进行
O_SYNC 等待物理 I/O 结束后再 write,包括更新文件属性的I/O
mode: 文件访问权限的初始值
read#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
作用:从给定的文件描述符指定的文件中,读取 count 个字节的数据,存放至 buf 中。
fd:指定要读写的文件描述符
buf:缓冲区,一般是一个数组,用于存放读取的内容
count:一次要读取的最大字节数
write#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
作用:将 buf 中的 count 字节数据写入指定文件描述符的文件中
fd:指定要写入的文件描述符
buf:缓冲区,一般是一个数组,读取存放于该数组的内容存放于文件中
count:要写入的实际字节数
dup
dup2
dup3
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
int dup3(int oldfd, int newfd, int flags);
作用:复制文件描述符,就是两个文件句柄指向同一个文件描述符,这两个文件句柄共享文件偏移地址、状态
oldfd:被复制的文件句柄
newfd:复制得到的文件句柄
注意:dup 函数返回的文件句柄是“未使用的最小文件句柄”,dup2 可以指定复制得到的文件句柄为 newfd,dup3 跟 dup2 类似,flags 参数必要么是 0,要么是 O_CLOEXEC
lseek#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
作用:重新定位读/写文件偏移
fd:指定要偏移的文件描述符
offset:文件偏移量
whence:开始添加偏移 offset 的位置
SEEK_SET,offset 相对于文件开头进行偏移
SEEK_CUR,offset 相对文件当前位置进行偏移
SEEK_END,offset 相对于文件末尾进行偏移
fsync#include <unistd.h>
int fsync(int fd);
作用:同步内存中所有已修改的文件数据到储存设备
fd:指定要同步的文件描述符
close#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int close(int fd);
作用:关闭已打开的文件

2.2 标准IO接口

fopen/fread/fwrite/fseek/fflush/fclose 这几个函数的用法:

函数名函数原型描述
fopen#include<stdio.h>
FILE *fopen(const char *filename, const char *mode);
作用:打开或者创建一个文件
filename:文件路径名,或者文件名
mode:文件的访问模式
r 打开一个用于读取的文件。该文件必须存在。
w 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
a 追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
r+ 打开一个用于更新的文件,可读取也可写入。该文件必须存在。
w+ 创建一个用于读写的空文件。
a+ 打开一个用于读取和追加的文件。
fread#include<stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
作用:从给定流 stream 读取数据到 ptr 所指向的数组中
ptr:指向带有最小尺寸 size*nmemb 字节的内存块的指针
size:这是要读取的每个元素的大小,以字节为单位
nmemb:元素的个数,每个元素的大小为 size 字节
stream:这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流
fwrite#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
作用:把 ptr 所指向的数组中的数据写入到给定流 stream 中
ptr:这是指向要被写入的元素数组的指针
size:这是要被写入的每个元素的大小,以字节为单位
nmemb:这是元素的个数,每个元素的大小为 size 字节
stream:这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流
fseek#include <stdio.h>
int fseek(FILE *stream, long int offset, int whence);
作用:设置流 stream 的文件位置为给定的偏移 offset
stream:这是指向 FILE 对象的指针,该 FILE 对象标识了流
offset:这是相对 whence 的偏移量,以字节为单位
whence:表示开始添加偏移 offset 的位置
SEEK_SET,offset 相对于文件开头进行偏移
SEEK_CUR,offset 相对文件当前位置进行偏移
SEEK_END,offset 相对于文件末尾进行偏移
fflush#include <stdio.h>
int fflush(FILE *stream);
作用:刷新流 stream 的输出缓冲区
stream:这是指向 FILE 对象的指针,该 FILE 对象指定了一个缓冲流
fclose#include <stdio.h>
int fclose(FILE *stream);
作用:关闭流 stream,且刷新其缓冲区
stream:这是指向 FILE 对象的指针,该 FILE 对象指定了要被关闭的流

3. fileio内部机制

3.1 系统调用接口内部流程

在Linux操作系统中,用户态(User Mode)和内核态(Kernel Mode)是两种不同的处理器运行模式,它们代表了程序执行时的不同权限级别和对系统资源访问的能力。

用户态(User Mode)

  • 应用程序运行的正常状态。在用户态下,进程可以执行的指令受到限制,主要目的是保护系统资源和保持系统稳定性。应用程序不能直接访问硬件资源(如内存、CPU寄存器、I/O设备等)或者执行特权指令。
  • 用户态下的进程运行在较低的CPU特权级别(通常是Ring 3),这意味着它们没有直接操作硬件的权限。
  • 当应用程序需要执行某些特权操作(如读写文件、网络通信、分配内存等)时,它必须通过系统调用(system call)向操作系统内核发出请求,这时会从用户态转换到内核态。

内核态(Kernel Mode)

  • 操作系统内核运行的状态,拥有最高权限。内核可以直接访问所有硬件资源,执行任意指令,并且可以改变处理器状态。
  • 在内核态下,进程运行在最高的CPU特权级别(Ring 0),享有对所有系统资源的完全控制权。
  • 内核态负责管理硬件资源、调度进程、处理中断和系统调用等核心功能。当系统调用发生时,处理器从用户态切换到内核态,执行内核代码完成请求的服务,然后返回用户态继续执行应用程序。
  • 在内核态下,还有一种特殊的上下文称为“中断上下文”,这是当硬件中断发生时,CPU暂停当前任务,转而执行中断处理程序的状态。

简而言之,用户态保证了应用程序的安全隔离,而内核态则提供了对系统资源的直接访问和管理能力,两者之间的转换是操作系统管理和控制资源的关键机制。

fileio也是一样,系统调用接口open/read/write/lseek/fsync/close就是用户态应用程序能够调用的函数,这些接口是由GLIBC(GNU C Library)提供的,用来访问Linux的内核服务。

GLIBC提供的open/read/write/lseek/fsync/close函数访问Linux的步骤:

  • 当使用open/read/write/lseek/fsync/close函数时,会设置异常原因,如open、read等,再调用汇编指令swi/svc来触发一个异常,并携带异常原因;
  • CPU发现异常后会分辨异常原因,(假设是open函数)在sys_call_table[NR_open]中分辨异常原因后,会在fs/open.c文件中调用SYSCALL_DEFINE3即sys_open函数执行open文件操作。

在这里插入图片描述

其中

ABI(Application Binary Interface):应用二进制接口

OABI(Old ABI):旧的应用二进制接口

EABI(Embedded ABI):“E”代表“Embedded”,表示ARM嵌入式系统设计的一种新的ABI规范

sys_call_table的函数指针数组:

在这里插入图片描述

fs/open.c中SYSCALL_DEFINE3函数

在这里插入图片描述

3.1 dup函数使用

dup函数使用代码:

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


int main(int argc, char **argv)
{
    char buf[10];
    char buf2[10];
    
    if (argc != 2)
    {
        printf("Usage: %s <file>\n", argv[0]);
        return -1;
    }

    int fd = open(argv[1], O_RDONLY);
    int fd2 = open(argv[1], O_RDONLY);
    int fd3 = dup(fd);

    printf("fd = %d\n", fd);
    printf("fd2 = %d\n", fd2);
    printf("fd3 = %d\n", fd3);

    if (fd < 0 || fd2 < 0 || fd3 < 0)
    {
        printf("can not open %s\n", argv[1]);
        return -1;
    }

    read(fd, buf, 1);
    read(fd2, buf2, 1);

    printf("data get from fd : %c\n", buf[0]);
    printf("data get from fd2: %c\n", buf2[0]);
    
    read(fd3, buf, 1);
    printf("data get from fd3: %c\n", buf[0]);
    
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

3.2 dup2函数使用

dup2函数使用代码:

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


/*
 * ./dup2 1.txt
 * argc    = 2
 * argv[0] = "./dup2"
 * argv[1] = "1.txt"
 */

int main(int argc, char **argv)
{
    int fd;
    
    if (argc != 2)
    {
        printf("Usage: %s <file>\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        printf("errno = %d\n", errno);
        printf("err: %s\n", strerror(errno));
        perror("open");
    }
    else
    {
        printf("fd = %d\n", fd);
    }

    /* fd=1的文件是系统标准输出文件,如printf*/
    dup2(fd, 1);

    printf("hello, world\n");  /* 打印到fd=1的文件    */
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

4. open file

4.1 open实例

open代码:

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

/*
 * ./open 1.txt
 * argc    = 2
 * argv[0] = "./open"
 * argv[1] = "1.txt"
 */

int main(int argc, char **argv)
{
    int fd;
    
    if (argc != 2)
    {
        printf("Usage: %s <file>\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        printf("errno = %d\n", errno);
        printf("err: %s\n", strerror(errno));
        perror("open");
    }
    else
    {
        printf("fd = %d\n", fd);
    }

    while (1)
    {
        sleep(10);
    }

    close(fd);
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

补充:error变量、strerror函数、perror函数

errno:errno h头文件定义了整数变量,它在系统调用和一些库函数在发生错误时被设置,以指示出错的地方。

在这里插入图片描述

在man errno中能够找到对应的错误码:

在这里插入图片描述

strerror:函数 char *strerror(int errnum); 指向errnum对应的字符串,使用 printf(“err: %s\n”, strerror(errno)); 就能打印出对应的错误码含义:

在这里插入图片描述

perror:函数 void perror(const char *s); 打印参数字符串s,后跟冒号和空白,然后显示一条与当前值oferrno相对应的错误消息和一行新行:

在这里插入图片描述

4.2 open函数分析

使用编写的open应用程序在后台分别打开2个文件,发现获得的文件句柄fd都是3,查看2个后台正在运行的程序进程,发现2个进程都有4个文件句柄,其中fd 0表示系统标准输入(stdin),例如scanffd 1表示系统标准输出(stdout),例如printffd 2表示标准错误(stderr),存放错误信息;fd 3是应用程序刚刚打开的文件句柄:

在这里插入图片描述

由此可知,先后2次使用open函数在后台打开文件,每次执行open时分配一个进程号,2次open属于不同的进程,所以2次open文件返回的文件句柄都是3并不冲突。

在open的执行过程中,文件句柄fd是如何与进程号关联起来的?需要根据代码进行分析:

由3.1章节可知,调用open函数时最终调用到fs/open.c中do_sys_open内核函数来打开文件:

在这里插入图片描述

继续分析fd_install函数:

在这里插入图片描述

其中current结构体为什么是task_struct结构体还需要后续再分析。

查看task_struct结构体,在task_struct结构体中有files_struct结构体:

在这里插入图片描述

files_struct结构体中会有一个fdtab:

在这里插入图片描述

fdtable结构体中会有file结构体,保存文件句柄fd:

在这里插入图片描述

因此对于每次执行的open文件操作都会创建一个task_struct结构体,在对应的fdtable中会保存此进程打开的文件句柄fd:

在这里插入图片描述

在fdtable的file结构体中会有一个f_pos保存当前偏移位置,在调用lseek、read、write 都会更新f_pos的位置:

在这里插入图片描述

5. create file

5.1 create实例

create代码:

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

/*
 * ./create 1.txt
 * argc    = 2
 * argv[0] = "./open"
 * argv[1] = "1.txt"
 */

int main(int argc, char **argv)
{
    int fd;
    
    if (argc != 2)
    {
        printf("Usage: %s <file>\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        printf("errno = %d\n", errno);
        printf("err: %s\n", strerror(errno));
        perror("open");
    }
    else
    {
        printf("fd = %d\n", fd);
    }

    while (1)
    {
        sleep(10);
    }

    close(fd);
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

5.2 create分析

“open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);”参考2.1章节系统调用接口中open描述,第2个参数是以可读可写、若文件不存在则创建文件、截取文件的方式open文件,第3个参数是open的文件权限是777,但是创建的文件2.txt权限并不是777,而是775,原因是系统的umask是2,所以其他用户的写权限是默认被关闭的

在这里插入图片描述

在这里插入图片描述

create是使用open函数实现的,在do_sys_open中调用build_open_flags打开文件是会使用到flags和mode参数:

在这里插入图片描述

操作与open函数基本相同。

6. write file

6.1 write实例

write代码:

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

/*
 * ./write 1.txt  str1 str2
 * argc    = 2
 * argv[0] = "./open"
 * argv[1] = "1.txt"
 */

int main(int argc, char **argv)
{
    int fd;
    int i;
    int len;
    
    if (argc < 3)
    {
        printf("Usage: %s <file> <string1> <string2> ...\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        printf("errno = %d\n", errno);
        printf("err: %s\n", strerror(errno));
        perror("open");
    }
    else
    {
        printf("fd = %d\n", fd);
    }

    for (i = 2; i < argc; i++)
    {
        len = write(fd, argv[i], strlen(argv[i]));
        if (len != strlen(argv[i]))
        {
            perror("write");
            break;
        }
        write(fd, "\r\n", 2);
    }

    close(fd);
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

write_in_pos代码:

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

/*
 * ./write 1.txt  str1 str2
 * argc    = 2
 * argv[0] = "./open"
 * argv[1] = "1.txt"
 */

int main(int argc, char **argv)
{
    int fd;
    int i;
    int len;
    
    if (argc != 2)
    {
        printf("Usage: %s <file>\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDWR | O_CREAT, 0644);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        printf("errno = %d\n", errno);
        printf("err: %s\n", strerror(errno));
        perror("open");
    }
    else
    {
        printf("fd = %d\n", fd);
    }

    printf("lseek to offset 3 from file head\n");
    lseek(fd, 3, SEEK_SET);

    write(fd, "123", 3);

    close(fd);
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

6.2 write分析

搜索SYSCALL_DEFINE3调用write的函数:

在这里插入图片描述

分析SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count)函数过程:

在这里插入图片描述

7. read file

7.1 read实例

read代码:

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

/*
 * ./read 1.txt
 * argc    = 2
 * argv[0] = "./read"
 * argv[1] = "1.txt"
 */

int main(int argc, char **argv)
{
    int fd;
    int i;
    int len;
    unsigned char buf[100];
    
    if (argc != 2)
    {
        printf("Usage: %s <file>\n", argv[0]);
        return -1;
    }

    fd = open(argv[1], O_RDONLY);
    if (fd < 0)
    {
        printf("can not open file %s\n", argv[1]);
        printf("errno = %d\n", errno);
        printf("err: %s\n", strerror(errno));
        perror("open");
    }
    else
    {
        printf("fd = %d\n", fd);
    }

    /* 读文件/打印 */
    while (1)
    {
        len = read(fd, buf, sizeof(buf)-1);
        if (len < 0)
        {
            perror("read");
            close(fd);
            return -1;
        }
        else if (len == 0)
        {
            break;
        }
        else
        {
            /* buf[0], buf[1], ..., buf[len-1] 含有读出的数据
             * buf[len] = '\0'
             */
            buf[len] = '\0';
            printf("%s", buf);
        }
    }

    close(fd);
    return 0;
}

上传到Ubuntu后编译运行:

在这里插入图片描述

7.2 read分析

搜索SYSCALL_DEFINE3调用read的函数:

在这里插入图片描述

分析SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count)函数过程:

在这里插入图片描述

8.简单实例——处理.csv表格

背景描述:创建一个score.csv表格,文件中包含张三、李四、王五同学的语文、数学、英语分数。

处理步骤:

  • 打开score.csv表格
  • 通过score.csv表格中的分数计算出每位同学的总分并对分数进行评价,最终结果输出到指定的表格文件(result.csv)中(评价标准:3科总分>=270为A+等级、3科总分>=240为A等级、其余成绩为B等级)

在这里插入图片描述

补充:使用notepad++打开score.csv表格,每一列之间用’,'隔开,每一行之间用回车换行隔开

在这里插入图片描述

处理score.csv表格数据时需要先读取score.csv表格中的数据到一个buf中,此时可以用回车换(0x0d和0x0a)作为读取每一行数据的结束标志,参考函数read_line()。

代码:

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


/* 返回值: n表示读到了一行数据的数据个数(n >= 0)
 *         -1(读到文件尾部或者出错)
 */
static int read_line(int fd, unsigned char *buf)
{
    /* 循环读入一个字符 */

    /* 如何判断已经读完一行? 读到0x0d, 0x0a */

    unsigned char c;
    int len;
    int i = 0;
    int err = 0;

    while (1)
    {
        len = read(fd, &c, 1);
        if (len <= 0)
        {
            err = -1;
            break;
        }
        else
        {
            if (c != '\n' && c != '\r')
            {
                buf[i] = c;
                i++;
            }
            else
            {
                /* 碰到回车换行   */
                err = 0;
                break;
            }
        }
    }

    buf[i] = '\0';

    if (err && (i == 0))
    {
        /* 读到文件尾部了并且一个数据都没有读进来 */
        return -1;
    }
    else
    {
        return i;
    }
}


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 */
    {
        strcpy(result_buf, data_buf);
    }
    else
    {
        sscanf(data_buf, "%[^,],%d,%d,%d,", name, &scores[0], &scores[1], &scores[2]);
        //printf("result: %s,%d,%d,%d\n\r", name, scores[0], scores[1], scores[2]);
        //printf("result: %s --->get name---> %s\n\r", data_buf, name);
        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]);
        //printf("result: %s", result_buf);
    }
}

/*
 * ./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 i;
    int len;
    unsigned char data_buf[1000];
    unsigned char result_buf[1000];
    
    if (argc != 3)
    {
        printf("Usage: %s <data csv file> <result csv file>\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);
    }

    fd_result = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd_result < 0)
    {
        printf("can not create file %s\n", argv[2]);
        perror("create");
        return -1;
    }
    else
    {
        printf("resultfile fd = %d\n", fd_result);
    }


    while (1)
    {
        /* 从数据文件里读取1行 */
        len = read_line(fd_data, data_buf);
        if (len == -1)
        {
            break;
        }

        //if (len != 0)
        //    printf("line: %s\n\r", data_buf);

        if (len != 0)
        {
            /* 处理数据 */
            process_data(data_buf, result_buf);
            
            /* 写入结果文件 */
            //write_data(fd_result, result_buf);
            write(fd_result, result_buf, strlen(result_buf));

            write(fd_result, "\r\n", 2);
        }

    }

    close(fd_data);
    close(fd_result);

    return 0;
}

代码及score.csv文件上传到ubuntu后运行效果:

在这里插入图片描述

将result.csv文件传回到windows打开:

在这里插入图片描述

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

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

相关文章

代码查重软件-自力更生

为了减轻工作量&#xff0c;自研了简单实用的代码查重工具&#xff0c;可以对若干文件之间进行查重。通过调试&#xff0c;相似度大于80%的没有一个是冤枉的。好用。去掉雷同的&#xff0c;其他的代码再慢慢看。

华为手机怎么打印文件?

关于华为手机打印的问题&#xff0c;如果您有打印机&#xff0c;并且已经成功和华为手机相连&#xff0c;在解决上就要容易很多。 具体操作如下&#xff1a; 选择文件 文件来源&#xff1a;华为手机上的文件可以来自多个应用&#xff0c;如图库、备忘录、文件管理等&#xf…

晨持绪科技:新手开抖音网店大概多久能做起来

抖音&#xff0c;这个汇聚亿万目光的舞台&#xff0c;早已成为电商的新战场。新手上路&#xff0c;开一间属于自己的抖音网店&#xff0c;大概需要多久能做起来?这个问题如同询问春日里的花蕾&#xff0c;何时绽放?答案藏于不断努力与摸索的过程之中。 "万事开头难"…

Oracle 集群的守护进程

ohas&#xff1a;主要用于守护cluster ware进程&#xff0c;在单节点建立集群的时候&#xff0c;没有crs&#xff0c;只有ohas、cluster ware GPnP&#xff1a;管理clusterware的配置信息&#xff0c;放在本地磁盘上 crs&#xff1a;管理clusterware中的资源&#xff0c;数据库…

大数据期末复习——hadoop、hive等基础知识

一、题型分析 1、Hadoop环境搭建 2、hadoop的三大组件 HDFS&#xff1a;NameNode&#xff0c;DataNode&#xff0c;SecondaryNameNode YARN&#xff1a;ResourceManager&#xff0c;NodeManager &#xff08;Yarn的工作原理&#xff09; MapReduce&#xff1a;Map&#xff0…

STM32mp157aaa按键中断实验

效果图&#xff1a; 源码&#xff1a; #include "key.h" void hal_key1_rcc_gpio_init() {// 使能GPIOF组RCC->MP_AHB4ENSETR | (0x1 << 5);// 设置引脚位输入模式GPIOF->MODER & (~(0X3 << 18));GPIOF->MODER & (~(0X3 << 16))…

2025年U.S.News世界大学排名前200榜单

近日&#xff0c;U.S. News公布了2025全球最佳院校排名&#xff0c;作为公认的四大世界高校排行榜&#xff0c;该排名主要围绕着学术声誉、学术成果等&#xff0c;因此备受访问学者、联合培养博士生及博士后申请者们青睐&#xff0c;知识人网小编特作介绍并发布排名前200的榜单…

CentOS 7 搭建rsyslog日志服务器

CentOS 7 搭建rsyslog日志服务器 前言一、IP地址及主机名称规划1.修改主机名 二、配置rsyslog日志服务器1.安装rsyslog服务2.编辑/etc/rsyslog.conf 文件3.启动并启用rsyslog服务4.验证端口是否侦听 三、在rsyslog日志服务器上配置firewalld防火墙四、配置rsyslog日志客户端1.编…

DSPy:变革式大模型应用开发

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径&#xff1a;AI代理工作流大模型应用开发实用开源项目汇总大模…

Git使用——首次创建本地仓库、配置、初始化、关联远程仓库

1、安装 Git软件 官网&#xff1a;git-scm.com 有时候官网打不开&#xff0c;这里留存个之前下载过的安装包&#xff1a; https://download.csdn.net/download/weixin_43908355/89502977 2、配置本地仓库 在准备建仓库的文件夹里&#xff0c;右键点击&#xff1a;Git Bash …

把 AI 人机炼成高玩,游戏 AI 技术实践指南,码住!

今天&#xff0c;为大家深入浅出地讲明白上亚运的经典 IP《梦三国 2》&#xff0c;到底应用了哪些来自网易数智的 AI 黑科技。看完你就会觉得&#xff1a;原来做 AI&#xff0c;我也行&#xff01; 方案概述 游戏作为 AI 落地最佳的试验田&#xff0c;近年来已经产生了多个极具…

maven构建断网springboot

maven构建断网springboot 我的依赖仓库&#xff08;本地电脑&#xff0c;记住常用的那几个&#xff09;org.springframework.boot下的 spring-boot-starter-parent spring-boot-starter-web /Users/lin/Documents/repo 使用maven进行创建空白项目 在pom.xml中补全parent类…

【文档+源码+调试讲解】科研经费管理系统

目 录 目 录 摘 要 ABSTRACT 1 绪论 1.1 课题背景 1.2 研究现状 1.3 研究内容 2 系统开发环境 2.1 vue技术 2.2 JAVA技术 2.3 MYSQL数据库 2.4 B/S结构 2.5 SSM框架技术 3 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 操作可行性 3.1.3 经济可行性 3.1…

51单片机嵌入式开发:STC89C52操作GPIO口LED灯

STC89C52操作GPIO口LED灯 1 芯片介绍1.1 芯片类型1.2 芯片系列说明 2 GPIO引脚寄存器说明3 GPIO操作3.1 GPIO输入3.2 GPIO输出3.3 GPIO流水灯3.4 Protues仿真 4 总结 1 芯片介绍 1.1 芯片类型 芯片采用宏晶科技品牌下的STC89C52RC单片机 选择STC89C52RC系列STC89C58RD系列单片…

echarts的折线图实现部分虚线部分实线

场景&#xff1a; 折线图一般都是实线为准&#xff0c;但是由于最后一个数据是预测。所以想要实现最后一段为虚线。 效果图&#xff1a; 具体实现&#xff1a; series:[{name: "销售总金额",type: "line",smooth: true,barWidth: 10,stack: Total,itemSty…

【机器学习】人工智能与气候变化:利用深度学习与机器学习算法预测和缓解环境影响

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f525;引言 1.1 背景介绍 1.2 人工智能与机器学习的崛起 1.3 本文内容概述 &#x1f528;气候变化的挑战 2.1 现今气候变化带来的影响和挑战 2.2 引发关注的气候变化趋势和数据 &#x1f916;人工智能…

甘肃香酥可口的烤花卷:味蕾的新宠

在美食的世界里&#xff0c;总有一些创新的美味能够让人眼前一亮&#xff0c;烤花卷便是其中之一。烤花卷&#xff0c;这甘肃一独特的美食&#xff0c;将传统花卷的柔软与烤制的香脆完美结合&#xff0c;为我们的味蕾带来了全新的体验。从外观上看&#xff0c;烤花卷呈现出诱人…

人脉社群平台微信小程序系统源码

&#x1f31f;【解锁人脉新纪元&#xff1a;探索人脉社群平台小程序】&#x1f31f; &#x1f680;【开篇&#xff1a;为什么我们需要人脉社群平台小程序&#xff1f;】&#x1f680; 在这个快节奏的时代&#xff0c;人脉不再是简单的名片交换&#xff0c;而是通往成功与机遇…

Elasticsearch:Runtime fields - 运行时字段(一)

运行时字段&#xff08;runtime fields&#xff09;是在查询时计算的字段。运行时字段使你能够&#xff1a; 向现有文档添加字段而无需重新索引数据开始处理数据而无需了解其结构在查询时覆盖索引字段返回的值定义用于特定用途的字段而无需修改底层架构 你可以像访问其他任何…

d3dcompiler_47.dll缺失怎么修复?d3dcompiler_47.dll修复使用说明

d3dcompiler_47.dll是一个重要的系统文件&#xff0c;属于MicrosoftWindows操作系统中Direct3D的一部分&#xff0c;它主要负责处理在Windows上运行的应用程序和游戏中的3D图形编程。这个DLL文件是“DirectX”的一项组成部分&#xff0c;DirectX是一套核心技术&#xff0c;用于…