首页 > 其他分享 >基础I/O

基础I/O

时间:2022-10-24 22:34:16浏览次数:54  
标签:文件 int 基础 缓冲区 fd printf PRINT

在c中的对文件进行操作

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int main(int argc,char *argv[])
  5 {
  6   if(argc!=2)
  7   {
  8     printf("Usage: %s filename\n",argv[0]);
  9     return 1;
 10   }
 11   //r,w,r+,w+,a,a+
 12   //r写 w写但会清空 w会自动创建 a追加
 13  // FILE *fp=fopen("log.txt","w");//打开log.txt 以r形式打开
 14   FILE *fp=fopen(argv[1],"r");
 15   if(fp==NULL)
 16   {
 17     perror("fopen");
 18     return 1;
 19   }
 20 
 21   char buffer[64];
 22   while(fgets(buffer,sizeof(buffer),fp)!=NULL)
 23   {
 24     printf("%s",buffer);                                                                                                                                           
 25   }
 26 
 27  // const char *msg="hello";
 28  // int cnt=1;
 29  // while(cnt<=5)
 30  // {
 31  //   fprintf(fp,"%s: %d\n",msg,cnt++);//讲msg写到fp内 再打印出来
 32  // }
 33   fclose(fp);//关闭fp指向的文件
 34 }

这里的fopen fcolse等等这些对文件进行操作的,都是对系统接口的一种封装

  • 好处是能降低学习成本,但每种语言都对系统接口封装不同的接口,
  • 如果想看其他语言的文件操作,又得重新学习,换个角度看,还会增加学习成本

 

其实可以直接学习系统接口,因为无论什么语言都是对系统接口的封装,所以学习系统接口,以后学习其他语言的文件操作,都会降低学习成本

 

参数

  • O_RDONLY(只读)
  • O_WRONLY(只写)
  • O_RDWR(读写)
  • O_APPEND(追加)
  • O_CREAT(若没有此文件,则创建)
  • O_TRUNC(清空文件)

 

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 #include<unistd.h>
  7 
  8 int main()
  9 {
 10   umask(0);//重新调整权限掩码  并且不影响外面的掩码  只影响文件内 (外面的umask实际在也是调用此接口进行修改权限掩码的)
 11   close(0);//fd默认变为0
 12   close(2);//fd默认变为2
 13   close(1);//fd默认变为1 但打印不出fd
 14  // int fd=open("log.txt",O_WRONLY|O_TRUMC|O_CREAT);
 15 // int fd=open("log.txt",O_WRONLY|O_TRUNC|O_CREAT,0666);//C中 w 封装的系统操作  文件权限                                                                                                                
 16 // int fd=open("log.txt",O_WRONLY|O_APPEND|O_CREAT,0666);//C中 a 封装的系统操作
 17  int fd=open("log.txt",O_RDONLY);//C中 a 封装的系统操作
 18 
 19   if(fd<0)
 20   {
 21     perror("open");
 22     return 1;
 23   }
 24 
 25   printf("fd : %d\n",fd);
 26 
 27  /* const char* str = "Hello Linux!\n";
 28   int cnt =1;
 29   while(cnt<=5)
 30   {
 31     write(fd,str,strlen(str));//写入文件 需要写权限 \0不用写入 所以用strlen
 32     cnt++;
 33   }*/
 34 
 35   char buff[256];
 36   ssize_t s=read(fd,buff,sizeof(buff)-1);//读文件 需要读权限 
 37   if(s>0)
 38   {
 39   buff[s]='\0';//读到5个字符 那么下标从0~4 在第5个位置放入\0
 40   printf("%s",buff);
 41   }
 42 
 43   close(fd);//关闭文件
 44 
 45   return 0;
 46 }
这里有个疑问 为什么open能接收多个参数呢?接收1个也行 多个也行 c中是没有默认参数的?这是怎么实现的呢?
  1 #include<stdio.h>
  2 
  3 #define PRINT_A 0x01
  4 #define PRINT_B 0x02
  5 #define PRINT_C 0x04
  6 #define PRINT_D 0x08
  7 #define allair 0x00                                                                                                                                                
  8 
  9 void show(int flast)
 10 {
 11   if(flast & PRINT_A)
 12   {
 13     printf("PRINT_A\n");
 14   }
 15   if(flast & PRINT_B)
 16   {
 17     printf("PRINT_B\n");
 18   }
 19   if(flast & PRINT_C)
 20   {
 21     printf("PRINT_C\n");
 22   }
 23   if(flast & PRINT_D)
 24   {
 25     printf("PRINT_D\n");
 26   }
 27   if(flast == allair)
 28   {
 29     printf("allair\n");
 30   }
 31 }
 32 
 33 int main()
 34 {
 35   show(PRINT_A);
 36   show(PRINT_A|PRINT_B);
 37   show(PRINT_A|PRINT_B|PRINT_C);
 38   show(PRINT_A|PRINT_B|PRINT_C|PRINT_D);
 39 
 40   show(allair);
 41   return 0;
 42 }
实际这些都是宏 用|传过去 然后 & 上 。

运行后 用fd接收文件描述符 发现默认为3  为什么呢?不是从0或者1开始呢?

因为在系统内的文件大概如下:

 

 而0,1,2对应的是标准输入(键盘)、表示输出(显示器)、标准错误(显示器)

这三个是默认有的,所以创建文件时第一个默认为3

创建一个文件 的过程为 先创建一个文件 随后从文件管理页找到最小的文件描述符且为空 然后链接起来 最后返回文件描述符

当你要找这个文件时 进程去文件管理页找对应的文件描述符 找到后,去链接内找文件 即可

 

所以文件描述符分配规则

从头遍历数组fd_array[]文件管理页 找到一个最小的,没有使用的下标,分配给新文件

 

当然 若你在开头关闭了 0 1 2 那么 文件描述符就会分配给你 你关闭的那个 因为是从头遍历

但如果你关闭了1 就打印不出来了 而关闭0、1 又能打印出来 为什么?

 第一:关闭的不是显示器 所以0,1可以打印出来

 第二:关闭的是显示器,但刷新缓冲后,打印在了文件里?为什么?

 

 因为1原本指向显示器,然后被修改,变为指向文件,最后就写入文件内,那为什么要刷新缓冲区才写入呢?因为有刷新策略的原因,后面就会讲到

文件描述符1原来指向标准输出,但因为关闭了1,再加上文件描述符规则,新文件的文件描述符则为1 但stdout(标准输出)依旧向1的内容输出,但1已经指向了文件,所以就是向文件内写入了。---重定向。

当然 也提供了函数 用来重定向 

  • dup2 头文件 unistd.h
  • dup2(old,new) 讲old的内容拷贝给new内。
  • dup2(fd,1)原本向显示器打印,现在向文件打印
  • duop(fd,0)由键盘输入,变为由文件输入
  8 int main()
  9 {
 10  // close(1);
 11   int fd=open("log.txt",O_RDWR);
 12   if(fd<0)
 13   {
 14     perror("open");
 15     return 1;
 16   }
 17   dup2(fd,1);                                                                                                                                                      
 18   printf("fd : %d",fd);
 19   const char* sef="hello open\n";
 20   int cnt=1;
 21   while(cnt<=5)
 22   {
 23     write(fd,sef,strlen(sef));
 24     cnt++;
 25   }
 26 
 27   fflush(stdout);//刷新缓冲区
 28 
 29   close(fd);
 30 
 31   return 0;
 32 }

重定向后,效果与关闭1相同

 

一个文件可以被打开多次,与智能指针的引用计数类似

 

缓冲区

缓冲区的意义

  • 解放使用缓冲区的进程时间
  • 缓冲区的存在可以集中处理数据刷新,减速IO的次数,从而提高整机的效率的目的
 
c语言
10 const char* s1="hello printf"; 11 const char* s2="hello fprintf"; 12 const char* s3="hello fputc"; 13 const char* s4="hello write"; 14 printf("%s",s1); W> 15 fprintf(stdout,s2); 16 fputs(s3,stdout); 17
系统接口 18 write(1,s4,strlen(s4)); 19 20 sleep(5);

缓冲区是由c语言提供的

在这段代码中,先打印write 再打印其他三个,因为c语言有提供缓冲区,而系统接口没有缓冲区,所以先打印了write再打印了其他三个

 

 

刷新策略

a 无缓冲(立即刷新) \n ...

b 行缓冲(逐行刷新)显示器...

c 全缓冲(缓冲区满,再刷新)块设备(磁盘文件)...

 

   10   const char* s1="hello printf\n";
   11   const char* s2="hello fprintf\n";
   12   const char* s3="hello fputc\n";
   13   const char* s4="hello write\n";
   14  printf("%s",s1);
W> 15  fprintf(stdout,s2);
   16  fputs(s3,stdout);
   17 
   18  write(1,s4,strlen(s4));                                                                                                                                         
   19 
   20  sleep(5);
   21 
   22  fork();
   23 
   24 
   25   return 0;

 

 

 

 通过重定向,可以发现除了write 其他都多打印了一遍,为什么?

  • 1.因为重定向后,刷新策略改变,由行缓冲变为全缓冲
  • 2.fork之前 三个已经在缓冲区内,刷新,打印。
  • 3.fork之后,生存子进程,发生写时拷贝,子进程也得到了一份相同代码,也把三个写进缓冲区,然后fork结束,又发生了刷新缓冲区,打印。
  • 4.而write是不存在缓冲区的,所以三个打印了两遍
  • 5.但正常写入到显示器上是不会刷新两遍的!

 

标签:文件,int,基础,缓冲区,fd,printf,PRINT
From: https://www.cnblogs.com/LonelyMoNan/p/16810763.html

相关文章

  • Netty介绍、BIO与NIO基础知识
    第1章Netty介绍和应用场景1.1Netty介绍Netty是由JBOSS提供的一个Java开源框架,现为Github上的独立项目。Netty是一个异步的、基于事件驱动的网络应用框架,用以快速开......
  • 【2022.10.24】Vue基础学习(1)
    内容概要1.前端发展介绍2.Vue的快速使用3.差值语法4.指令系统之文本指令5.指令系统之事件指令6.指令系统之属性指令内容详细1前端发展介绍#HTML(5)、CSS(......
  • 计算机基础(一)
    1、什么是计算机?计算机(Computer)俗称电脑,是一种高速计算的电子机器,计算机可以进行数值运算、逻辑判断,能够接收和储存信息数据(文本、图片、音频、视频等),还可以按照储存在......
  • 第4章 最基础的分类算法-k近邻算法 kNN
    4-1k近邻算法基础 ......
  • 2022-2023-1 20221318 《计算机基础和程序设计》第九周学习总结
    作业信息这个作业属于那个班级https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP作业要求https://www.cnblogs.com/rocedu/p/9577842.html#WEEK09作业目标学习......
  • css-常用布局-基础的五种
    三栏布局:如果不考虑高度,即用内容填充高度的话,是可以用inline-block和calc()实现布局的。但适用性差。float和position的兼容性好,但float会用到calc属性影响兼容性。calc和......
  • 2022-2023-1 20221318 《计算机基础和程序设计》第八周学习总结
    作业信息这个作业属于那个班级https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP作业要求https://www.cnblogs.com/rocedu/p/9577842.html#WEEK08作业目标学习......
  • Spring的基础使用二
    Spring的基础使用二Setter注入<beanid="studentTwo"class="com.wfy.pojo.Student"><propertyname="sid"value="1001"></property><propertyna......
  • 面试题JavaScript基础
    原博客地址01.如何开启js严格模式?js严格模式有什么特点?参考点:js基础知识参考答案://全局开启'usestcict'//局部开启functionfu(){'usestrict'}/*1......
  • 【Python基础学习】第十一节 内置函数详解
    Python基础学习之内置函数Python3.5版本中的68个内置函数,按顺序逐个进行了自认为详细的解析,现在是时候进行个总结了。为了方便记忆,将这些内置函数进行了如下分类:1.数学运......