椋鸟C语言笔记#34:文件的随机读写、文件读取结束或失败的判定、缓冲区

萌新的学习笔记,写错了恳请斧正。


目录

文件的随机读写

fseek

使用实例

ftell

使用实例

rewind

文件读取结束或失败的判定

feof

ferror

典型使用方式

文件缓冲区

fflush


文件的随机读写

文件的随机读写是指我们可以控制文件位置指示器(光标)的位置,以完成复杂的读写操作

fseek
#include 
int fseek( FILE* stream, long offset, int origin );

fseek函数用于移动文件位置指示器(光标)的位置,移动成功则返回0,发生错误则光标位置不变、返回非0整数并设置流结构体上的错误指示器

其中,origin是基准位置,可以取值有:SEEK_SET(文件头)、SEEK_CUR(当前光标位置)、SEEK_END(文件尾),一般这三个值分别是0、1、2(最好不要这样写)。而offset为偏移的字节数,代表以origin为基准偏移特定字符,比方说origin为SEEK_CUR且offset为-1时,就将光标左移一个字节。

使用实例

文件被写入“This is a sample.”

#include 
#include 

int main()
{
	FILE* pf = fopen("file.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return EXIT_FAILURE;
	}

	fputs("This is an apple.", pf);
	fseek(pf, 9, SEEK_SET);
	fputs(" sam", pf);

	fclose(pf);
	pf = NULL;
	return EXIT_SUCCESS;
}
ftell
#include 
long ftell( FILE* stream );

ftell用于返回此时文件位置指示器相对文件头的偏移量

使用实例
#include 
#include 

int main()
{
	FILE* pf = fopen("file.txt", "w");
	if (!pf)
	{
		perror("fopen");
		return EXIT_FAILURE;
	}

	fputs("1145141919810", pf);
	printf("%d\n", ftell(pf));
	fseek(pf, 0, SEEK_SET);
	printf("%d\n", ftell(pf));
	fseek(pf, 7, SEEK_CUR);
	printf("%d\n", ftell(pf));

	return EXIT_SUCCESS;
}
rewind
#include 
void rewind( FILE* stream );

rewind函数用于将文件位置指示器返回到文件起始位置

效果等于offset为0且origin为SEEK_SET的fseek函数

文件读取结束或失败的判定

feof
#include 
int feof( FILE *stream );

feof检查流结构体上的文件尾指示器,如果指示器被设置则返回非0值,否则返回0

feof函数的作用是:当文件读取结束,判断结束的原因是否是“遇到文件尾”

请不要在读取过程中使用feof的返回值来判断文件是否读到结束

ferror
#include 
int ferror( FILE* stream );

ferror检查流结构体上的错误指示器,如果指示器被设置则返回非0值,否则返回0

典型使用方式

先判断读取是否结束,如果读取结束则通过feof和ferror判断是遇到文件尾结束还是出错

  1. 判断文本文件是否结束:判断返回值是否为EOF(fgetc)或者NULL(fgets)
  2. 判断二进制文件读取结束:判断fread返回值是否小于实际要读的个数

比方说我们处理文本文件可以:

#include 
#include 

int main()
{
    FILE* fp = fopen("test.txt", "r");
    if (!fp)
    {
        perror("File opening failed");
        return EXIT_FAILURE;
    }

    int c; // 注意:int,非char,要求处理EOF
    while ((c = fgetc(fp)) != EOF)  // 标准C I/O读取文件循环
        putchar(c);

    if (ferror(fp))
        puts("I/O error when reading.");
    else if (feof(fp))
        puts("End of file reached successfully.");

    fclose(fp);
    fp = NULL;
    return EXIT_SUCCESS;
}

处理二进制文件可以:

#include 
#include 

enum { SIZE = 5 };
double a[SIZE] = { 1.,2.,3.,4.,5. };
double b[SIZE];

int main()
{
    FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式
    if (!fp)
    {
        perror("fopen-wb");
        return EXIT_FAILURE;
    }


    fwrite(a, sizeof * a, SIZE, fp); // 写 double 的数组
    fclose(fp);

    fp = fopen("test.bin", "rb");
    if (!fp)
    {
        perror("fopen-rb");
        return EXIT_FAILURE;
    }

    size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组
    if (ret_code == SIZE)
    {
        puts("Array read successfully, contents: ");
        for (int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
        putchar('\n');
    }
    else
    {
        if (feof(fp))
            printf("Error reading test.bin: unexpected end of file\n");
        else if (ferror(fp))
            perror("Error reading test.bin");
    } // error handling

    fclose(fp);
    fp = NULL;
    return EXIT_SUCCESS;
}

文件缓冲区

ANSIC标准采用“文件缓冲系统”来处理数据文件。

也就是说,系统会在内存中为每个打开的文件流创建一个“文件缓冲区”。内存中运行的程序如果想对外存中的文件进行I/O(输入输出)操作,传递的信息就需要通过输入缓冲区和输出缓冲区。

在不刷新缓冲区的情况下,信息只有在填满整个输入/输出缓冲区后,整个缓冲区的信息才会整个打包发送给程序/文件,否则信息将滞留在缓冲区直到被刷新。

刷新缓冲区是指将已经写到输入缓冲区的信息冲入、已经写到输出缓冲区的信息冲出;将尚未写入缓冲区的信息舍弃

由于缓冲区的存在,我们在更新模式(存在标签"+")下打开文件时:

  • 若中间没有fflush函数或文件定位函数,则输出后不应有输入
  • 若中间没有文件定位函数且输入操作没有遇到文件尾,则输入后不应有输出
fflush
#include 
int fflush( FILE* stream );

fflush函数用于刷新缓冲区,成功返回0,否则返回EOF并设置流结构体的错误指示器

如果输入空指针,则刷新所有缓冲区


你可能感兴趣的:(C语言笔记,笔记,c语言,开发语言)