文件类型:
常规文件 r
目录文件 d
字符设备文件 c
块设备文件 b
管道文件 p
套接字文件 s
符号链接文件 I
标准IO 流
FILE:
标准IO用一个结构体类型来存放打开的文件的相关信息
标准IO的所有操作都是围绕FILE进行的
流:
FILE又被称为流
文本流/二进制流
文本流和二进制流:
Windows:
二进制流:换行符 ---- ‘\n’
文本流: 换行符 ---- ‘\r’’\n’
Linux:
换行符 ---- ‘\n’
流的缓冲类型:
全缓冲:
当流的缓冲区无数据或无空间时才执行实际IO操作
行缓冲:
当在输入和输出中遇到换行符 ‘\n’ 时,进行IO操作
当流和一份终端关联时,典型的行缓冲
无缓冲:
数据直接写入文件,流不进行缓冲
标准IO定义三个流,程序运行时自动打开:
标准输入流 0 STDIN_FILENO stdin
标准输出流 1 STDOUT_FILENO stdout
标准错误流 2 STDERR_FILENO stderr
-------------------------------------------------------------------------------
打开一个标准IO流:
FILE *fopen(const char *path,const char *mode);
成功时返回流指针,出错返回NULL
mode参数
r 或 rb | 以只读方式打开文件,文件必须存在 |
r+ 或 r+b | 以读写方式打开文件,文件必须存在 |
w 或 wb | 以只写方式打开文件,若文件存在,则文件长度清0.若文件不存在,则创建文件。 |
w+ 或 w+b | 以读写方式打开文件,其他同 w |
a 或 ab | 以只写方式打开文件,若文件不存在,则创建;向文件写入的数据被追加在文件末尾。 |
a+ 或 a+b | 以读写方式打开文件,其他同 a |
当给定“b”参数时,表示以二进制方式打开文件,但Linux下忽略该参数
fopen() 创建的文件访问权限是0666(rw-rw-rw-)
Linux系统中umask设定会影响文件的访问权限,规则为(0666&—umask)
用户可通过umask函数修改相关设定
fopen示例:
#include <stdio.h>
int main(int argc,char *argv[]) {
FILE *fp; if((fp = fopen("fopendemo.txt","r+")) == NULL) { printf("fopen error\n"); return -1; } return 0; } |
-------------------------------------------------------------------------------
处理错误信息
extern int errnor; 存放错误号
void perror(const char *s); 先输出字符串s 再输出错误号对应的错误信息
char *sterror(int errno); 根据错误号返回对应错误信息
示例:
#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; } return 0; } return 0; } |
输出结果
-------------------------------------------------------------------------------
关闭流
int fclose(FILE *stream);
调用成功返回0,失败返回EOF,并设置errno
流关闭时自动刷新缓冲中的数据并释放缓冲区
当一个程序正常终止时,所有打开的流都会被关闭
流一旦关闭后就不能执行任何操作
-------------------------------------------------------------------------------
读写流
读写一个字符:(一般读写二进制文件)
fgetc() / fputc() 一次读/写一个字符
读写一行:(一般读写文本文件,读取文本时,0会与 \0 冲突)
fgets() / fputs() 一次读/写一行
读写若干个对象:(可读写二进制文件,也可读写文本文件,效率高,推荐)
fread() / fwrite() 每次读/写若干个对象,而每个对象具有相同的长度
1.按字符输入输出
1.1按字符输入
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
成功时返回读取到的字符,若到文件末尾或出错返回EOF
getchar() 等同于 fgetc(stdin)
示例:统计文件大小
#include <stdio.h> #include <string.h> #include <errno.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); } |
1.2按字符输出
int fputc(int c,FILE *stream);
int putc(int c,FILE *stream);
int putchar(int c);
参数c 输出的字符
参数 stram 选择输出的流
成功时返回写入的字符;出错时返回EOF
putchar(c) 等同于 fputc(c,stdout)
示例:向一个打开的文件写入26个英文字母
#include <stdio.h> #include <string.h> #include <errno.h> int main(int argc,char *argv[]) {
FILE *fp; int ch;
if((fp = fopen(argv[1],"w")) == NULL){
perror("fopen"); return -1; }
for(ch = 'a';ch <= 'z';ch++){
fputc(ch,fp); } } |
示例:把一个文件内容写到另一个文件(argv[1]源文件,argv[2]目标文件)
#include <stdio.h> #include <string.h> #include <errno.h> int main(int argc,char *argv[]) {
FILE *fps,*fpd; int ch;
if(argc < 3) {
printf("Usage: %s <src_file> <dst_file>\n",argv[0]); return -1; }
if((fps = fopen(argv[1],"r")) == NULL) { perror("fopen src file"); return -1; }
if((fpd = fopen(argv[2],"w")) == NULL) { perror("fopen dst file"); return -1; }
while ((ch = fgetc(fps)) != EOF) { fputc(ch,fpd); } fclose(fps); fclose(fpd); } |
diff 比较文本文件
若有不一样的地方,会输出不一样的内容
2 按行输入输出
2.1按行输入
char *gets(char *s);
char *fgets(char *s,int size,FILE *stream); 从指定的流读取指定大小的字符放入缓冲区中
成功时返回s,到文本末尾或出错时返回NULL
gets 不推荐使用,容易造成缓冲区溢出
遇到‘\n’或已输入size-1个字符时返回,总是包含‘\0’
*s 缓冲区首地址
*stream 流指针
size 大小
gets()
没有定义缓冲区大小
如果用户输入 字符大小 超过缓冲区大小 会造成缓冲区溢出
fgets相对于gets更安全
size-1 原因
应为还要在读取到的字符串末尾加上 ‘\0’
示例:
#include <stdio.h> #include <string.h> #define N 6
int main() { char buf[N];
fgets(buf,N,stdin); printf("%s",buf); return 0; } |
2.2按行输出
int puts(const char *s);
int fputs(const char *s,FILE *stream);
成功时返回输出的字符个数,出错返回EOF
puts将缓冲区s中的字符串输出到stdout,并追加'\n'
fputs将缓冲区s中的字符串输出到stream
#include <stdio.h> #include <string.h> #include <errno.h>
int main(int argc,char *argv[]) {
FILE *fp; char buf[] = "hello world"; if((fp = fopen(argv[1],"a")) == NULL) { perror("fopen"); return -1; } fputs(buf,fp); return 0; } |
按指定对象输入输出
size_t fread(void *ptr,size_t size,size_t n,FILE *fp);
size_t fwrite(const void *ptr,size_t size,size_t n,FILE *fp);
成功返回读写个数;出错返回EOF
既可读写文本文件,也可读写数据文件
size_t fread()
void *ptr 缓冲区首地址
size_t size 指定这次从流中读取的每个对象所占的大小
size_t n 指定从流中读取多少个对象
FILE *fp 流指针 用来选择从那个流中读取数据
示例:实现文件的复制
#include <stdio.h>
#define N 64
int main(int argc,char *argv[]) { FILE *fps,*fpd; char buf[N]; int n;
if(argc < 3) {
printf("Usage : %s <src_file> <dst_file> \n",argv[0]); return -1; } if((fps = fopen(argv[1],"r")) == NULL) { perror("fopen src file"); return -1; } if((fpd = fopen(argv[2],"w")) == NULL) { perror("fopen dst file"); return -1; } while((n = fread(buf,1,N,fps)) > 0) {
fwrite(buf,1,n,fpd); } fclose(fps); fclose(fpd); } |
刷新流
流自动刷新缓冲区
- 缓冲区满或者出现换行符‘\n’
- 关闭流时
- Fflush()函数刷新
int fflush(FILE *fp);
成功返回0,出错返回EOF
将流缓冲区中的数据写入实际文件
Linux下只能刷新出缓冲区
#include <stdio.h>
int main() {
FILE *fp; if((fp = fopen("test.txt","w")) == NULL) { perror("fpeon"); return -1; } fputc('a',fp); fflush(fp); while(1); return 0; } |
定位流
long ftell(FILE *stream);
long fseek(FILE *stream,long offset,int whence);
void rewind(FILE *stream);
ftell() 成功时返回当前读写位置,出错返回EOF
fseek() 定位一个流,成功时返回0,失败返回EOF
whence参数:基准点
SEEK_SET 文件开始位置
SEEK_CUR 文件当前读写位置
SEEK_END 文件末尾
offset 偏移量,可正可负
0 文件开始位置
流的读写位置 = 偏移量 + 基准点
rewind() 将流定位到文件开始位置
读写流时,当前读写位置自动后移,不需程序控制
示例
在文件末尾添加字符
#include <stdio.h>
void main() {
FILE *fp = fopen("test.txt","r+"); fseek(fp,0,SEEK_END); fputc('t',fp); } |
获取文件长度
#include <stdio.h>
int main() {
FILE *fp; if((fp = fopen("test.txt","r+")) == NULL) { perror("fopen"); return -1; }
fseek(fp,0,SEEK_END); printf("length is %ld \n",ftell(fp)); return 0;
} |
判断流是否出错和结束
int ferror(FILE *stream);
返回1表示出错,否则返回0
int feof(FILE *stream);
返回1表示到末尾,否则返回0
格式化输出
int printf(const char *fmt,…);
int fprintf(FILE *stream,const char *fmt,…); 输出到流stream中
int sprint(char *s,const char *fmt,…); 输出到缓冲区中
成功时返回字符个数,失败时返回EOF