在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.但正常写入到显示器上是不会刷新两遍的!

 

原文地址:http://www.cnblogs.com/LonelyMoNan/p/16810763.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性