文件操作知识点

本文详细介绍了程序运行时内存管理、文件的分类(程序文件和数据文件)、二进制文件与文本文件的区别、流的概念、标准输入输出流的使用、文件的打开与关闭、顺序读写函数、文件缓冲区原理以及如何处理文件操作中的缓冲问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:  我们应该知道一般程序运行时产生的数据是存放在内存中的。但是如果程序关闭后这些内存就会被系统回收,如果内存内的有用的数据没有被保存下来,这些数据就丢失了。所以这个时候我们就可以使用磁盘来储存我们的数据。

目录

程序文件的分类

文件名

 二进制文件和文本文件

文件的打开和关闭 

流的概念: 

标准流:

文件打开和关闭 

文件的顺序读写函数:

fseek 

ftell 

rewind

 文件读取结束的判定

文件缓冲区的概念


程序文件的分类

程序文件一般分为两类:程序文件和数据文件。

程序文件: 源代码(.c文件) 、目标文件(.obj) 、 可执行程序(.exe)

数据文件:用于保存数据的文件。 区别于用来运行程序的程序文件。 

文件名

每个文件都有一个路径和名称, 这是用来寻找该文件的途径。 便于用户使用。

文件名一般包含三个部分: 文件路径, 文件主干, 文件后缀

 二进制文件和文本文件

 一个数据,我们都知道数据在计算机中是以二进制的方式进行储存, 如果这个数据从计算机中不加转换的就输出到外存的文件中, 就是二进制文件。 

但是如果这个数据转换成ASCII码的形式, 那么就是文本文件。 

字符一律通过ASCII码的形式进行储存, 数值则ASCII和二进制储存皆可。

比如10000这个数值, 如果用ASCII就花费五个字节

但是用二进制就只需要4个字节。 

文件的打开和关闭 

流的概念: 

程序数据进行输出到外部设备时, 以及程序的数据从外部设备获取数据时, 不同的外部设备的输入输出操作是各不相同的。 为了方便对各种设备进行方便的操作呢, 人们抽象出了流的概念。 可以将其抽象成一条充满字符的河。

我们通过向这条河中写数据, 或者从这条河中拿数据, 这就是数据的输入和输出。 

标准流:

我们打开一个程序, 那么我们就默认打开了三个流

一个是标准输入流stdin:标准输⼊流,scanf就是从标准输入流读取数据。 

一个是标准输出流stdout: 标准输出流, printf就是将数据输出到标准输出流中。

一个是标准错误流stderr:用于输出错误信息。

三个流的类型是FILE*, 通常称为文件指针。 

文件打开和关闭 

在文件读写之前应该打开文件,在文件读写之后应该关闭文件。  

在我们打开文件之后, 都会返回一个FILE*类型的指针指向该文件, 就建立了文件和指针之间的练习。

FILE是一个结构体类型。 它里面的成员变量是用来保存文件信息的。 当我们打开了一个文件, 就会在内存区生成一块对应的文件信息区。然后这块这块文件信息区的类型就是FILE, 里面储存的就是打开的文件的信息。 fopen就是返回这块文件信息区的地址。 然后我们是由FILE* 类型的指针进行接收:

打开文件函数:

FILE*  fopen( const char* filename, const char* mode);

关闭文件函数:

int fclose( FILE* stream);

其中mode为文件的打开方式。

有以下几种打开方式:

⽂件使⽤⽅式含义如果指定⽂件不存在
“r”(只读)为了输⼊数据,打开⼀个已经存在的⽂本⽂件出错
“w”(只写)为了输出数据,打开⼀个⽂本⽂件建⽴⼀个新的⽂件
“a”(追加)向⽂本⽂件尾添加数据建⽴⼀个新的⽂件
“rb”(只读)为了输⼊数据,打开⼀个⼆进制⽂件出错
“wb”(只写)为了输出数据,打开⼀个⼆进制⽂件建⽴⼀个新的⽂件
“ab”(追加)向⼀个⼆进制⽂件尾添加数据建⽴⼀个新的⽂件
“r+”(读写)为了读和写,打开⼀个⽂本⽂件出错
“w+”(读写)为了读和写,建议⼀个新的⽂件建⽴⼀个新的⽂件
“a+”(读写)打开⼀个⽂件,在⽂件尾进⾏读写建⽴⼀个新的⽂件
“rb+”(读写)为了读和写打开⼀个⼆进制⽂件出错
“wb+”(读写)为了读和写,新建⼀个新的⼆进制⽂件建⽴⼀个新的⽂件
“ab+”(读写)打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写建⽴⼀个新的⽂件

文件的顺序读写函数:

fgetc()                        字符输入函数                      所有输入流

fputs()                        字符输出函数                      所有输出流

fgets()                        字符串输入函数                   所有输入流

fputs()                        字符串输出函数                  所有输出流

fscanf()                      格式化输入函数                  所有输入流

fprintf()                       格式化输出函数                  所有输出流

fread()                        二进制输入函数                  文件

fwrite()                       二进制输出函数                   文件

上述得所有所有输入流包括标准输入流和其他输入流(如文件输入流), 上述的所有输出流包括标准输出流和其他输出流(如文件输出流)。 

这些函数的使用方式如下:

向文件中读取数据, 然后保存到内存中开辟的空间中的函数是fgetc, fgets, fscanf, fread。


int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//
	char arr[20] = { 0 };

	fscanf(pf, "%s", arr);

	printf("%s", arr);

	//
	fclose(pf);
	return 0;
}

fscanf格式化输入, 区别于其他的读取流插入函数的特点是它可以插入任意类型的值。 读入格式由我们自己控制。 而像fgets, fgetc只能读取字符。将文件中的数据读取到arr中。arr代表了要读取到的内存空间, pf代表从pf文件流中读取。



int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//

	char arr[20] = { 0 };
	while(fgets(arr, 10, pf) != NULL) 
	{
		printf("%s", arr);
	}


	//
	fclose(pf);
	return 0;
}

fgets, 向内存中获取数据, pf是文件流, 从pf文件流中获取十个字符到arr之中。

arr代表了内存空间, pf是文件流。

fgets如果读取正常, 返回的是存储读到的字符数组的内存的地址, 如果读取过程中读到了文件末尾, 或者发生了错误。 就返回NULL。



int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//

	char ch;

	while ((ch = fgetc(pf)) != EOF) 
	{
		printf("%c", ch);
	}



	//
	fclose(pf);
	return 0;
}

fgtc同样是从文件中读取字符的操作 , 它是一个一个的读取, 在一次的程序运行之中, 流的位置,会随着使用它进行读取向后偏移。

fgetc如果读取正确, 返回的是读取字符的ASCII码的值。 读取的过程中读到了文件的末尾或者发生了错误,就返回EOF。

向文件中写数据的函数是: fputs, fputc, fprintf, fwrite



int main() 
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL) 
	{
		perror("fopen");
		return 1;
	}
	//
	fputs("abcdef\n", pf);
	fputs("hello world\n", pf);
	
	//
	fclose(pf);
	return 0;
}


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

	char arr[] = { "hello world\nabcdefg\nslfdjk" };
	
	fputs(arr, pf);



	//
	fclose(pf);
	return 0;
}

fseek 

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

fseek是根据流的初始位置和偏移量来定位一个文件指针位置。

以下为实例:

int main()

{

        FILE* Open = fopen("text", "wb");

        fputs("hello world", Open);

        fseek(Open, 5, SEEK_SET);

        fputs("earth", Open);

        fclose(Open);

        return 0;

} 

 这里传的参数SEEK_SET, 其实就是文件的起始位置。 然后fseek的第二个位置的参数是偏移量, 这里的fseek代表的意思就是让Open文件指针指向文件相对于起始位置的第五个偏移量的位置, 也就是第六个字符的位置。

ftell 

long int ftell ( FILE * stream );

返回文件指针相对于起始位置的偏移量.

下图为实例:

int main()

{

        FILE* Open;

        long size;

        Open = fopen("text", "rb");

        if(Open == NULL)

        {

                exit(-1 );

         }

        fseek(Open, 0, SEEK_END);

        size  =  ftell(Open);

        fclose(Open);

        printf("%d", size);

}

 ftell就是告诉我们文件指针此时所在的相对起始位置的偏移量。如图就是使用size进行接收Open相对于起始位置的偏移量。

rewind

void rewind (FILE* stream);

让文件指针的位置返回到起始位置。

以下为实例:

int main()

{

        FILE* Open = fopen("text", "w+");

        char arr[27];

        for(int n = 'A'; n <= 'Z'; n++)

        {

                fputc(n, Open);

        }

        rewind(Open);

        fread(arr, 1, 26, Open);

        fclose(Open);

        arr[26] = '\0';

        puts(arr);

        return 0;

} 

使用rewind之后,  open文件留置针会重新指向文件的起始位置。

第一个for循环是将A~Z输出到文件中, 然后此时文件流指向了文件的末位置的下一个位置, 然后rewind, 此时文件流就指向了起始位置。 然后fread的操作就是将文件中的数据以二进制的形式读取到arr代表的空间之中。  

注意:fread的声明:size_t fread(void* ptr, size_t size, size_t num, FILE* pf)

这里的size代表每一个要读取的元素的大小, num代表要读取多少个元素。

 文件读取结束的判定

应该要牢记, 判断文件是否读取结束不要直接使用feof。

feof的作用是:当文件读取结束的时候, 判断文件读取结束的原因是不是文件到了末尾。 

当读取文本文件时, 判断返回值:EOF(fgetc)或者NULL(fgets)。

当读取二进制文件时, 判断返回值是否小于实际要读取的个数。

        feof的返回值是int类型。 检测是否遇到了文件末尾,一个文件流读取文件到了末尾会设置一个标志。对于feof来说, 一个文件流的文件末尾标志。如果被设置的话, 那么就返回一个非零的值。 

        所以对于feof来说, 如果返回了一个非零的值, 那么说明文件读取正常结束了。

        否则的话就应使用ferror, ferror用来检测流上的错误标记是否被设置。 如果错误标记被设置, 那么ferror返回一个非零的值。

如图是读取正常结束代码


int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == nullptr) 
	{
		return -1;
	}
	//
	char ch = 0;
	while (ch = fgetc(pf) != EOF) 
	{
		//
	}
	if (feof(pf)) 
	{
		cout << "文件正常结束" << endl;
	}
	else if (ferror(pf)) 
	{
		perror("fputc");
	}
	


	return 0;
}

如贴图为读取错误代码, 这个时候我文件打开方式是读, 但是我进行了写的操作。


int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == nullptr) 
	{
		return -1;
	}
	//
	char ch = 0;
	fputc(ch, pf);
	if (feof(pf)) 
	{
		cout << "文件正常结束" << endl;
	}
	else if (ferror(pf)) 
	{
		perror("fputc");
	}
	

	return 0;
}

文件缓冲区的概念

ANSIC标准采用缓冲文件系统处理数据文件的。缓冲文件系统就是指,系统自动的在内存中为程序中的每一个正在使用的文件开辟一块”文件缓冲区”,从内存向磁盘输入数据应该先通过文件缓冲区, 等到文件缓冲区满了再一起送到磁盘上。如果是磁盘向计算机读入数据,则从磁盘文件中读取数据到文件缓冲区, 然后等到满了再一起读到计算机内存中。 

int main()

{

        FILE*pf = fopen("text", "w");
        fputs("abcdef", pf);
        printf("正在写数据,这时候我们可以打开⽂件,因为要观看是否写上内容,因为有10秒缓冲。但是我们发现⽂件没有内容。\n");
        Sleep(10000);
        printf("刷新缓冲区\n");
        fflush(pf);//这个时候其实写上了。
        printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
        Sleep(10000);
        fclose(pf);//刷新缓冲区
        pf = NULL;
        return 0;
}

因为有缓冲区的存在, c语言操作文件的时候, 需要做刷新缓冲区或者再文件操作结束的时候关闭文件。 否则可能导致读写文件的问题。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值