【Linux】文件操作

[复制链接]
发表于 2025-6-16 04:58:34 | 显示全部楼层 |阅读模式
目录

一、知识铺垫
二、回首一下之前C语言的文件操作,并对比重定向
1、w选项与输出重定向(>)
2、a选项与追加重定向(>>)
3、熟悉一下读写操作
4、练习读操作:
三、什么叫做当前路径?当前路径与文件创建的关系?
四、访问文件的系统调用
1、步调默认打开的文件流
​编辑
2、常见的读写函数:
3、open系统调用:
4、一个小细节:用位图传参:
5、open参数解析:
6、写文件的系统调用:write
7、关闭文件的系统调用:close
五、文件形貌符(fd)
1、认识文件形貌符
2、文件形貌符具体是什么?为什么后续访问文件的系统调用都要通过fd来操作?
3、一切皆文件
4、文件形貌符表的分配规则以及利用规则实现重定向
(1)文件形貌符表的分配规则
(2)改变重定向的系统调用(dup2)
(3)dup2利用场景
5、给自界说shell增长重定向功能
六、缓冲区题目:
1、简单先容
2、为什么利用缓冲区能提高服从?
3、缓冲区在哪里?
4、用代码证实缓冲区的存在
七、模拟实现文件操作的常用接口(有缓冲区和无缓冲区版本
1、无缓冲区版本
mystdio.h
mystdio.c
filetest.c
2、有缓冲区版本
mystdio.c
mystdio.h
filetest.c

   前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,不由得分享一下给大家
  点击跳转到网站
  一、知识铺垫

   (1)文件 = 内容 + 属性
  (2)访问文件之前,都得先打开该文件。修改文件都是通过实行代码的方式完成修改。
  (3)打开文件条件要文件必须加载到内存中
  (4)由谁打开文件?历程打开文件。
  (5)一个历程可以打开多少文件?可以打开多个文件
  (6)一定时间内,系统中会存在多个历程,也大概同时存在更多的被打开文件,OS要不要管理多个被历程打开的文件呐?答案是肯定要管理的。如何管理呐?答案是先形貌在构造。
  (所以内核中一定要有形貌被打开文件的结构体,并用其界说对象)。
  (7)历程和文件的关系:结构体之间的关系,struct task_struct 和 struct XXX
  (8)系统中是不是所有的文件都被历程打开了?答案是:并不是,那些没有被打开的文件是被存储磁盘中的,所以也叫磁盘文件。
  二、回首一下之前C语言的文件操作,并对比重定向

  
  1. #include<stdio.h>
  2. int main()
  3. {
  4.     FILE *fp = fopen("./log.txt","w");
  5.     if(fp == NULL)
  6.     {
  7.         perror("fopen");
  8.         return 1;
  9.     }
  10.     const char* str = "hello file\n";
  11.     fputs(str,fp);
  12.     fclose(fp);
  13.     return 0;
  14. }
复制代码

  1、w选项与输出重定向(>)

   以" w "选项打开文件,是对文件进行写操作,但是打开前会将文件原有内容清空。而重定向(" > ")也是会将文件原有内容清空,由于重定向之前需要将文件打开,而打开这个操作就会将文件内容清空:
  

  2、a选项与追加重定向(>>)

   fopen以"a"选项打开文件,是追加的方式进行写,即在文件原有内容的末尾接着写,这与追加重定向功能一样:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.     FILE *fp = fopen("./log.txt","a");
  5.     if(fp == NULL)
  6.     {
  7.         perror("fopen");
  8.         return 1;
  9.     }
  10.     const char* str = "hello file\n";
  11.     fputs(str,fp);
  12.     fclose(fp);
  13.     return 0;
  14. }
复制代码

  

  还有其他选项可以查手册。
  3、熟悉一下读写操作

  
  1. #include<stdio.h>
  2. #include<string.h>
  3. #define FILENAME "log.txt"
  4. //练习读写操作
  5. int main()
  6. {
  7.     FILE* fp = fopen(FILENAME,"w");
  8.     if(fp == NULL)
  9.     {
  10.         perror("fopen");
  11.         return 1;
  12.     }
  13.     const char* msg = "hello HF";
  14.     int cnt = 6;
  15.     while(cnt)
  16.     {
  17.         int n = fwrite(msg,strlen(msg),1,fp);
  18.         printf("write %d block\n",n);
  19.         cnt--;
  20.     }
  21.     fclose(fp);
  22.     return 0;
  23. }
复制代码

  4、练习读操作:

  
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #define FILENAME "log.txt"
  6. //练习读
  7. int main()
  8. {
  9.     FILE* fp = fopen(FILENAME,"r");
  10.     if(fp == NULL)
  11.     {
  12.         perror("fopen");
  13.         return 1;
  14.     }
  15.     char buffer[64];
  16.     while(1)
  17.     {
  18.         char* r = fgets(buffer,sizeof(buffer),fp);
  19.         if(!r) break;//返回NULL则终止读
  20.         printf("%s\n",buffer);
  21.     }
  22.     return 0;
  23. }
复制代码

  三、什么叫做当前路径?当前路径与文件创建的关系?

   当前路径指历程启动时地点的工作目录。历程启动时,会自动记录自己启动时的地点的目录,可通过指令查看:(ls  /proc/历程pid -l)
  

    当前路径与文件创建的关系:从前都以为文件是默认创建在可实行步调的同级目录,实则不然,文件是默认创建在历程的工作目录下。
  如果我们在创建文件之前,修改历程的工作目录,那么文件也会创建到修改后的工作目录下:
  修改历程的工作目录的接口:

  参数就是要修改的工作目录。
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #define FILENAME "HF.txt"
  6. //修改进程的工作目录
  7. int main()
  8. {
  9.     int i = chdir("/root/study/linux-learning");
  10.     if(i)
  11.     {
  12.         printf("转换失败\n");
  13.     }
  14.     FILE* fp = fopen(FILENAME,"w");
  15.     if(fp == NULL)
  16.     {
  17.         perror("fopen");
  18.         return 1;
  19.     }
  20.     const char* msg = "hello HF";
  21.     int cnt = 6;
  22.     while(cnt)
  23.     {
  24.         int n = fwrite(msg,strlen(msg),1,fp);
  25.         printf("write %d block\n",n);
  26.         cnt--;
  27.     }
  28.     fclose(fp);
  29.     return 0;
  30. }
复制代码

  四、访问文件的系统调用

1、步调默认打开的文件流

   


  2、常见的读写函数:

  
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #define FILENAME "log.txt"
  6. //常见的写函数
  7. int main()
  8. {
  9.     printf("hello printf\n");
  10.     fprintf(stdout,"hello fprintf\n");//将数据输出到标准输出中(stdout显示器设备);
  11.     fputs("hello fputs\n",stdout);//也是将数据输出到标准输出中,但不能像fprintf那样支持格式化输出
  12.     const char* msg = "hello fwrite\n";
  13.     fwrite(msg,1,strlen(msg),stdout);
  14.     return 0;
  15. }
复制代码
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #define FILENAME "log.txt"
  6. int main()
  7. {
  8.     //fscanf
  9.     char buffer[64];
  10.     fscanf(stdin,"%s",buffer);//从标准输出(键盘)中读取数据放到buffer中,空格和换行符作为分隔符
  11.     printf("%s\n",buffer);
  12.     return 0;
  13. }
复制代码
3、open系统调用:

   访问文件不仅仅有C语言的文件接口,OS还必须提供对应的访问文件的系统调用,就是open系列的系统调用:
  

  4、一个小细节:用位图传参:

  
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #define FILENAME "log.txt"
  6. //用位图传参
  7. #define ONE 1
  8. #define TWO (1<<1)
  9. #define THREE (1<<2)
  10. #define FOUR (1<<3)
  11. #define FIVE (1<<4)
  12. void myPrint(int flag)
  13. {
  14.     if(flag & ONE) printf("1");
  15.     if(flag & TWO) printf("2");
  16.     if(flag & THREE) printf("3");
  17.     if(flag & FOUR) printf("4");
  18.     if(flag & FIVE) printf("5");
  19.     printf("\n");
  20. }
  21. int main()
  22. {
  23.     myPrint(ONE);
  24.     myPrint(TWO);
  25.     myPrint(ONE | TWO);
  26.     myPrint(THREE | FOUR | FIVE);
  27.     myPrint(FIVE);
  28.     return 0;
  29. }
复制代码

  5、open参数解析:

   

  (1)参数一:pathname,是要打开大概创建的文件路径名(绝对路径/相对路径)
  (2)参数二:flags,标记位,表现打开文件的方式,具体值是宏界说(可查看手册),传参方式类似于第4点的位图传参方式,可以通过按位或(|)设置多个标记。
  注意:利用这些标记位需要包罗头文件:<fcntl.h> 
  三个根本标记位:
  标记作用O_RDONLY只能读取,不能写入,若进行写操作会返回 EBADF 错误O_WRONLY只能写入,不能读取,需配合 O_CREAT 创建新文件O_RDWR可同时读取和写入,写入大概覆盖原有内容,需控制偏移量  其他标记如下:
  标记作用O_CREAT如果文件不存在,则创建它(需配合第三个参数 mode 利用)。O_EXCL与 O_CREAT 联用,若文件已存在则返回错误(可用于避免文件被不测覆盖)。O_TRUNC若文件存在且为可写模式,将其长度截断为 0(即打开文件前先清空文件内容)。O_APPEND追加写,写入时始终追加到文件末尾(自动将文件偏移量设置到文件末尾)。O_NONBLOCK以非壅闭模式打开文件(用于 I/O 多路复用,如网络编程)。  (3)参数三:mode,用于指定新建文件的权限,仅当利用 O_CREAT 或 O_TMPFILE 标记时生效。偶然间,如果不指定mode参数,那么创建的文件的权限大概会乱码:
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<fcntl.h>
  6. #define FILENAME "log.txt"
  7. //使用open
  8. int main()
  9. {
  10.     int fd = open("HF.txt",O_WRONLY | O_CREAT);
  11.     if(fd == -1)
  12.     {
  13.         perror("open");
  14.         return 1;
  15.     }
  16.     return 0;
  17. }
复制代码

  如图会发现创建的文件的权限位乱码的,所以此时我们需要利用第三个参数code解决:
  

  但受系统权限掩码的限定,会导致创建的文件的权限与我们设置的权限不一样,此时需要提前利用一个系统调用:umask(0),头文件: <sys/stat.h> 
  表明:用于设置当前历程的文件创建掩码(file creation mask)为 0。文件创建掩码是一个位掩码,用于在创建文件或目录时屏蔽某些权限位,从而控制新创建文件的默认权限。
  此时就与我们设置的权限一样了。
  

  6、写文件的系统调用:write

   

  参数解析:
  参数名称数据范例形貌fdint文件形貌符,指向已打开的文件、管道、套接字或设备(如标准输入stdin对应 fd = 0;标准输出stdout对应 fd = 1;标准错误stderr对应 fd = 2)。
通过open系统调用获取,用于标识写入目标。bufconst void *写入缓冲区指针,它是一个指向用户空间缓冲区的指针,这个缓冲区里存储着准备写入的数据。数据的传输方向是从 用户空间(buf)到内核空间(fd 对应的设备或文件)。
可以是字符数组、结构体或其他数据范例,需确保内存访问权限合法。countsize_t要写入的字节数,指定从buf中读取的最大数据量。
实际写入字节数大概小于count(如碰到文件末尾、磁盘空间不足或权限限定)。
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<fcntl.h>
  6. #include<sys/stat.h>
  7. #define FILENAME "log.txt"
  8. int main()
  9. {
  10.     umask(0);
  11.     int fd = open("HF.txt",O_WRONLY | O_CREAT,0666);
  12.     const char* str = "hello write\n";
  13.     write(fd,str,strlen(str));
  14.     return 0;
  15. }
复制代码

  注意:
  

  7、关闭文件的系统调用:close

   

  五、文件形貌符(fd)

1、认识文件形貌符

   这是一个及其重要的概念,文件形貌符也就是open函数的返回值,我们先看看值是什么?
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<fcntl.h>
  6. #include<sys/stat.h>
  7. #define FILENAME "log.txt"
  8. //认识文件描述符
  9. int main()
  10. {
  11.     int fd1 = open("HF1.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
  12.     int fd2 = open("HF2.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
  13.     int fd3 = open("HF3.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
  14.     int fd4 = open("HF4.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
  15.     int fd5 = open("HF5.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
  16.     printf("fd1:%d\n",fd1);
  17.     printf("fd2:%d\n",fd2);
  18.     printf("fd3:%d\n",fd3);
  19.     printf("fd4:%d\n",fd4);
  20.     printf("fd5:%d\n",fd5);
  21.     return 0;
  22. }
复制代码

  可以看到值其实就是整形,但为什么是从3开始呐?
  由于0,1,2端口已经默认被三个标准占用了:
  标准输入(stdin):0
  标准输出(stdout):1
  标准错误(stderr):2
  

    由上述知识我们可以知道,C语言的相干文件接口,本质就是封装了各个系统调用,重要是为了保证自己的跨平台性。
  2、文件形貌符具体是什么?为什么后续访问文件的系统调用都要通过fd来操作?

   上面我们知道,历程要管理打开的文件需要先形貌后构造。
  (1)起首task_struct中存在一个成员变量(struct file_struct *files),这个成员指向的结构体(struct file_struct)里面存在一个成员,该成员表现历程打开的文件形貌符表。
  (2)所谓文件形貌符表也就是一个数组,其数组范例为(struct file**   fd_array[ ]),这是一个二级指针,其内容的范例为(struct file*)
  (3)struct file是文件结构体,里面包罗了文件属性、方法集、文件运行时的状态信息、操作函数和资源引用等等信息(如下图),被称为文件操作的 “控制器”。
  (4)而open返回值fd(文件形貌符)就是这个文件形貌符表的下标,有了这个下标,我们就可以找到下标对应的struct file,从而就可以操作这个文件,所以后续访问文件的系统调用都要通过fd来操作的。
  

  3、一切皆文件

   

  4、文件形貌符表的分配规则以及利用规则实现重定向

(1)文件形貌符表的分配规则

   文件形貌符表的分配规则:会叫最小的没有被利用的下标,分配给最新打开的文件。
  

  输出重定向的现象:
  

  (2)改变重定向的系统调用(dup2)

   

  我们先学习dup2系统调用,参数解析如下:
  (1)oldfd:已存在的、有效的文件形貌符,指向一个已打开的文件、设备或套接字。
  
若 oldfd 无效(如未打开或已关闭),dup2() 返回 -1 并设置 errno=EBADF
  
  (2)newfd:新绑定的文件形貌符,dup2() 会将文件对象从前的文件形貌符oldfd解绑,然后与 newfd进行绑定。
  
若 newfd 未打开
直接将 newfd 指向 oldfd 对应的 struct file 对象。
若 newfd 已打开
先关闭 newfd(减少其原 struct file 的引用计数),再复制 oldfd 的文件对象到 newfd。
若 newfd == oldfd
不实行任何操作,直接返回 newfd(避免自我关闭)。
  (3)dup2利用场景

   重定向到文件:
  1. //使用dup2
  2. int main()
  3. {
  4.     int fd = open("newfile",O_WRONLY|O_CREAT|O_TRUNC,0666);
  5.     dup2(fd,1);//将newfile文件与标准输出进行绑定
  6.     //这样printf就会默认向上述文件进行输出
  7.     printf("hello newfile\n");
  8.     return 0;
  9. }
复制代码

  从文件读取内容到数组:
  1. int main()
  2. {
  3.     int fd = open("newfile",O_RDONLY,0666);
  4.     dup2(fd,0);//将文件的文件描述符与标准输出进行绑定
  5.     char buffer[1024];
  6.     while(1)
  7.     {
  8.         //默认情况,stdin会从键盘中读取,若键盘不输入,是会发生阻塞的
  9.         char* s = fgets(buffer,sizeof(buffer),stdin);//此时stdin会默认从文件中读取
  10.         if(s==NULL)break;
  11.         printf("file content:%s",buffer);
  12.     }
  13.     return 0;
  14. }
复制代码

  5、给自界说shell增长重定向功能

六、缓冲区题目:

1、简单先容

   缓冲区其实是一块内存区域,目的是用来提高利用者的服从(空间换时间)。
  比如从云南到北京运送货品,总共需要运送100kg,如果一次运送10kg,需要来回10次,如许耗费的时间就非常多;如果我用比较大的运输机一次就能运送100kg,如许就用运送一次,大大提高了服从。
  

  2、为什么利用缓冲区能提高服从?

   注意:平时我们所说的,包括这里即将减少的缓冲区都是语言层面的缓冲区(比如C语言里面的缓冲区),与OS内核中的缓冲区没有关系。
  为什么利用语言层面的缓冲区能提高服从?
  结合上述运送物资的例子,我们知道通过系统调用访问OS是需要有很大开销的,如果我们语言层面不设置缓冲区,那么来一点数据就送给OS,又来一点数据又会访问OS,如许就会多出很多开销,但如果我们在语言层设置一个缓冲区,让需要存储的数据线一点一点累积保存到缓冲区,达到一定的空间后,我们一次性传输给OS,如许访问OS的次数就大大减少了,从而就提高了服从。
  

  

  3、缓冲区在哪里?

   缓冲区是在FILE结构体中,也就是由FILE结构体来维护缓冲区:
  缓冲区常见字段:
  1. struct _IO_FILE {
  2.     // 基础文件描述符
  3.     int _fileno;
  4.    
  5.     // 缓冲区指针与状态
  6.     char* _IO_read_ptr;      // 读缓冲区当前位置
  7.     char* _IO_read_end;      // 读缓冲区结束位置
  8.     char* _IO_read_base;     // 读缓冲区起始位置
  9.    
  10.     char* _IO_write_base;    // 写缓冲区起始位置
  11.     char* _IO_write_ptr;     // 写缓冲区当前位置
  12.     char* _IO_write_end;     // 写缓冲区结束位置
  13.     char* _IO_buf_base;      // 缓冲区基址
  14.     char* _IO_buf_end;       // 缓冲区结束地址
  15.    
  16.     // 缓冲区状态标志
  17.     int _IO_write_base;      // 写缓冲区起始位置(重复字段,实际为标志位)
  18.     unsigned _flags;         // 缓冲区标志(如是否全缓冲、行缓冲等)
  19.     unsigned _IO_file_flags; // 文件状态标志
  20.    
  21.     // 缓冲区大小与类型
  22.     int _IO_buf_size;        // 缓冲区大小
  23.     int _mode;               // 读写模式
  24.    
  25.     // ... 其他字段(省略)
  26. };
复制代码
核心字段:
  

  

  4、用代码证实缓冲区的存在

  
  1. //证明缓冲区的存在
  2. int main()
  3. {
  4.     //使用系统调用
  5.     const char* s1 = "hello write\n";
  6.     write(1,s1,strlen(s1));
  7.     //使用C语言接口
  8.     const char* s2 = "hello fprintf\n";
  9.     fprintf(stdout,"%s",s2);
  10.     const char* s3 = "hello fwrite\n";
  11.     fwrite(s3,strlen(s3),1,stdout);
  12.     fork();
  13.     return 0;
  14. }
复制代码
如果我们直接运行那么就会正常打印,由于表现器是行刷新(即写完一行就刷新数据(\n))
  

  但如果我们重定向到某个文件,会发现一个奇怪的现象:
  C语言接口的内容会存在两份,而系统调用的接口内容只有一份

  由于我们利用了重定向,重定向的刷新策略是全缓存刷新(即缓冲区满了才刷新数据),但很显然代码中的两条内容是塞不满缓冲区的,所以此时会一直等待,末了会碰到fork创建子历程,而刷新数据也属于修改数据的一种方式,父子历程中恣意一个历程修改共享数据时,都会进行写实拷贝,末了历程结束,缓冲区强迫刷新,父子历程都会向文件中刷新数据,所以C语言接口的数据会存在两份,而系统调用接口write会直接将内容存在系统内部的缓冲区,此时内容与父子历程无关,所以只有一份数据。
  其次printf、scanf等等函数的格式化输出也与缓冲区有关,可以搜刮相识相识。
  七、模拟实现文件操作的常用接口(有缓冲区和无缓冲区版本)

1、无缓冲区版本

mystdio.h

  
  1. #pragma once
  2. #include<stdio.h>
  3. typedef struct _myFILE
  4. {
  5.     int fileno;
  6. }myFILE;
  7. myFILE* my_fopen(const char* pathname,const char* mode);
  8. int my_fwrite(myFILE* fp,const char* fs,int size);
  9. //int my_fread();
  10. void my_fclose(myFILE* fp);
复制代码
mystdio.c

  
  1. #include "mystdio.h"
  2. #include <string.h>
  3. #include <sys/stat.h>
  4. #include <sys/types.h>
  5. #include <stdlib.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. myFILE *my_fopen(const char *pathname, const char *mode)
  9. {
  10.     int flag = 0;
  11.     if (strcmp(mode, "r") == 0)
  12.     {
  13.         flag |= O_RDONLY;
  14.     }
  15.     else if (strcmp(mode, "w") == 0)
  16.     {
  17.         flag |= (O_CREAT | O_WRONLY | O_TRUNC);
  18.     }
  19.     else if (strcmp(mode, "a") == 0)
  20.     {
  21.         flag |= (O_CREAT | O_WRONLY | O_APPEND);
  22.     }
  23.     else
  24.     {
  25.         return NULL;
  26.     }
  27.     int fd = 0;
  28.     if (flag & O_WRONLY)
  29.     {
  30.         umask(0);
  31.         fd = open(pathname, flag, 0666);
  32.     }
  33.     else
  34.     {
  35.         fd = open(pathname, flag);
  36.     }
  37.     if (fd < 0)
  38.         return NULL;
  39.     myFILE *fp = (myFILE *)malloc(sizeof(myFILE));
  40.     if (fp == NULL)
  41.         return NULL;
  42.     fp->fileno = fd;
  43.     return fp;
  44. }
  45. int my_fwrite(myFILE* fp,const char* s,int size)
  46. {
  47.     return write(fp->fileno,s,size);
  48. }
  49. void my_fclose(myFILE* fp)
  50. {
  51.     close(fp->fileno);
  52.     free(fp);
  53. }
复制代码
filetest.c

  
  1. #include"mystdio.h"
  2. #include<string.h>
  3. const char* filename = "./log.txt";
  4. int main()
  5. {
  6.     myFILE* fp = my_fopen(filename,"w");
  7.     if(fp == NULL) return 1;
  8.     const char* s = "hello myflie\n";
  9.     my_fwrite(fp,s,strlen(s));
  10.     my_fclose(fp);
  11.     return 0;
  12. }
复制代码
2、有缓冲区版本

mystdio.c

  
  1. #include "mystdio.h"
  2. #include <string.h>
  3. #include <sys/stat.h>
  4. #include <sys/types.h>
  5. #include <stdlib.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. myFILE *my_fopen(const char *pathname, const char *mode)
  9. {
  10.     int flag = 0;
  11.     if (strcmp(mode, "r") == 0)
  12.     {
  13.         flag |= O_RDONLY;
  14.     }
  15.     else if (strcmp(mode, "w") == 0)
  16.     {
  17.         flag |= (O_CREAT | O_WRONLY | O_TRUNC);
  18.     }
  19.     else if (strcmp(mode, "a") == 0)
  20.     {
  21.         flag |= (O_CREAT | O_WRONLY | O_APPEND);
  22.     }
  23.     else
  24.     {
  25.         return NULL;
  26.     }
  27.     int fd = 0;
  28.     if (flag & O_WRONLY)
  29.     {
  30.         umask(0);
  31.         fd = open(pathname, flag, 0666);
  32.     }
  33.     else
  34.     {
  35.         fd = open(pathname, flag);
  36.     }
  37.     if (fd < 0)
  38.         return NULL;
  39.     myFILE *fp = (myFILE *)malloc(sizeof(myFILE));
  40.     if (fp == NULL)
  41.         return NULL;
  42.     fp->fileno = fd;
  43.     fp->cap = SIZE;
  44.     fp->pos = 0;
  45.     fp->flush_mode = LINE_FLUSH;
  46.     return fp;
  47. }
  48. void my_fflush(myFILE* fp)
  49. {
  50.     if(fp->pos == 0) return;
  51.     write(fp->fileno,fp->outbuffer,fp->pos);
  52.     fp->pos = 0;
  53. }
  54. int my_fwrite(myFILE* fp,const char* s,int size)
  55. {
  56.     //向缓冲区写入
  57.     memcpy(fp->outbuffer+fp->pos,s,size);
  58.     fp->pos += size;
  59.     if((fp->flush_mode & LINE_FLUSH) && fp->outbuffer[fp->pos-1] == '\n')
  60.     {
  61.         my_fflush(fp);
  62.     }
  63.     else if((fp->flush_mode & LINE_FLUSH)&&fp->pos == fp->cap)
  64.     {
  65.         my_fflush(fp);
  66.     }
  67.     return size;
  68.     //return write(fp->fileno,s,size);
  69. }
  70. const char* toString(int flag)
  71. {
  72.     if(flag & NONE_FLUSH) return "None";
  73.     else if(flag & LINE_FLUSH)return "Line";
  74.     else if(flag & FULL_FLUSH)return "FULL";
  75.     return "err";
  76. }
  77. void DebugPrint(myFILE* fp)
  78. {
  79.     printf("outbuffer:%s\n",fp->outbuffer);
  80.     printf("fd:%d\npos:%d\ncap:%d\nflush_node:%s\n",fp->fileno,fp->pos,fp->cap,toString(fp->flush_mode));
  81. }
  82. void my_fclose(myFILE* fp)
  83. {
  84.     my_fflush(fp);
  85.     close(fp->fileno);
  86.     free(fp);
  87. }
复制代码
mystdio.h

  
  1. #pragma once
  2. #include<stdio.h>
  3. #define SIZE 4096//缓冲区大小
  4. #define NONE_FLUSH (1<<1)//无自动刷新
  5. #define LINE_FLUSH (1<<2)//行刷新
  6. #define FULL_FLUSH (1<<3)//全刷新
  7. typedef struct _myFILE
  8. {
  9.     //char inbuffer[];
  10.     char outbuffer[SIZE];
  11.     int pos;
  12.     int cap;
  13.     int flush_mode;
  14.     int fileno;
  15. }myFILE;
  16. myFILE* my_fopen(const char* pathname,const char* mode);
  17. int my_fwrite(myFILE* fp,const char* fs,int size);
  18. //int my_fread();
  19. void my_fflush(myFILE* fp);
  20. void DebugPrint(myFILE* fp);
  21. void my_fclose(myFILE* fp);
复制代码
filetest.c

  
  1. #include "mystdio.h"
  2. #include <string.h>
  3. #include <unistd.h>
  4. const char *filename = "./log.txt";
  5. int main()
  6. {
  7.     myFILE *fp = my_fopen(filename, "w");
  8.     if (fp == NULL)
  9.         return 1;
  10.     int cnt = 5;
  11.     char buffer[64];
  12.     while (cnt)
  13.     {
  14.         snprintf(buffer, sizeof(buffer), "helloworld,hellobit,cnt:%d", cnt--);
  15.         my_fwrite(fp, buffer, strlen(buffer));
  16.         DebugPrint(fp);
  17.         sleep(2);
  18.     }
  19.     my_fclose(fp);
  20.     return 0;
  21. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表