• Linux 基本语句_7_C语言_文件I/O文件复制操作


    标准I/O与文件I/O的区别:

    在这里插入图片描述
    标准I/O每次调用函数写入字符,并不是直接将字符写入文件,而是先写入缓冲区

    文件I/O则是每次调用函数写入都会产生一次系统调用,Liunx必须从用户态切换至内核态,但过度频繁得执行系统调用会增加系统开销

    标准I/O库通常更易于使用,因为它提供了更高级的函数,适用于许多不同的数据类型,文件I/O更接近底层,因此提供的控制更有限,通常需要更多的手动管理。

    除此之外:

    文件I/O相较于前者有着更小得内存开销,且更快,能对设备文件调用(linux一切皆是文件)而前者不行

    文件I/O简介:

    若说标准I/O的核心是流指针(FILE)那么文件I/O核心就是文件描述符

    文件描述符:

    文件描述符是一个非负整数,它是一个索引值,并指向在内核中每个进程代开文件的记录表。当打开一个或创建一个新文件时,内核就会向进程返回一个文件描述符,读写文件时需要把相应文件描述符作为参数传递给相应函数

    进程:

    听歌和打游戏就是两个不同进程,操作系统可以有效地管理这些不同的进程并分配资源。

    I/O:

    也通俗的讲就是你向计算机获取文件信息或向计算机写入信息。

    在这里插入图片描述

    通俗讲0、1、2描述符已被系统提前占据,后续打开新文件描述符的取值为3、4…

    文件描述符作为有限资源,一个进程开启的最大文件描述符为1023,一个进程最多使用1024个文件描述符,这个值与文件,文件多少没有关系,已打开文件描述符个数有关

    朴素的讲一个进程能同时打开的文件数量是有限的

    操作:

    打开文件:

    在这里插入图片描述
    open函数有俩重载函数,其中下面一个函数的mode是为型创建文件设置权限用的例如0777就是权限全开,第一个0没有用,后续文件权限可以用chmod对新建文件修改权限

    在这里插入图片描述
    这些权限可以叠加,用|即可:

    在这里插入图片描述

    标准I/O用的是fopen函数,返回值是FILE指针流

    写文件:

    在这里插入图片描述

    ssize_t 是 C 和 C++ 编程语言中的一个数据类型,通常用于表示有符号的大小,主要用于 I/O 操作和函数返回值中。
    
    • 1

    在这里插入图片描述
    read函数中count是期望读取的字节数,实际读取字只会小于等于count,读取数据存入buf中

    void类型相当于java中的object

    read函数的返还值,很特别后续有大用,后续代码测试中再细致说明

    设置指针位置:

    在这里插入图片描述
    文件读取一般从文件开头开始读,读一字节再跳到下一字节,这个函数直接可以控制指针位置,可以控制读取文件的具体部分

    关闭文件:

    在这里插入图片描述
    linux内核会自动关闭打开文件,为什么还要用close?

    在Linux中,内核确实会在进程终止时自动关闭所有打开的文件。这是为了确保文件描述符不会被泄漏,从而释放与这些文件描述符相关的系统资源。

    然而,显式地使用close函数来关闭文件是一种良好的编程习惯,而不是依赖于操作系统的自动处理。这是因为有时你可能需要在进程执行期间显式关闭文件,而不是等到进程终止。如果你在程序中打开了大量文件,如果不关闭它们,可能会导致文件描述符用尽,从而阻止进程打开更多文件。

    另外,显式关闭文件可以帮助你更好地控制程序的文件访问,确保文件在不再需要时被及时释放,有助于避免资源泄漏问题。所以,尽管内核会在进程终止时关闭文件,但在实际编程中,最好养成显式关闭文件的好习惯。

    使用文件I/O实现文件复制:

    #include                                                                                         
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define N 10 //定义每次读取的长度
    
    int main(int argc, const char * argv[]){
        int origin;//源文件
        int destination;//目的文件
        ssize_t readnum;
        unsigned char buf[N];
        if((origin = open(argv[1], O_RDONLY)) < 0){//打开源文件
           printf("源文件打开错误\n");
           exit(1);
        }
        if((destination = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664)) > 0){//只写打开文件,不存在创建,否者清空
          lseek(destination, 0, SEEK_SET);//将指针制于文件首部
          
          while((readnum = read(origin, buf, N)) > 0){//若读取出数据
               write(destination, buf, N);//写入
          }
        }else{
           printf("目标文件打开错误\n");
           exit(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    效果:

    ./a.out main.c ccc.c
    
    • 1

    复制main.c到ccc.c文件可以看出:
    在这里插入图片描述
    ccc.c文件多出来一个trun 0

    在这里插入图片描述

    这个原因的产生和我们的写函数脱不了关系

     if((destination = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664)) > 0){
       lseek(destination, 0, SEEK_SET);//将指针制于文件首部
    
       while((readnum = read(origin, buf, N)) > 0){//若读取出数据
            write(destination, buf, N);//写入
       }
     }else{
        printf("目标文件打开错误\n");                                      
        exit(1);
     }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    write(destination, buf, N);//写入
    
    • 1

    问题出在写入函数写入的实际长度为N,若read函数没有读取到长度为N的字符串,那么write写入的时候剩下就就会补0这也就是turn 0产生的原因

    while((readnum = read(origin, buf, N)) > 0)
    
    • 1

    改进方法,讲N换成read函数的返回值即可,read在读取到一些字符串,并且刚好到达文件末尾仍会返回读取的字符串个数,而不是0,但当你再次调用想获取更多字符串的时候就会返回0

    修改代码:

    #include                                                                                         
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define N 10 //定义每次读取的长度
    
    int main(int argc, const char * argv[]){
        int origin;//源文件
        int destination;//目的文件
        ssize_t readnum;
        unsigned char buf[N];
        if((origin = open(argv[1], O_RDONLY)) < 0){//打开源文件
           printf("源文件打开错误\n");
           exit(1);
        }
        if((destination = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664)) > 0){//只写打开文件,不存在创建,否者清空
          lseek(destination, 0, SEEK_SET);//将指针制于文件首部
          
          while((readnum = read(origin, buf, N)) > 0){//若读取出数据
               write(destination, buf, readnum);//写入
          }
        }else{
           printf("目标文件打开错误\n");
           exit(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    效果:
    在这里插入图片描述

    复制文件后半部分:

    完整代码:

    #include       
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    #define N 10
    char buf[N];
    
    int main(int argc, const char * argv[]){
    	int origin;
    	int destination;
    	int readnum;
    	if(argc < 2){//检查文件数量 
    		printf("file number at least 2\n");
    		exit(1);
    	}
    	if((origin = open(argv[1], O_RDONLY)) < 0){
    		printf("open error\n");
    		exit(1);
    	}
    	if((destination = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664)) < 0){
    		printf("write error\n");
    		exit(1);
    	}
    	lseek(origin,  lseek(origin, 0, SEEK_END) / 2, SEEK_SET);
    	lseek(destination, 0, SEEK_SET);
    	
    	while((readnum = read(origin, buf, N)) > 0){
    		write(destination, buf, readnum);
    	}
    
    	return 0;
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    注意:

    lseek(origin,  lseek(origin, 0, SEEK_END) / 2, SEEK_SET);
    
    • 1

    将其更改为:

    lseek(origin,  0, SEEK_END / 2);
    
    • 1

    上述更改是不行的,因为SEEkK_END值为2除以二变为1与SEEK_CUR操作等效即移动文件当前位置

    lseek返回值为指针相对于初始位置的大小,理应根据这一特性,找到文件长度,再根据将指针调制文件首部,偏移量为文件长度的一半即可让指针在文件中间

  • 相关阅读:
    为了代码简洁性,引起值类型和引用类型赋值的错误说明
    《最新出炉》系列初窥篇-Python+Playwright自动化测试-16-处理模态对话框弹窗
    PostgreSQL数据库统计信息——acquire_inherited_sample_rows采样函数
    【C++】enum class 域化枚举
    复旦-华盛顿大学EMBA科创的奥E丨《神奇的材料》与被塑造的我们
    开学季征文 | 一位开发实习生的真情流露
    某车联网App 通讯协议加密分析(三) Trace Block
    程序员的数学课11 灰度实验:如何设计灰度实验并计算实验的收益?
    Spark 单机和集群环境部署教程
    PyQt5桌面应用启动模板
  • 原文地址:https://blog.csdn.net/xyint/article/details/134077052