标准IO:
每个文件都有一个结构体 struct FILE ,来 记录自己的信息, in out
FILE
{
char *in;
//输入缓存区
char *out;
//输出的缓存区
}
文件本身都是存在 磁盘上,配备的缓存区在 内存中
有什么好处?
减少对磁盘的损耗
【linux标准IO实验,linux程序设计之文件标准IO】效率会提高,内存的速度快过于磁盘
大家知道有两个缓存区就行了,
缓存区有多大?
全缓存区: 默认情况
行缓存区:printf.
无缓存区: perror
FILE *fp;
fp = fopen("hello.txt","r");
if(fp==NULL)
{
perror("fopen");
//可以打印出错的原因
return -1;
}
标准IO 的函数接口:API函数
打开,关闭,读,写,判断文件结束,冲洗fflush(清理缓存区的意思)
2.1 打开 fopen
FILE *fopen(const char *path, const char *mode);
const char *path: 要打开的文件的路径
const char *mode: 以什么方式打开
r : 以只读的方式打开,如果这个文件不存在,报错
打开文件之后,位置指针在 文件开头
r+ : 以读写的方式打开,其他跟 r 方式相同
w : 以只写的方式打开,如果文件不存在,创建
如果有,原来的内容会被清空,位置指针在 文件开头
w+: 以读写的方式打开,其他跟 w 方式相同
a: append 追加,只写打开,文件不存在,创建
打开后,位置指针在 文件末尾
a+: 与a相同,读操作的话,位置指针在文件开头
写操作的话,位置指针在文件末尾
返回值:
打开文件正确:返回 打开的文件的 FILE 结构体指针
打开失败: 返回NULL
下面三个 文件系统会自动打开,大家可以直接使用
FILE * stdin;
标准输入设备,一般是键盘
FILE *stdout;
标准输出设备,一般是屏幕
FILE *stderr;
标准出错设备,一般是屏幕
2.2 文件的关闭fclose
int fclose(FILE *fp);
参数: fp 前面用fopen打开的文件返回值;
有 fopen ,就需要有 fclose ,否则文件中数据,极有可能被破坏
2.3 文件的读写: 字符,行,格式化,普通
2.3.1 按照字节读: fgetc, getchar , 写 fputc, putchar
(1) fgetc:
int fgetc(FILE *stream);
含义:读出stream 中的一个字节。
返回: 该字符的 ASCII
当读出一个字符以后,位置指针会自动往后跳一个字节。
特殊字符:EOF (-1), 如果读到了 EOF 字符, 代表到了文件尾部.
(2) getchar
int getchar(void);
含义: 从键盘读一个字符
返回: 该字符的 ASCII
getchar() <=======>fgetc(stdin)
(3) fputc
int fputc(int c, FILE *stream);
含义: 把 c 字符,写到 stream 对应的 文件流中
FILE *fp;
hg
char ch;
fp = fopen("1.txt","r");
if(fp==NULL)
{
perror("fopen");
return -1;
}
while((ch = fgetc(fp)) != EOF)
{
fputc(ch, stdout);
}
(4) putchar
int putchar(int c);
把 c 字符 输出到 屏幕: 等价于 fputc(ch, stdout);
main函数的运行参数:
argc: 参数的个数
char *argv[] : 字符串 数组, 保存的就是参数的列表
int main(int argc, char *argv[])
{
int i;
printf("argc=%d\n",argc);
//main函数参数的个数
for(i=0;
i
{
printf("%s\n",argv[i]);
}
}
课堂练习:
fgetc, fputc, 实现将某个文件的第一行拷贝到一个新文件中,两个文件名 通过运行参数设置,
fcopybychar.c
3.3.2 按行: 读 fgets, gets 写: fputs, puts
(1) gets
char *gets(char *s);
含义: 从标准输入文件(键盘)读一行,存放在 s 指向的内存中,并在最后加上一个 '\0'
char buf[10];
gets(buf);
这个函数有隐患,会有bugs,现在基本不用这个函数
//从键盘输入 "hello world"
(2) fgets
char *fgets(char *s, int size, FILE *stream);
含义: 从 stream 文件流,可以是stdin, 读一行,读到 \n,或者是 EOF 就不读了
存放在 s 所指向的内存中, size 要读的大小
存放的时候,会在最后加上一个 '\0'
位置指针:挪到 还没读的 第一个字节。
如果一行没读完,下次会接着读。直到 '\n' 或者 EOF 为止
返回值: 正确 返回 s
返回NULL : 代表 读到了文件末尾,或者出错
例子:
int main(int argc, char argv[])
{
FILE *fpr,fpw;
char buf[10];
int i;
fpr = fopen("1.txt","r");
if(fpr==NULL)
{
perror("fopen");
return -1;
}
fgets(buf,10,fpr);
for(i=0;
i<10;
i++)
{
printf("buf[%d]=%d\n",i,buf[i]);
}
}
(3) int puts(const char *s);
//写到屏幕
int fputs(const char *s, FILE *stream);
含义: 把 s 所指向的内存的内容,写到 stream 对应的 流中
3.3.3 指定格式: 输入 scanf , fscanf, sscanf
输出 printf, fprintf, sprintf
(1) fscanf:
int fscanf(FILE *stream, const char *format, ...);
含义: 从 stream 里 按照 format 读出数据
参数: ... 若干个读出来的数据项 地址
返回值: 真正读出来的 数据项 个数.
有个文件:每行包含三项学生的数据:序号,姓名,成绩
int main()
{
FILE *fp;
int id;
char name[20];
float score;
fp = fopen("stu1.dat","r");
if(fp==NULL)
{
perror("fopen");
return -1;
}
fscanf(fp,"%d%s%f",&id,name,&score);
printf("id=%d,name=%s,score=%f\n",id,name,score);
fclose(fp);
}
(2) sscanf
int sscanf(const char *str, const char *format, ...);
含义:从str 为首地址的存储空间 按照格式 读出数据.
下位机 发送数据到 上位机:一般 一串字符串:
str = “temp:30,humi:60,people:1”
char buf[20];
int temp, humi,peo;
sscanf(str,"%s:%d,%s:%d,%s:%d",buf,&temp,buf,&humi,buf,&peo)
if(temp>50)
报火警
if(peo==1)
报110
(3) fprintf
int fprintf(FILE *stream, const char *format, ...);
含义: 将... 的数据按format格式写入到文件stream中
返回:实际输出到文件中 的字符个数
练习: 从键盘输入 学号,姓名,成绩 到 新文件中 stu1.dat 中 , fprintf.c
int main()
{
int id,n;
char name[20];
float score;
FILE *fp;
fp = fopen("stu1.dat","w");
printf("please input no:");
scanf("%d",&id);
printf("please input name:");
scanf("%s",name);
printf("please input score:");
scanf("%f",&score);
n = fprintf(fp,"%d%s%f",id,name,score);
//返回:实际输出到文件中 的字符个数
printf("n=%d\n",n);
fclose(fp);
}
(4) sprintf
int sprintf(char *str, const char *format, ...);
含义:将... 的数据按照 format 格式 写入 str 为首 的地址
返回值: 实际输出到 str 里的字节个数.
(5) snprintf
int snprintf(char *str, size_t size, const char *format, ...);
含义:与sprintf相同,多了一个参数 size 表示每次写多少个字节 -1。 (最后要存放一个 '\0')
注意:所谓的安全,可以控制size,别越界
%12d -----> 输出的宽度 是12个字节,前面补 空格
%012d------> 输出的宽度是12个字节, 前面补 0
int main () {
char a[16];
size_t i;
i = snprintf(a, 13, "%012d", 12345);
// 第 1 种情况
printf("i = %lu, a = %s\n", i, a);
// 输出:i = 12, a = 000000012345
i = snprintf(a, 9, "%012d", 12345);
// 第 2 种情况
printf("i = %lu, a = %s\n", i, a);
// 输出:i = 12, a = 00000001
return 0;
}
4.1 按任意结构读写: 以上的都是文本结构,所谓的文本文件 内容都是 ASCII 码编码的。 .c .txt
对于二进制文件, .bmp .jpg .exe 需要用 fread, fwrite 来读写
(1) fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
含义: 从stream 文件流中读到信息,存放到 ptr 为首地址的 地方
参数:
ptr: 从stream文件流中 读到的信息存放到这里
size:读数据的 单位 (例如:每次读一个字节:1, 每次读一个学生结构体 sizeof(struct STU)
nmemb:读多少个size (上面的单位)
返回值: 真正读了多少个单位,不是字节数
读完之后,文件stream 位置指针 向后移动 (返回值*size) 个字节。
(2) fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
含义: 要将 ptr 位首地址的数据 写到 stream 文件中
参数: size : 要写的数的单位
nmemb : 写多少个 单位
返回值: 真正写了多个个单位
文件的位置指针 向后移动 (返回值*size) 个字节。
学生的结构体:
typedef struct stu_info
{
int id;
char name[20];
float score;
}STU;
STU stu[3], stu2[3];
课堂练习: 从键盘输入三个学生数据,存储到 stu中, 写入新文件 stu.dat .
从stu.dat 读数据 fread存放到 stu2数据中,
显示到屏幕。
rewind(fp);
重新把位置指针 放到文件开头
typedef struct stu_info
{
int id;
char name[20];
float score;
}STU;
int main()
{
FILE *fp;
int i;
STU stu[3],stu2[3];
fp = fopen("stu.dat","w+");
for(i=0;
i<3;
i++)
{
printf("please input id score name\n");
scanf("%d%f%s",&stu[i].id,&stu[i].score,stu[i].name);
}
fwrite(stu,sizeof(STU),3,fp);
rewind(fp);
fread(stu2,sizeof(STU),3,fp);
for(i=0;
i<3;
i++)
{
printf("%d: %d %f %s\n",i+1,stu2[i].id,stu2[i].score,stu2[i].name);
}
}
4.2 如何判断文件是否结束 feof(), 不能再用 EOF (-1)
int feof(FILE *stream);
含义: 判断 stream 流 是否结束,
如果结束 返回 非0 true
如果未结束 返回 0 flase
if/while( feof(fp)) 如果 fp结束 为 真
if/while(!feof(fp)) 如果 fp 没结束 为真
feof 不太稳定,已经结束了,判断还多一次. (因为 最后还有一次 EOF )
一般是先读 再判断 ,如果先判断 再读 就会多一次
冲洗函数 fflush
int fflush(FILE *stream);
含义: 如果stream 是输入流,则 丢弃stream 的缓冲区的内容
某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲,
但是并非所有编译器都要支持这个功能(linux 下的 gcc 就不支持),
在scanf语句中间可以使用以下两行代码清除缓冲区,防止前面的错误影响后面的输入
scanf("%*[^\n]");
scanf("%*c");
如果stream 是输出流,则 写入 stream
fflush 在 scanf("%c") 之前 ,用的比较多, 避免某些按键,(回车,空格)作为char 类型输入的后面的变量中。
fflush(NULL);
是把该进程 所有打开的 流 同步
int main()
{
printf("hello world");
fflush(stdout);
//强制把 输出缓存区的内存 写入 stdout
while(1);
}
定位函数 fseek ,想把 位置指针移动到 文件的任意位置
rewind(fp);
重新把位置指针 放到文件开头
int fseek(FILE *stream, long offset, int whence);
参数: stream 文件流
offset: 偏移多少字节, 正 往后移动, 100 往后移动 100 个字节
负 往前移动, -100 往前移动 100 个字节
whence : 从哪里开始移动 (参照物)
SEEK_SET 文件头
SEEK_CUR 当前位置
SEEK_END 文件尾
fseek(fp, 6, SEEK_SET);
//定位到文件头6个字节的位置
fseek(fp, 0, SEEK_END);
//定位到文件末尾
long ftell(FILE *stream);
含义: 告诉你,返回 stream 流的位置指针的 位置。
有什么方法可以获取到文件的大小?
fseek(fp, 0, SEEK_END);
//定位到文件末尾
filelength= ftell(fp);