待续
概览
- 文件内容、模式、格式;fopen的第二个参数表示的读写模式、fclose;文件指针;
- getc与putc;fprintf与fscanf;fgets与fputs;rewind、ungetc;fseek与ftell;fgetpos与fsetpos;fread与fwrite;feof与ferror;随机访问实例,自写s_gets
示例
- 文件内容、模式、格式;fopen的第二个参数表示的读写模式、fclose;文件指针;
- 文件内容、模式、格式都有两种,二进制和文本,二进制文件存的就是内存中的二进制数据,文本则是以字符(一个字节)的形式储存文件。二进制模式是逐字节读取和写入的,使用
fread
和fwrite
函数;文本模式是以字符形式读取和写入的,有很多函数,诸如:scanf
、printf
、getc
、putc
、fscanf
、fprintf
、fgets
、fputs
等。UNIX、Linux只有一种文件格式,不区分二进制和文本,都以\n
为换行符,文件结尾都是统计信息,而其他操作系统可能不是这样的,所以C用文本模式读取文件时会进行一个映射(比如mac中换行\r
映射为\n
),而用二进制模式读取时存的是什么就是什么,没有这样的映射。 - 关于
fopen
函数第一个参数是目标文件指针,第二个参数表示读写模式,如下图所示:r
表示读,w
表示清空写,a
表示追加写,它们三个都加上+
表示的是更新模式,既能读又能写,注意区分写的方式。然后对于二进制文件,就在这三个字母后面加b
即可(与+
谁先谁后没影响),对于只有一种文件格式的UNIX系统加不加b
都一样。 打开一个文件的过程,你知道吗?FILE
其实是个结构体,包含了文件信息,创建的文件的缓冲区的信息和填充程度的信息等。
#include<stdio.h>
int main(int argc, char * argv[]){
FILE * f = fopen("test.dat","wb+");
double d[3] = {0.1,4.5,6.8};
printf("成功写入%zd个\n",fwrite(d,sizeof(double),3,f));
double e[3];
// 这里要注意,此时文件指针的位置!
rewind(f);
printf("成功读取%zd个\n",fread(e,sizeof(double),3,f));
for(int i=0; i<3; i++){
printf("%f ",e[i]);
}
fclose(f);
}
// 结果示例
// 成功写入3个
// 成功读取3个
// 0.100000 4.500000 6.800000
- getc与putc;fprintf与fscanf;fgets与fputs;rewind、ungetc;fseek与ftell;fgetpos与fsetpos;fread与fwrite;feof与ferror;随机访问实例,自写s_gets
int getc(FILE *); // 成功,则返回该字符的ASCII值,失败返回-1(EOF)
int putc(int, FILE *); // 成功,则返回该字符的ASCII值,失败返回-1(EOF)
int fprintf(FILE * __restrict, const char * __restrict, ...);
int fscanf(FILE * __restrict, const char * __restrict, ...);
char *fgets(char * __restrict, int, FILE *); // 成功返回字符串首地址,失败返回0(NULL)
int fputs(const char * __restrict, FILE * __restrict); // 失败返回-1(EOF)
void rewind(FILE *); // 回到文件首
int ungetc(int, FILE *); // 将一个字符放回文件流
int fseek(FILE *, long, int); // 指定文件指针的位置,后跟字节数(相对偏移量,对照SEEK_CUR、SEEK_END、SEEK_SET)
long ftell(FILE *); // 获取当前文件指针所在位置
int fgetpos(FILE * __restrict, fpos_t *); // 弥补 ftell 字节数为20亿(long)的缺陷,可以更多
int fsetpos(FILE *, const fpos_t *);
size_t fread(void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream); // 例如数组的地址、元素的大小、元素的个数;成功返回读取到的个数,失败返回-1(EOF)
size_t fwrite(const void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream);
int feof(FILE *); // 它们俩的检测是在返回EOF之后再判断是文件末尾还是其他错误
int ferror(FILE *);
#include<stdio.h>
int main(int argc, char * argv[]){
FILE * f = fopen("test.dat","wb+");
double d[3] = {0.1,4.5,6.8};
printf("成功写入%zd个\n",fwrite(d,sizeof(double),3,f));
double e[3];
// 这里要注意,此时文件指针的位置!
rewind(f);
printf("成功读取%zd个\n",fread(e,sizeof(double),3,f));
for(int i=0; i<3; i++){
printf("%f ",e[i]);
}
printf("%d ",getc(f)); // 此时返回了EOF
if(feof(f)){ // 判断是不是文件末尾
printf("eof");
}
if(ferror(f)){ // 判断是不是错误
printf("error");
}
fclose(f);
}
// 结果示例
// 成功写入3个
// 成功读取3个
// 0.100000 4.500000 6.800000 -1 eof
#include<stdio.h>
#include<string.h>
#define MAXSIZE 41
char * s_gets(char * str, int n);
int main(int argc, char * argv[]){
char c[MAXSIZE];
s_gets(c,MAXSIZE);
fputs(c,stdout);
char d = getchar();
putchar(d);
}
char * s_gets(char * str, int n){
char * res;
char * pos;
res = fgets(str,n,stdin);
pos = strchr(str,'\n'); // 关键部分!
if(res){
if(pos){
*pos = '\0';
}
else{
while(getchar() != '\n'){
continue;
}
}
}
return res;
}
// 结果示例
// ./test
// ahgahgkjahdgjkahjgkahlkajhlkafjgklr agjakldg
// ahgahgkjahdgjkahjgkahlkajhlkafjgklr ah
// h
随机访问实例,用到了自定义缓冲区的函数,参数中涉及0、1、2(完全缓冲、行缓冲、无缓冲,_IOFBF、_IOLBF、_IONBF)。
int setvbuf(FILE * __restrict, char * __restrict, int, size_t);
C 库函数 int fflush(FILE *stream)
刷新流 stream 的输出缓冲区,只要最近一次流的操作不是输入就行。