在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