前言
接着上个文件操作(1)继续讲
1.所有流变为标准流
如上图所示,有些函数是适用于所有流的,既然这样,那肯定也适用于标准流了,若要用上标准流,这时就要用上stdout与stdin了
int main()
{
char arr[20] = { 0 };
fscanf(stdin, "%s", arr);
fprintf(stdout, "%s", arr);
return 0;
}
fscanf(stdin, “%s”, arr)这个表示的是从键盘输入的读取到arr中
fprintf(stdout, “%s”, arr)这个表示的是打印在屏幕上
屏幕和键盘就是标准流了
2.文件的随机读写
为什么在文件中读取数据时,会接着上次读取,而不是每次都从开头开始读取,那是因为有个东西记录了位置,其实背后就是文件指针记录的,所以只要掌握的文件指针的位置,就可以想读取哪里就读取哪里了,实现文件的随机读取。
2.1fssek
int fseek( FILE *stream, long offset, int origin );
文件每读取一个就会前进一步,文件指针就会前进一步,这个函数的作用主要是定位文件指针,把文件指针设置在某个位置,origin 是起点,表示把文件指针从origin 的位置向后移offset步,也就是affset个字节,如果offset是负数就表示向前移,而origin 总共只能取三个值,SEEK_CUR表示文件指针当前位置,SEEK_END表示文件指针结尾位置,SEEK_SET表示文件指针开头位置。
int main()
{
FILE*pf=fopen("text.txt", "w");
for (char c = 'a'; c <= 'z'; c++)
{
fputc(c, pf);
}
flose(pf);
pf = NULL;
return 0;
}
先在文件中填充数据,现在开始操作。
int main()
{
FILE* pf = fopen("text.txt", "r");
char c = 0;
c=fgetc(pf);//指向a
printf("%c", c);
fseek(pf, 6, SEEK_SET);//从开头后移六个位置
c = fgetc(pf);
printf("%c", c);
fseek(pf, -2, SEEK_END);//最后位置前移2步
c = fgetc(pf);
printf("%c", c);
fseek(pf, -4, SEEK_CUR);//当前位置前移4步
c = fgetc(pf);
printf("%c", c);
fclose(pf);
pf = NULL;
return 0;
}
2.3ftell
long ftell( FILE *stream );
这个函数很简单,返回值就是当前文件指针相对于起始位置的偏移量
还是以文件中为a~z举例
int main()
{
FILE* pf = fopen("text.txt", "r");
char c = 0;
c = fgetc(pf);
c = fgetc(pf);
c = fgetc(pf);
int far = ftell(pf);
printf("%d", far);
fclose(pf);
pf = NULL;
return 0;
}
因为fgetc使用了三次,所以理所应当当前文件指针相对于起始位置3个偏移量
2.4 rewind
void rewind( FILE *stream );
这个函数的主要作用是把当前文件指针位置调回到起始位置
int main()
{
FILE* pf = fopen("text.txt", "r");
char c = 0;
c = fgetc(pf);
printf("%c", c);
rewind(pf);
c = fgetc(pf);
printf("%c", c);
fclose(pf);
pf = NULL;
return 0;
}
这里打印了两个a就可以说明文件指针在第二次就回到了起始位置了
3.文件读取结束的判定
因为fgetc的返回值是那个字符,fgets的返回值是那个数组地址,fread的返回值是读取的字符个数,所以判断读取是否结束时的循环条件为fgetc是否为EOF,fgets是否为NULL,fread的返回值是否小于读取个数。
int main()
{
FILE* pf = fopen("text.txt", "r");
char c = 0;
while ((c = fgetc(pf)) != EOF)
{
;
}
fclose(pf);
pf = NULL;
return 0;
}
如上图,循环条件为c = fgetc(pf)) != EOF,那么这样的话,退出循环条件就只有两种可能了,第一正常读取到了结尾,正常结束了,c=EOF了,第二就是读取错误,强制退出了,会返回一个错误码。那如何分辨这两种情况呢,第一是看返回值是不是NULL,第二就是用feof与ferror这两个函数了。
int feof( FILE *stream );
int ferror( FILE *stream );
feof是判断是否读取到文件末尾的函数,如果是读取到结尾了,正常结束了,feof会返回真
ferror是判断是否读取错误的,如果读取错误,就会返回真
int main()
{
FILE* pf = fopen("text.txt", "r");
char c = 0;
while ((c = fgetc(pf)) != EOF)
{
;
}
//法一
if (c == EOF)
{
printf("正常读取到了结尾\n");
}
//法二
if (feof(pf))
{
printf("正常读取到了结尾\n");
}
else if (ferror(pf))
{
printf("文件读取错误\n");
}
fclose(pf);
pf = NULL;
return 0;
}
看这个代码,按照常理来说,没什么错误的话,就是正常读取到了末尾,果然是。
但法一并不可靠,并不是判断是否读到文件末尾和读取错误最好的方法,所以最好还是用法二,下面举一个读取错误的例子,可以r改为w。
int main()
{
FILE* pf = fopen("text.txt", "w");
int c = 0;
while ((c = fgetc(pf)) != EOF)
{
;
}
//法二
if (feof(pf))
{
printf("正常读取到了结尾\n");
}
else if (ferror(pf))
{
printf("文件读取错误\n");
}
fclose(pf);
pf = NULL;
return 0;
}
4.文件缓冲区
先举个实例
int main()
{
FILE* pf = fopen("text.txt", "w");
for (char c = 'a'; c <= 'z'; c++)
{
fputc(c, pf);
}
pf = fopen("text.txt", "w");
int c = 0;
while ((c = fgetc(pf)) != EOF)
{
printf("%c",c);
}
fclose(pf);
pf = NULL;
return 0;
}
看上面这个程序,我想在同一个程序实现r与w,先存入数据到文件中,在读取打印出,结果却是没有打印,什么原因呢,请直接往下看
向文件中读取数据或者存入数据时,并不是直接就会传递的,他们中间是有个桥梁的,比如向文件中存入数据时,会先将数据放入文件缓冲区中,等到fclose时,才会放入文件中,所以在程序先先写入数据,再读取数据,是不太可能实现的,这里再看一个实例。
int main()
{
FILE* pf = fopen("text.txt", "w");
for (char c = 'a'; c <= 'z'; c++)
{
fputc(c, pf);
}
Sleep(20000);
pf = fopen("text.txt", "w");
int c = 0;
while ((c = fgetc(pf)) != EOF)
{
printf("%c", c);
}
fclose(pf);
pf = NULL;
return 0;
}
这个程序的中间加入了Sleep函数,意思就是在fputc之后程序暂停一下,你现在去文件那里看一下,你会发现文件那里根本还没有存入数据,如果自己嫌操作麻烦,可以去看一下文件操作(2)的视频,这个现象与我们的分析是一样的,最后fclose之后,文件中就有数据了,因为fclose相当于刷新了一下,缓冲区,把数据全部都放在文件中了。
那有什么函数可以实现文件缓冲区的刷新吗,答案是有的,这个函数就是fflush。可以实现文件缓冲区的刷新。
int main()
{
FILE* pf = fopen("text.txt", "w");
for (char c = 'a'; c <= 'z'; c++)
{
fputc(c, pf);
}
Sleep(10000);
fflush(pf);
Sleep(10000);
pf = fopen("text.txt", "w");
int c = 0;
while ((c = fgetc(pf)) != EOF)
{
printf("%c", c);
}
fclose(pf);
pf = NULL;
return 0;
}
如上,增加fflush这个函数,这次用与上次相同的操作,你就会发现,第二次休眠中,文件中已经就有数据了。这就是文件缓冲区。
那为什么要有文件缓冲区呢,因为把数据先放在一起,在一起打包带走,要比每次都运输一点点的效率要高。比如日常生活中,明明可以跑一遍就做完了,你愿意跑10遍吗?
这也是为什么文件打开了一定要fclose的原因,要刷新缓冲区嘛。
总结
好了,C语言文件操作就结束了!!!!