文件基础
概念:一组相关数据的有序集合
文件有文件名和路径
Linux支持不同的文件类型:
- 常规文件 r,如:文本文件、二进制文件
- 目录文件 d
- 字符设备文件 c
- 块设备文件 b
- 管道文件 p
- 套接字文件 s
- 符号链接文件 l
注意:操作系统不同,所支持的文件类型也不同
标准I/O-介绍
标准I/O由ANSI C标准定义
主流操作系统上都实现了C库
作用:标准I/O通过缓冲机制减少系统调用,实现更高的效率,移植性好
- 没有操作系统直接访问计算机资源,不安全
- 有操作系统,程序要访问硬件资源,需要通过操作系统提供的系统调用接口访问。
- 不同的操作系统提供的系统调用接口各不相同,Linux的系统调用大致分为:进程控制、进程间通信、文件系统控制、存储管理、网络管理、套接字控制、用户管理。
解释:标准I/O函数在执行时用到系统调用,Linux必须从用户态切换到内核态,处理相应的请求,然后再返回到用户态。如果频繁的执行系统调用会增加系统的开销。为了避免这种情况,标准I/O使用时再用户空间创建缓冲区,读写时先操作缓冲区,在合适的时机再通过系统调用访问实际的文件,从而减少了使用系统调用的次数。
标准I/O-流
FILE
- 标准IO用一个结构体类型来存放打开的文件的相关信息
- 标准I/O的所有操作都是围绕FILE来进行
流(stream)
FILE又被称为流
文本流/二进制流
不同操作系统对流的定义不同:
Windows
- 二进制流:换行符<-->'\n'
- 文本流:换行符<-->'\r''\n'
Linux
- 换行符<-->'\n'
标准I/O-流的缓冲类型
- 全缓冲:默认情况下打开的就是全缓冲,当缓冲区无数据、无空间或执行flush操作时才执行I/O操作。
- 行缓冲:当输入或输出时遇到换行符(‘\n')时,进行IO操作;当流和一个终端关联时,标准输入输出流是典型的行缓冲。注意:我们在打印调试时一定要加上换行符,否则,打印的信息仍存放在缓冲区中。
- 无缓冲:数据直接写入文件,流不进行缓冲。标准出错流是不带缓冲的,出错信息可以立刻显示在终端上。
标准I/O概述http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012531&ajax=1
标准I/O预定义3个流,程序运行时自动打开
标准输入流 | 0 | STDIN_FILENO | stdin |
标准输出流 | 1 | STDOUT_FILENO | stdout |
标准错误流 | 2 | STDERR_FILENO | stderr |
标准I/O-打开流
FILE *fopen(const char *path,const char *mode);
成功返回流指针,失败时返回NULL。
标准I/O-fopen-mode参数
fopen示例:
#include<stdio.h>
int main(int argc,char *argv[])
{
FILE *fp;
if((fp=fopen("test.txt","r+")==NULL){
printf("fopen error\n");
return -1;
}
...
return 0;
}
标准I/O-fopen-新建文件权限
- fopen()创建的文件访问权限是0666(rw-rw-rw-)
- Linux系统中umask设定会影响文件的访问权限,其规则是(0666&~umask).umask 0022,运算后644
- 用户可以通过umask函数修改相关设定。
- 如果希望umask不影响文件访问权限,该如何设定?0
流的打开与关闭http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012532&ajax=1
标准I/O-处理错误信息
extern int errno;
void perror(const char *s);
char *strerror(int errno);
- errno存放错误号
- perror先输出字符串s,在输出错误号 对应的错误信息。
- strerror根据错误号返回错误信息
标准I/O-处理错误信息-示例1
#include<stdio.h>
int main(int argc,char *argv[])
{
FILE *fp;
if((fp=fopen("test.txt",r+)==NULL)
{
perror("fopen");
return -1;
}
...
fopen:No such file or directory
标准I/O-处理错误信息-示例2
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(int argc,char *argv[])
{
FILE *fp;
if((fp=fopen("test.txt",r+)==NULL)
{
printf("fopen:%s\n",strerror(errno));
return -1;
}
...
fopen:No such file or directory
错误处理http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012533&ajax=1
标准I/O-关闭流
int fclose(FILE *stream);
- fclose()调用成功返回0,失败返回EOF并设置errno;
- 流关闭时自动刷新缓冲中的数据并释放缓冲区;
- 当一个程序正常终止时,所有打开的流都会被关闭;
- 流一旦关闭后就不能执行任何操作。
标准I/O-思考和练习
程序中能够打开的文件或流的个数有限制,如何测试?
思路:循环打开流,成功则计数器加一,直到出错为止。
答案:1021+stdin+stdout+stderr=1024
/*************************************************************************
> File Name: filebyte.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月22日 星期六 20时1分29秒
************************************************************************/
#include<stdio.h>
int main(int argc, const char *argv[])
{
int num=0;
FILE *fp;
while(1)
{
if((fp=fopen("1.txt","a+"))!=NULL)
{
num++;
}
else
{
perror("fopen");
break;
}
}
printf("The IO stream max is %d\n",num);
fclose(fp);
return 0;
}
标准I/O-流的读写
标准I/O-流的读-按字符输入
int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar(void);
成功返回读取的字符,若到文件结尾或出错时返回EOF
getchar()等同于fgetc(stdin).
标准I/O-fgetc-示例1
int ch;
ch=fgetc(stdin);
printf("%c\n",ch);
标准I/O-fgetc-示例2:统计文件大小
/*************************************************************************
> File Name: filebyte.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月22日 星期六 20时19分22秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
int ch, count=0;
if((fp=fopen(argv[1],"r"))==NULL)
{
perror("fopen");
return -1;
}
while((ch=fgetc(fp))!=EOF)
{
count++;
}
printf("total %d bytes\n",count);
return 0;
}
标准I/O-流的写-按字符输出
int putc(int c,FILE * stream);
int fputc(int c,FILE * stream);
int putchar(int c);
成功返回写入的字符;出错返回EOF;
putchar(c)等同于fputc(c,stdout);
标准I/O-fputc-示例1:向文件中输入a-z
/*************************************************************************
> File Name: a_zfile.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月22日 星期六 20时37分28秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int ch;
FILE *fp;
if((fp=fopen(argv[1],"w"))==NULL)
{
perror("fopen");
return -1;
}
for(ch='a';ch<='z';ch++)
{
fputc(ch,fp);
}
return 0;
}
标准I/O-fputc-示例2:实现文件复制
/*************************************************************************
> File Name: mycp.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月22日 星期六 21时30分20秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int ch;
FILE *fps,*fpd;
if(argc<3)
{
printf("%s srcfile desfile\n",argv[0]);
}
if((fps=fopen(argv[1],"r"))==NULL)
{
perror("fopen src");
return -1;
}
if((fpd=fopen(argv[2],"w"))==NULL)
{
perror("fopen des");
return -1;
}
while((ch=fgetc(fps))!=EOF)
{
fputc(ch,fpd);
}
fclose(fps);
fclose(fpd);
return 0;
}
流的读写http://www.proedu.com.cn:8800/web/shareVideo/index.action?id=1012534&ajax=1
标准I/O-按行输入
char * gets(char *s);//不建议使用,容易造成缓冲区溢出
char * fgets(char * s,int size,FILE * stream);
成功返回s,失败或到达文件末尾:NULL。
fgets遇到‘\n'或已输入size-1个字符时返回,总是包含'\0',不能保证每次都能读出一行。
标准I/O-fgets-示例
#define N 6
char buf[N];
fgets(buf,N,stdin);
printf("%s",buf);
假设键盘输入分别是:abcd<回车> abcdef<回车>,buf中的内容是?
标准I/O-按行输出
int puts(const char *s);
int fputs(const char *s,FILE *stream);
成功返回s,失败:NULL
puts将缓冲区s的字符串输出到stdout,并追加'\n'
标准I/O-fputs-示例
puts("hello world");
FILE *fp;
char buf[]="hello world";
if((fp=fopen(argv[1],"a"))==NULL)
{
perror("fopen");
return -1;
}
fputs(buf,fp);
注意:输出的字符串中可以包含'\n',也可以不包含
标准I/O-思考和练习
如何统计一个文本文件包含多少行?
1.fgetc? 效率低
/*************************************************************************
> File Name: fgtcline.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月23日 星期日 11时11分45秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
int ch,num=0;
if(argc<2)
{
printf("usage : %s <file>\n",argv[0]);
return -1;
}
if((fp=fopen(argv[1],"r"))==NULL)
{
perror("fopen");
return -1;
}
while((ch=fgetc(fp))!=EOF)
{
if(ch=='\n')//疑问:此处换成if(fgetc(fp)=='\n'),测试结果不对
{
num++;
}
}
printf("the line of %s is %d\n",argv[1],num);
return 0;
}
因为fgetc(fp)在一次循环中执行了2次。
2.fgets?如何判断读取了一行?
buf[strlen[buf]-1]?='\n'
/*************************************************************************
> File Name: fileline.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月23日 星期日 10时50分57秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include<string.h>
int main(int argc, char *argv[])
{
FILE *fp;
char buf[128];
int num=0;
if(argc<2)
{
printf("Usage : %s <fiel>\n",argv[0]);
return -1;
}
if((fp=fopen(argv[1],"r"))==NULL)
{
perror("fopen");
return -1;
}
while(fgets(buf,128,fp)!=NULL)
{
if(buf[strlen(buf)-1]=='\n')
num++;
}
printf("the line of %s is %d\n",argv[1],num);
return 0;
}
标准I/O-按对象读写
size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream);
size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream);
size:读取的每个记录的大小;nmemb:读取的记录数
成功返回实际读取到的nmemb数目,失败返回EOF
既可以读写文本文件,也可以读写数据文件。
int s[10];
if(fread(s,sizeof(int),10,fp)<0)
{
perror("fread");
return -1;
}
struct student
{
int no;
char name[8];
float score;
}s[]={{1,"zhang",97},{2,"wang",99}};
fwrite(s,sizeof(student),2,fp);
标准I/O-思考和练习
如何利用fread、fwrite实现文件的复制?
/*************************************************************************
> File Name: freadwritecp.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月23日 星期日 13时43分39秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fps,*fpd;
char ch[128];
int n;
if(argc<3)
{
printf("Usage : %s<src_file> <des_file>\n",argv[0]);
return -1;
}
if((fps=fopen(argv[1],"r"))==NULL)
{
perror("fopen src:");
return -1;
}
if((fpd=fopen(argv[2],"w"))==NULL)
{
perror("fopen des:");
return -1;
}
while((n=fread(ch,1,128,fps))>0)
{
fwrite(ch,1,n,fpd);
}
fclose(fps);
fclose(fpd);
return 0;
}
标准I/O-刷新流
什么情况会刷新流?
- 缓冲区满或遇到’\n'
- 流关闭
- fflush
int fflush(FILE *fp);
成功:0,失败:EOF
Linux下只能刷新输出缓冲区
/*************************************************************************
> File Name: fflush.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月23日 星期日 15时00分01秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
if((fp=fopen("tetx.txt","w"))==NULL)
{
perror("fopen:");
return -1;
}
fputc('a',fp);
fflush(fp);
while(1);
return 0;
}
定位流-ftell/fseek/rewind
#include<stdio.h>
long tell(FILE *stream);
int fseek(FILE *stream,long offset,int whence);
void rewind(FILE *stream);
ftell成功返回流的当前读写位置,出错返回EOF
fseek()定位一个流,成功返回0,失败返回EOF
whence基准值,参数:SEEK_SET/SEEK_CUR/SEEK_END
offset参数:偏移量,可正可负
rewind()将流定位到文件的开始位置
读写流时,当前读写位置自动后移
定位流-示例一
在文件末尾追加字符't'
FILE *fp=fopen("text.txt","r+");
fseek(fp,0,SEEK_END);
fputc('t',fp);
定位流-示例二
获取文件长度
/*************************************************************************
> File Name: filelongfseek.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月23日 星期日 15时27分59秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
if((fp=fopen("text.txt","r+"))==NULL)
{
perror("fopen");
return -1;
}
fseek(fp,0,SEEK_END);
printf("length of file is %ld\n",ftell(fp));
return 0;
}
标准I/O-判断流是否出错和结束
include<stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);
ferror()返回1表示流出错;否则返回0
feof()返回1表示文件已到末尾;否则返回0
标准I/O-格式化输出
#include<stdio.h>
int printf(const char *fmt,...);
int fprintf(FILE *stream,const char *fmt,...);
int sprintf(char *buf,const char *fmt,...);
成功:输出字符数(sprintf返回存入数组中的字符数)
失败:EOF
标准I/O-格式化输出-示例
以指定格式“年-月-日”分别写入文件和缓冲区
int year,month,date;
FILE *fp;
char buf[64];
year=2014;month=10;date=26;
fp=fopen("text.txt","a+");
fprintf(fp,"%d-%d-%d\n",year,month,date);
sprintf(buf,"%d-%d-%d\n",year,month,date);
标准I/O-思考和练习
/*************************************************************************
> File Name: time.c
> Author: 徐维龙
> Mail: 3190217171@qq.com
> Created Time: 2020年02月23日 星期日 16时18分08秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int main(int argc, char *argv[])
{
FILE *fp;
char buf[64];
int line =0;
time_t t;
struct tm *tp;
if((fp=fopen("time.txt","a+"))==NULL)
{
perror("fopne");
return -1;
}
while(fgets(buf,64,fp)!=NULL)
{
if(buf[strlen(buf)-1]=='\n');
line++;
}
while(1)
{
time(&t);
tp=localtime(&t);
fprintf(fp,"%02d、%04d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1970,\
tp->tm_mon+1,tp->tm_mday,tp->tm_hour, tp->tm_min,tp->tm_sec);
fflush(fp);
sleep(1);
}
fclose(fp);
return 0;
}