C语言学习第二十三天(文件操作)

1、二进制和文本文件的区别

数据在内存中以二进制的形式存储,如果不加转换的输出到外存,称之为二进制文件

如果要求在外存上以ASCII码的形式存储,则需要在存储前转化。以ASCII字符的形式存储的文件就是文本文件。

我们就用整数10000为例,以ASCII码的形式存储占用5个字节:

00110001 00110000 00110000 00110000 00110000

如果二进制形式输出,就占4个字节:

00000000 00000000 00100111 00010000

2、文件的打开和关闭

流和标准流

程序的数据需要输出到外部设备,还需要从外部设备获取数据,不同的外部设备的输入输出操作是不一样的,所以程序员创造出来了流的概念来方便对各种设备进行操作。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流来操作的,我们想要向流里写入数据或者读取数据,都是要打开流,然后操作

标准流

stdin----标准输入流,在大多环境中从键盘中输入,scanf函数就是从标准输入流中读写数据

stdout--标准输出流,大多数环境下输出至显示器界面,printf函数就是将信息输出到标准输出流中

stderr---标准错误流,大多数环境中输出到显示器界面

stdin、stdout、stderr三个流的类型是:FILE*,称之为文件指针,C语言中,就是通过FILE*的文件指针来维护流的各种操作的

文件指针

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(比如文件的名字,文件的状态以及文件当前的位置等)。这些信息是保存到一个结构体变量中的。该结构体类型是由系统声明的,取名FILE

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节

一般都是通过一个FILE指针来维护这个FILE结构的变量,这样使用起来更加的方便

FILE* pf;//文件指针变量

定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区的信息就可以访问该文件。所以,通过文件指针变量能够间接找到与他相关联的文件

文件的打开和关闭

文件在读取之前应该先打开文件,在使用结束之后应该关闭文件

在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。

我们使用fopen来打开文件,用fclose来关闭文件

//打开文件
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+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件

举一个例子:

#include
int main()
{
	FILE* pFile;
	//打开文件
	pFile = fopen("hehe.txt", "w");
	//文件操作
	if (pFile != NULL)
	{
		fputs("haha", pFile);//将字符串写入到指定的文件流中
		//关闭文件
		fclose(pFile);
	}
	return 0;
}

文件的顺序读写

函数名 功能 适用于
fgetc 字符输入函数 所有输入流
fputc 字符输出函数 所有输出流
fgets 文本行输入函数 所有输入流
fputs 文本行输出函数 所有输出流
fscanf 格式化输入函数 所有输入流
fprintf 格式化输出函数 所有输出流
fread 二进制输入 文件
fwrite 二进制输出 文件

文件的随机读写

fseek

根据文件指针的位置和偏移量来定位文件指针

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

举个例子:

int main()
{
	FILE* pFile;
	pFile = fopen("haha.txt","wb");
	fputs("This is an apple.", pFile);
	fseek(pFile, 9, SEEK_SET);// SEEK_SET和0等同,从文件开始处
	fputs(" sam", pFile);
	fclose(pFile);
	return 0;

}

rewind

这个东东可以让文件指针位置回到文件起始位置

int main()
{
	int n;
	FILE* pFile;
	char buffer[27];
	pFile = fopen("haha.txt", "w+");
	for (n = 'A'; n <= 'Z'; n++)
		fputc(n, pFile);
	rewind(pFile);
	fread(buffer, 1, 26, pFile);
//size_t fread(void *ptr,size_t size,size_t count FILE*stream);
	fclose(pFile);
	buffer[26] = '\0';
	printf(buffer);
	return 0;
}

文件读取结束的判定

易被错误使用的feof

需要注意的是:在文件读取的过程中,不能用feof函数的返回值直接来判断文件是否结束

feof的作用:当文件读取结束的时候,判断是否结束的原因是否是:遇到文件尾结束

1、文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL

fgetc判断是否为EOF

fgetc判断返回值是否为NULL

2、二进制文件的读取结束的判断,判断返回值是否小于实际要读的个数

比如fread判断返回值是否小于实际要读的个数

一个关于文本文件的例子:
 

int main(void)
{
	int c;//注意:int,非char,要求处理EOF
	FILE* fp = fopen("test.txt", "r");
	if (!fp) {
		perror("File opening failed");
		return EXIT_FAILURE;
	}
	//fgetc当读取失败的时候或者遇到文件结束的时候,都会返回EOF
	while ((c = fgetc(fp)) != EOF)
	{
		putchar(c);
	}
	//判断是什么原因结束的
	if (ferror(fp))
		puts("I/O error when reading");
	else if (feof(fp))
	
		puts("End of file reached successfully");
	fclose(fp);

}

二进制文件的例子

#include

enum{SIZE = 5};
int main(void)
{
	double a[SIZE] = { 1.,2.,3.,4.,5. };
	FILE* fp = fopen("test.bin", "wb");//必须用二进制的形式
	fwrite(a, sizeof * a, SIZE, fp);//写double的数组
	//void *ptr 指向要写入的数据的指针
    //size_t size 每个数据项的大小
	//size_t count 要写入的数据项的数量
	//FIle*stream 要写入数组的文件指针
	fclose(fp);
	double b[SIZE];
	fp = fopen("test.bin", "rb");
	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 {//error handling
		if (feof(fp))
			printf("Error reading test.bin: unexpected end of file\n");
		else if (ferror(fp)) {
			perror("Error reading test.boin");
		}
	}
	fclose(fp);
}

这里需要再次区分ferror和feof的区别

feof如果没有到达文件尾,则返回0,到达文件尾,返回1 

ferror无错误时返回0,有错误出现时,返回一个非零值

文件缓冲区

ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统就是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小是根据C编译系统决定的。

#include
#include

int main()
{
	FILE* pf = fopen("test.txt","w");
	fputs("abcdef", pf);//先将代码放在缓冲区
	printf("睡眠十秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区的时候,才将输出缓冲区的数据写到文件(磁盘)
	//fflush在高版本已经不能使用了
	printf("再睡眠十秒 ,这个时候,再次打开test.txt文件,文件有内容了\n");
	Sleep(10000);
	fclose(pf);
	//fclose在关闭文件的时候,也会刷新缓冲区
	pf = NULL;

	return 0;
}

因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂ 件。 如果不做,可能导致读写⽂件的问题。

你可能感兴趣的:(学习)