I:Input 默认输入:键盘
O:Output 默认输出:显示器
IO 1、人机交互(键盘 显示器)
2、文件读写(文件的写 读)
基本IO:
单字符读写:getchar()/putchar()
字符串读写:gets()/puts
格式化读写:scanf()/printf()
以上三组函数默认操作的是键盘和显示器终端。底层完成的是指针对键盘文件和显示器终端设备文件的读写
其中:IO都是将数据与当前进程的操作;
fgetc()/fputc()
fgetc()/fputc()
int fgetc(FILE *stream); ===>I 操作 《从fp 输入操作
功能: 该函数可以从指定的流对象关联的文件流中获取一个字符并返回;
参数: stream 一个已经打开的文件流对象/流指针,如果streamstdin的时候等价于getchar();
返回值:成功 返回获取的字符数据
失败/文件结尾 EOF;
#include <stdio.h>
int main(void)
{
char c = 0;
FILE *fp = fopen("./hello.c","r");
if(NULL == fp)
{
perro("fopen");
return -1;
}
while( (c=fgetc(fp)) != EOF)
printf("%c",c);
fclose(fp);
return 0;
}
int fputc(int c, FILE *stream);
功能: 该函数可以将指定的字符写入到目标文件流对象关联的文件中
参数: c 要写入文件的但个字符
stream 要写入的目标文件流指针,如果stream==stdout 等价于 putchar()
返回值:成功 写入字符
失败 EOF
#include <stdio.h>
int main(void)
{
char c = 0;
FILE *fp = fopen("./hello.c","r");
FILE *fp1 =fopen("./hehe.c","a+");
if(NULL == fp)
{
perro("fopen");
return -1;
}
while( (c=fgetc(fp)) != EOF)
fputc(c,fp1)
fclose(fp);
fclose(fp1);
return 0;
}
#include <stdio.h>
int main(void)
{
while(1)
fputc(fgetc(stdin),stdout);
return 0;
}
2、如何使用fputc和fgetc验证标准输出设备的行缓存和满缓存
#include <stdio.h>
int main(void)
{
//fputc('x',stdout);
//fputc('\n',stdout);
//fputc('y',stderr);
int i = 0;
for(i=0;i<1024;i++)
fputc('x',stdout);
fputc('y',stdout);
while(1)
sleep(1);
return 0;
}
单字符文件读写的异常处理
1、文件打开一场 ==》 fopen的返回值 ==》NULL:
文件IO 异常 ==》 fputc/fgfetc的返回值 == EOF;
2、文件的真正异常 ===》perror()函数捕获;
void perror(const char *s);
功能:该函数可以用于捕获系统错误并输出错误信息。
参数:s 错误信息的前导词;
返回值:无
3、系统的异常捕获机制: errno 的宏 ==>系统错误码表;
打印perror错误码表
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void)
{
int i =0;
for(i=0;i<140;i++)
{
errno = i;
printf("error %d : %s\n",errno,strerror(errno));
}
return 0;
}
fgetc()/fputc()
优点:
可以指定文件流读写,可以完成二进制的文件操作
缺点:
一次只能读一个字符,效率低下;
fgets()/fputs()
char *fgets(char *s, int size, FILE *stream);
功能:该函数可以从指定的stream文件流对象中获取长度为size的数据到本地内存s所在的地址中
参数:s用于存储数据的本地内存,一般是数组或堆区内存,size 用于存储数据的长度,一般是s数组的大小。
注意:该值会自动做减1 大小调整,原因:字符串;stream 文件流指针,一个已经打开的文件流对象;
返回值:成功 返回有效数据的地址,一般是s的地址
失败/文件结尾 NULL;
#include <stdio.h>
int main(void)
{
char s[128]={0}
FILE *fp = fopen("./abc","r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
char *p = fgets(s,sizeof(s),fp);
if(NULL == p)
{
perror("fgets");
fclose(fp);
return -1;
}
printf("%s",s);
fclose(fp);
return 0;
}
int fputs(const char *s, FILE *stream);
功能:该函数可以将s所在内存区的数据指定输出到目标为stream的流对象上。
参数:s 要输出的目标字符串
stream 要输出的目标文件流指针
如果stream == stdout 的时候,则输出信息打印到屏幕。
返回值:成功 0
失败 -1;
#include <stdio.h>
int main(void)
{
char s[128]={0}
FILE *fp = fopen("./abc","a+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
char *p = fgets(s,sizeof(s),fp);
if(NULL == p)
{
perror("fgets");
fclose(fp);
return -1;
}
fputs(p,fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(int argc,const char* argv[])
{
int i = 0;
FILE *fp = fopen("./abc","w+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
for(i=0;i<4096;i++)
// for(i=0;i<4097;i++)
{
fputs("a",fp);
}
while(1)
sleep(1);
return 0;
}
2、 提示用户任意输入信息,将用户输入的信息使用fputs函数写入
指定文件中存储。当用户输入"quit"的时候程序退出,并且
文件中不能有"quit";
#include <stdio.h>
#include <string.h>
int main(int argc,const char* argv[])
{
char s[128]={0};
char *p = &s[0];
FILE *fp = fopen("./abc","w+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
while(1)
{
printf("Please input some strings(quit for exit):");
scanf("%s",p);
if(strcmp(p,"quit")==0)
break;
fputs(p,fp);
fputs("\n",fp);
}
fclose(fp);
return 0;
}
3、
fputs()/fgets()的优缺点:
优点:
文本文件处理方便,比单字符操作效率高
缺点:
不能操作二进制文件,只能操作文本文件
fread/fwrite
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:该函数可以从指定的文件流指针stream中获取nmemb个单元大小为size的数据到本地内存ptr所在地
参数:ptr 本地内存地址,用于存储数据
size 获取数据的单元大小,如果szie == char 大小,则该和那树以字节为单位获取数据
如果size == int/short大小,则函数以整数单位获取数据
如果szie == sizeof(类型),则大院大小 == 类型大小
nmemb 获取数据的单元个树表示一次存储数据的单位,一般是内存区单元个数
stream 获取数据的来源文件流指针,如果是stdin表示从键盘获取
返回值: 成功 返回世纪获取的数据长度 一般 <= nmemb;
失败/文件结尾 0
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * fp = fopen("./abc","r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
FILE * fp1 = fopen("./abc1","w+");
if(NULL == fp1)
{
perror("fopen");
return -1;
}
char buff[128]={0};
fread(buff,sizeof(char),128,fp);
fwrite(buff,sizeof(char),128,fp1);
fclose(fp);
fclose(fp1);
return 0;
}
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:该函数可以将ptr所在内存区的数据中nmemb个大小为size单元的数据
写入到目标为stream的流指针文件中。
参数: ptr 数据所在的内存区地址,有可能是数组也有可能是堆区内存。
size 每次写入文件的数据单元大小。
nmemb 每次写入文件的数据单元个数。
stream 要写入数据的目标文件流指针。
如果stream == stdout 则输出数据到屏幕显示。
返回值:成功 写入文件的数据长度 一般小于等于nmemb个数。
失败 0
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * fp = fopen("./abc","w+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
char buff[128];
fwrite(buff,sizeof(char),128,fp);
fclose(fp);
return 0;
}
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("Usage %s [src] [dst] \n",argv[0]);
return -1;
}
FILE * fp_src = fopen(argv[1],"r");
if(NULL == fp_src)
{
perror("fopen src");
return -1;
}
FILE * fp_dst = fopen(argv[2],"w");
if(NULL == fp_dst)
{
perror("fopen dst");
return -1;
}
int ret = 0;
char s[128] ={0};
while(1)
{
bzero(s,sizeof(s));
ret = fread(s,sizeof(char),sizeof(s),fp_src);
if(ret == 0) break;
fwrite(s,sizeof(char),ret,fp_dst);
fflush(fp_dst);
}
fclose(fp_src);
fclose(fp_dst);
return 0;
}
思考题:
fread和fwrite 函数能不能完成ascii码文件的读写? 怎么操作?
fread(s,sizeof(char),1,fp); =====>fgetc();
fread(s,sizeof(char),sizeof(s),fp); ===>fgets(); ===》用这个
fread(s,sizeof(s),1,fp); ===>自杀;
fread(s, 10, 12,fp); ===>丢数据;
fwrite(s,sizeof(char),sizeof(s),fp2); ===>多数据
fwrite(s,sizoef(char),len,fp2); ===>len = fread(); len = 有效长度 ==》用这个
使用fread和fwrite读写结构文件:相见f fwrite.c 、 read.c
fwrite.c
#include <stdio.h>
#include "head.h"
int main(int argc, char *argv[])
{
FILE * fp = fopen("./abc","w+");
struct STU mystu ={1,"zhangsan",99.9};
fwrite(&mystu,sizeof(struct STU),1,fp);
fclose(fp);
return 0;
}
fread.c
#include <stdio.h>
#include "head.h"
int main(int argc, char *argv[])
{
FILE * fp = fopen("./abc","r");
struct STU mystu ={0};
fread(&mystu,sizeof(struct STU),1,fp);
printf("stu id = %d name = %s sc = %.2f\n",
mystu.id,mystu.name,mystu.score);
fclose(fp);
return 0;
}
使用fread和fwrite完成多个学生信息的录入和输出
#include <stdio.h>
#include "head.h"
int main(int argc, char *argv[])
{
int ret = 0;
FILE * fp = fopen("./abc","r");
struct STU mystu ={0};
while(1)
{
ret = fread(&mystu,sizeof(struct STU),1,fp);
if(ret == 0) break;
printf("stu id = %d name = %s sc = %.2f\n",
mystu.id,mystu.name,mystu.score);
}
fclose(fp);
return 0;
}
#include <stdio.h>
#include "head.h"
int main(int argc, char *argv[])
{
FILE * fp = fopen("./abc","w+");
struct STU mystu ={1,"zhangsan",99.9};
while(1)
{
scanf("%d %s %f",&mystu.id,mystu.name,&mystu.score);
fwrite(&mystu,sizeof(struct STU),1,fp);
fflush(fp);
}
fclose(fp);
return 0;
}
fscanf/fprintf
int fscanf(FILE *stream, const char *format, …);
功能: 该函数可以从指定的stream流对象中获取指定格式format的数据
到本地内存。
参数: stream 获取数据的文件流指针,如果stream == stdin 等价scanf
format 获取数据的格式表,类似与scanf的格式表;
… 获取数据的可变长地址表,类似与scanf的地址表;
返回值:成功 获取的数据个数
失败/结尾 EOF;
#include <stdio.h>
int main(int argc, char *argv[])
{
char name[32] ={0};
int num = 0;
/* printf("请按照格式输入数据:姓名 电话\n");
fscanf(stdin,"%s %d",name,&num);
fprintf(stdout,"姓名: %s 电话:%d\n",name,num);
*/ FILE * fp = fopen("./abc","r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
int ret = 0;
while(1)
{
ret = fscanf(fp,"%s %d",name,&num);
if(ret == EOF) break;
printf("name = %s num = %d \n",name,num);
}
fclose(fp);
return 0;
}
int fprintf(FILE *stream, const char *format, …);
功能:该函数可以将指定格式的数据输出到目标文件流指针上。
参数:stream 要输出数据的目标流文件指针,如果stream == stdout 的是
等价于 printf()
format 输出数据的格式表,类似于printf的格式表;
… 可变长的数据地址表,类似于printf的地址表;
返回值:成功 返回实际输出的数据长度
失败 EOF
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 5;
char *name ="test";
int num = 12431243;
printf("hello %d \n", a);
fprintf(stdout,"world %d \n",a);
FILE * fp = fopen("./abc","a+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
fprintf(fp,"%d %s %d \n",a,name,num);
fclose(fp);
return 0;
}
io操作框架
io操作框架:
fopen() ===》 io ===》fclose()
fgetc()/fputc() **
fgets()/fputs() *****
fread()/fwrite() ****
fscanf()/fprintf() ***
问题1:如何指定位置读写; ===》定位操作;
rewind()===>归位操作:
void rewind(FILE *stream);
功能:该函数可以将指定的文件流指针设置到文件开头位置。
参数:stream 文件流指针管理的文件
返回值:无
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * fp = fopen("./def","r+");
if(NULL == fp)
{
perror("open def");
return -1;
}
char buff[128] ={0};
fputs("nihao",fp);
rewind(fp);
fgets(buff,sizeof(buff),fp);
printf("buff = %s \n",buff);
fclose(fp);
return 0;
}
fseek() ===>定位操作
int fseek(FILE *stream, long offset, int whence);
功能:stream 要定位指针的文件流对象
offset 文件中指针位置的偏移量,单位 是字节
如果 offset >0 则向文件末尾偏移
offset =0 则不移动
offset <0 则向文件开头偏移
whence 要偏移指针的起始位置:
SEEK_SET 表示起始位置是文件开头
SEEK_CUR 表示起始位置是文件当前指针位置
SEEK_END 表示起始位置是文件末尾
返回值:成功 0
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * fp = fopen("./def","r+");
if(NULL == fp)
{
perror("fopen def");
return -1;
}
//int c = fgetc(fp);
//printf("c = %c \n",c);
fseek(fp,400,SEEK_SET);
int c = fputc('x',fp);
printf("c = %c \n",c);
fclose(fp);
return 0;
}
问题:
1、向文件开头偏移越过开头位置的时候名文件指针不动。
2、向文件末尾偏移越过末尾位置,仍然可以写操作 ==》空洞文件
3、定位后的写操作会覆盖原有数据
4、在设备文件上不支持定位操作:29号错误, seek无效
5、以a/a+方式打开的文件上不支持定位操作
rewind == (void) fseek(stream, 0L, SEEK_SET);
ftell() ==>距离函数
long ftell(FILE *stream);
功能:该函数可以返回当前文件指针距离文件开头位置的字节长度
参数:stream要测试的目标文件流对象
返回值:成功 返回当前文件指针距离开头的字节数
失败 -1
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * fp = fopen("./def","r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
long n = ftell(fp);
printf("n1 = %ld \n",n);
fseek(fp,20,SEEK_CUR);
n = ftell(fp);
printf("n2 = %ld \n",n);
fclose(fp);
return 0;
}
#include <stdio.h>
long file_size(char *filename)
{
if(NULL == filename) return 0;
FILE * fp = fopen(filename,"r");
if(NULL == fp) return 0;
fseek(fp,0L,SEEK_END);
long n = ftell(fp);
fclose(fp);
return n;
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Usage %s [file] \n",argv[0]);
return -1;
}
long size = file_size(argv[1]);
printf("file %s size %ld \n",argv[1],size);
return 0;
}
文件结尾判断
int feof(FILE *stream);
功能: 该函数可以用于检测目标文件指针是否到达文件结尾
参数: stream要检测的目标文件流对象
返回值:如果当前已经到文件末尾 则返回真
否则 返回假
注意:该函数一定要在io操作完毕后进行判断。
#include <stdio.h>
int main(void)
{
int c = 0;
FILE *fp = fopen("./def","r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
while(1)
{
c = fgetc(fp);
// if(c == EOF) break;
if(feof(fp)) break;
printf("%c")
}
fclose(fp);
return 0;
}
练习:
在四组io函数中其他3个io函数任选一个测试feof的效果。
#include <stdio.h>
int main(int argc,const char* argv[])
{
char buff[128]={0};
FILE *fp = fopen("./def","r");
if(NULL == fp)
{
perror("fopen");
return -1;
}
while(1)
{
fgets(buff,sizeof(buff),fp);
if(feof(fp)) break;
fputs(buff,stdout);
}
return 0;
}
文件流转置: freopen()
FILE *freopen(const char *path, const char *mode, FILE *stream);
功能:该函数可以将一个已经打开的文件流指针转置成一个新的文件流指针
参数;path 新的文件路径+名称
mode 新的文件打开方式,“r/r+ w/w+ a/a+”
stream 旧的已经存在或者打开的文件流对象。
返回值:成功 返回新的文件流指针
失败 返回NULL;
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * fp = fopen("./abc","r+");
if(NULL == fp)
{
perror("fopen");
return -1;
}
fputs("nihao",fp);
freopen("./def","r+",fp);
fputs("wohao",fp);
fclose(fp);
return 0;
}
主要作用:转置标准输出。 freopen(“xxx”,“w+”,stdout);
问题: xxx文件会一直增长 ===》/dev/null 文件做为目标文件
重新打开标准输出或者转置到日志文件:
/dev/tty ===>stdout的默认文件位置
/var/log/xxx.log