当前位置: 首页 > article >正文

【C语言】文件操作(超万字解析+形象图解)

文章目录

  • 前言
  • 一、文件的简单介绍以及使用文件的意义
    • 1.文件的简单介绍
      • 1.1 程序文件
      • 1.2 数据文件
    • 2.二进制文件和文本文件
    • 3.为什么使用文件?
  • 二、文件缓冲区
  • 三、文件指针
  • 四、流的概念
    • 1.流
    • 2.标准流
  • 五、文件的打开和关闭
  • 六、文件的顺序读写
    • 1. 顺序读写函数介绍
      • 1.1 fputc和fgetc
      • 1.2 fputs和fgets
      • 1.3 fprintf和fscanf
      • 1.4 fwrite和fread
  • 七、文件的随机读写
    • 1.文件中的内部位置指示器
    • 2.fseek
    • 3.ftell
    • 4.rewind
  • 八、文件读取结束的判定
    • 1.feof函数
    • 2.ferror函数


前言

一、文件名和文件的分类
二~三、文件缓冲区、文件指针
四、抽象出流的概念简化对各种输入输出设备的操作
五、fopen和fclose函数的介绍
六~七、文件的顺序读写和随机读写(文章主要篇幅)
八、文件读取结束的判定


一、文件的简单介绍以及使用文件的意义

1.文件的简单介绍

文件存储在磁盘(硬盘)上。
一个文件要有一个唯一的文件标识,以便用户识别和引用,我们把这个文件标识称为“文件名”。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如以下图示中文件的文件名是:C:\Program Files\Everything\Changes.txt
它的文件路径是:C:\Program Files\Everything
文件名主干是:Changes
文件后缀是:.txt

在这里插入图片描述
在程序设计中,我们一般谈的文件有两种:程序文件数据文件(从文件功能的角度来分类的)。

1.1 程序文件

程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

1.2 数据文件

文件的内容不一定是程序,也可以是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
C语言中的文件操作针对的是数据文件。
在未学习文件操作之前,处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果输出到显示器(屏幕)上。
其实有时候我们会把信息输出到磁盘的文件中,当需要的时候再从磁盘的文件中把数据读取到内存中使用,这里使用到的文件就是数据文件

2.二进制文件和文本文件

根据数据的组织形式,数据文件又被划分为文本文件二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
⼀个数据在文件中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节。

在这里插入图片描述

3.为什么使用文件?

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件

二、文件缓冲区

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

注:每一个文件在内存中只有一个缓冲区,在向文件输出数据时,它就作为输出缓冲区,在从文件输入数据时,它就作为输入缓冲区。

在这里插入图片描述

三、文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了⼀个相应的文件信息区(实际是一个结构体变量),用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 FILE.
例如,VS2013 编译环境提供的 stdio.h 头文件中有以下的文件类型申明:

struct _iobuf {
	char* _ptr;
	int _cnt;
	char* _base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char* _tmpfname;
};
typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件的时候,系统会根据文件的情况自动创建⼀个FILE结构的变量,并将该文件的相关信息填充到这个FILE结构的变量中,使用者不必关心细节。⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下面我们可以创建⼀个FILE*的指针变量:

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

定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是⼀个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。
图示如下:
在这里插入图片描述

四、流的概念

1.流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念。
C程序针对文件、屏幕、键盘等的数据输入输出操作都是通过流操作的。
⼀般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

在这里插入图片描述

2.标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流
• stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。
• stdout - 标准输出流,在大多数的环境中输出至显示器(屏幕)界面,printf函数就是将信息输出到标准输出流中。
• stderr - 标准错误流,在大多数环境中输出到显示器(屏幕)界面。
这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为文件指针类型。

为什么这几个流的类型会是 FILE * 呢?
这是因为为了简化用户对输入输出设备的操作,使用户不必去区分各种输入输出设备之间的区别,操作系统把各种设备都统一当作“文件”来处理。所以C语言中会通过 FILE * 的文件指针来维护流的各种操作。

在这里插入图片描述

五、文件的打开和关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个 FILE * 的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC 规定用 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+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

注:
1、用w方式打开文件有两种情况,如果原来已存在一个以该文件名命名的文件,则在打开该文件的同时将该文件中原有的内容清空,再向该文件中写入指定内容;如果原来不存在该文件,则新创建一个以指定的名字命名的文件并打开。
2、如果希望向指定文件末尾添加新的数据(不希望删除文件中原有数据),则应该用 a 方式打开指定文件。
3、如果fopen函数打开文件失败,它会返回一个NULL,并且记录打开失败的错误类型,失败的原因可能是:用 r 方式打开一个并不存在的文件;磁盘出故障;磁盘已满无法创建新文件等等。可以使用perror函数打印错误原因。
4、如果打开文件后没有使用fclose函数关闭文件就结束程序,那么向文件中写入的数据可能会丢失。因为,在向文件写数据时,是先将数据输出到文件缓冲区,待缓冲区充满后才正式输出给文件。如果当数据未充满缓冲区时程序就结束运行,就有可能使缓冲区中的数据丢失。但如果我们在使用完文件之后,及时使用fclose函数将文件关闭就可以避免写入文件数据丢失的情况,因为fclose函数在关闭文件的同时,还会把缓冲区中的数据输出到磁盘文件,然后才撤销文件信息区。

代码1:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	//打开⽂件
	pFile = fopen("myfile.txt", "w");
	if (pFile == NULL)//pFile为NULL,证明打开文件失败
	{                 //文件都打开失败了,就要及时停止程序并分析失败原因
		perror("fopen");//perror函数可以打印错误信息
		exit(1);//exit()函数的作用是提前退出程序,只要括号内数字不为0都表示异常退出
	}
	//向文件中写入内容
	fputs("One Piece", pFile);//后面会详细介绍fputs函数
	//关闭⽂件
	fclose(pFile);
	pFile = NULL;
	return 0;
}

检查创建的这个代码程序的文件列表(需要打开的文件没有明确指定文件路径,fopen函数会默认在当前代码程序的文件列表里寻找指定文件。例如:myfile.txt 就是没有明确指定文件路径的文件,fopen函数会直接在当前代码程序的文件列表里寻找它;C:\Program Files\Everything\Changes.txt 是明确指定文件路径的文件,fopen函数会按照此路径精确寻找该文件
在这里插入图片描述
发现这个代码程序的文件列表中没有"myfile.txt"这个文本文件
在这里插入图片描述
接下来执行代码
在这里插入图片描述
fopen(“myfile.txt”, “w”),分析这句代码,这句代码的意思是以 w 方式打开文件名为 myfile.txt 的文件,因为我们是以 w 方式打开文件,如果原来不存在该文件,则新创建一个以指定的名字命名的文件并打开。接下来我们再打开这个代码程序的文件列表,发现确实新创建了一个文件名为 myfile.txt 的文件
在这里插入图片描述
fputs(“One Piece”, pFile),这句代码的意思是向指定文件中写入内容“One Piece”。我们检查文件中内容,发现与写入的内容一致。

在这里插入图片描述
代码2:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	char arr[20] = { 0 };
	//打开⽂件
	pFile = fopen("myfile.txt", "r");
	if (pFile == NULL)//pFile为NULL,证明打开文件失败
	{                 //文件都打开失败了,就要及时停止程序并分析失败原因
		perror("fopen");//perror函数可以打印错误信息
		exit(1);//exit()函数的作用是提前退出程序,只要括号内数字不为0都表示异常退出
	}
	//把指定文件中的内容读入到程序数据中
	fgets(arr, 10, pFile);
	printf("%s\n", arr);
	//关闭⽂件
	fclose(pFile);
	pFile = NULL;
	return 0;
}

代码1已经将内容“One Piece”写入 myfile.txt 文件中,我们这个代码建立在这个基础上
在这里插入图片描述
fgets(arr, 10, pFile); printf(“%s\n”, arr); 这两句代码的意思是把文件中的内容读入到字符串arr中,然后打印字符串内容,发现打印出的字符串中内容与文件中内容一致,读入成功。
在这里插入图片描述
再演示一种打开文件失败的案例,我们先把代码1中创建的文件 myfile.txt 删去,再运行函数
在这里插入图片描述
因为运行程序前我们已将文件 myfile.txt 删去,所以程序运行时,是用 r 方式打开一个并不存在的文件,fopen函数打开文件会失败,就会返回一个NULL,并且记录打开失败的原因。我们发现 perror 函数打印出的错误原因正是:没有这样的文件(也就是找不到指定文件)。
在这里插入图片描述

六、文件的顺序读写

1. 顺序读写函数介绍

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

1.1 fputc和fgetc

int fputc ( int character, FILE * stream );
1、功能:将字符写入到流的内部位置指示器指示的位置,然后内部位置指示器自动前进1位。
如果发生写入错误,则返回 EOF 并设置错误指示符 (ferror)。

int fgetc ( FILE * stream );
1、功能:读取成功返回字符的ASCll码值
2、返回值:返回指定流的内部位置指示器当前指向的字符。然后,内部文件位置指示器前进到下一个字符
如果位置指示符位于文件末尾,则函数返回 EOF(返回类型为 int 以容纳特殊值 EOF) 。
如果发生其他读取错误,该函数还会返回 EOF,但会设置其错误指示符 (ferror)

补充 在文件的所有有效字符后有一个文件尾标志,这个文件尾标志用标识符EOF(end of file)表示,EOF在 stdio.h 头文件中被定义为-1

代码1:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("myfile.txt", "w");
	if (pFile == NULL)
	{                 
		perror("fopen");
		exit(1);
	}
	//把程序数据中的内容写入到指定文件中
	fputc('W', pFile);
	fputc('u', pFile);
	fputc('K', pFile);
	fputc(111, pFile);//'o'的ACSll码值是111
	fputc(110, pFile);//'n'的ACSll码值是110
	fputc(103, pFile);//'g'的ACSll码值是103
	fclose(pFile);
	pFile = NULL;
	return 0;
}

程序运行之后,检查对应文件中的内容,发现已经按顺序依次写入了相应字符。
在这里插入图片描述
以 w 的方式打开文件,文件中的内容一开始会是空的。文件中有一个内部位置指示器,当文件为空时,它指向文件的开头,用fputc函数将字符写入到文件的内部位置指示器指示的位置,然后内部位置指示器自动前进1位,指向写入字符的后一个位置,再使用fputc函数向文件中写入字符会重复以上过程。 图示如下:
在这里插入图片描述

代码2:
(代码1已经将内容“WuKong”写入 myfile.txt 文件中,代码2建立在这个基础上)

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("myfile.txt", "r");
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	//把指定文件中的内容读入到程序数据中
	int n = 0;
	while((n=fgetc(pFile))!=EOF)//循环读取文件"myfile.txt"中的内容并打印
	{                           //直到遇到文件末尾,也就是读取到EOF才停止
		printf("%c", n);
	}
	printf("\n");
	fclose(pFile);
	pFile = NULL;
	return 0;
}

在这里插入图片描述
当用 r 方式打开文件时,内部位置指示器一开始会指向文件开头的字符。fgetc函数会返回文件的内部位置指示器当前指向的字符,然后,内部文件位置指示器前进到下一个字符,再使用fgetc函数读取文件中字符时会重复以上过程。如果位置指示符位于文件末尾,则函数返回 EOF。 图示如下:
在这里插入图片描述
代码3:
(fgetc函数适用于所有输入流,fputc函数适用于所有输出流 ,代码3将演示它们是如何在其它流中使用)

#include <stdio.h>
int main()
{
	printf("用户输入:");
	char a = fgetc(stdin);//stdin(标准输入流)————键盘
	char b = fgetc(stdin);//fgetc函数获取用户用键盘输入的数据
	char c = fgetc(stdin);
	char d = fgetc(stdin);
	char e = fgetc(stdin);
	char f = fgetc(stdin);

	printf("输出:");
	fputc(a, stdout);//stdout(标准输出流)————屏幕
	fputc(b, stdout);//fputc函数将字符输出到屏幕
	fputc(c, stdout);
	fputc(d, stdout);
	fputc(e, stdout);
	fputc(f, stdout);
	fputc('\n', stdout);
	return 0;
}

在这里插入图片描述

1.2 fputs和fgets

int fputs ( const char * str, FILE * stream );
1、功能:将 str 指向的字符串写入流。
该函数从指定的地址 (str) 开始复制,直到到达终止字符 ‘\0’ 。 ‘\0’ 不会被复制到流中。
2、返回值:成功后,将返回非负值。
出错时,该函数返回 EOF 并设置错误指示符(ferror)。

char * fgets ( char * str, int num, FILE * stream );
1、功能:从流中读取字符并将其作为字符串存储到 str 中,直到读取(num-1)个字符,或者到达换行符或文件末尾,以先发生者为准。终止字符 ‘\0’ 会自动附加到复制到 str 的字符之后。
2、返回值:读取成功后,该函数返回 str。
换行符会使 fgets 提前停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中。
如果在尝试读取字符的过程中遇到文件结尾,则读取提前停止。如果在还没有读取到任何字符之前发生这种情况,则返回的指针为NULL(并且 str 的内容保持不变)。
如果发生读取错误,则设置错误指示符(ferror)并返回NULL(但 str 指向的内容可能已改)。

代码1:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("myfile.txt", "w");
	if (pFile == NULL)
	{                 
		perror("fopen");
		exit(1);
	}
	//向文件中写入内容
	fputs("Monchi D Luffy", pFile);
	fclose(pFile);
	pFile = NULL;
	return 0;
}

程序运行之后,检查对应文件中的内容,发现字符串"Monchi D Luffy"成功写入文件。
在这里插入图片描述

代码2:
(代码1已经将内容“Monchi D Luff”写入 myfile.txt 文件中,代码2建立在这个基础上)

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	char arr[20] = { 0 };
	pFile = fopen("myfile.txt", "r");
	if (pFile == NULL)
	{                
		perror("fopen");
		exit(1);
	}
	//把指定文件中的内容读入到程序数据中
	fgets(arr, 20, pFile);
	printf("%s\n", arr);
	fclose(pFile);
	pFile = NULL;
	return 0;
}

在这里插入图片描述
代码3:
(fgets函数适用于所有输入流,fputs函数适用于所有输出流 ,代码3将演示它们是如何在其它流中使用)

#include <stdio.h>
int main()
{
	char arr[20] = "xxxxxxxxxxxxxxxxxxx";
	printf("用户输入:");
	fgets(arr, 20, stdin);
                          
	printf("输出:");
	fputs(arr, stdout);
	return 0;
}

在这里插入图片描述
用户输入:one piece,然后按下回车(回车是’\n’字符)。
在这里插入图片描述
换行符’\n’会使 fgets函数 提前停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中。终止字符 ‘\0’ 会自动附加到复制到 str 的字符之后。
在这里插入图片描述
在这里插入图片描述

1.3 fprintf和fscanf

int fprintf ( FILE * stream, const char * format, … );
将格式化数据写入流
int fscanf ( FILE * stream, const char * format, … );
从流中读取格式化数据

这两个函数最好对比标准化输入输出函数printf和scanf函数来学习:

int printf ( const char * format, … );
标准化输出函数(默认向 标准输出流stdout<屏幕> 输出内容)
int scanf ( const char * format, … );
标准化输入函数(默认从 标准输入流stdin<键盘> 输入内容)

代码1:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("myfile.txt", "w");
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	//向文件中写入内容
	fprintf(pFile, "%d %lf %c %s", 100, 3.14, 'D', "wukong");
	fclose(pFile); 
	pFile = NULL;
	return 0;
}

在这里插入图片描述
代码2:
(代码2建立在代码1的基础上)

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	int n = 0;
	double f = 0.0;
	char c = '0';
	char arr[10] = { 0 };
	pFile = fopen("myfile.txt", "r");
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	//把指定文件中的内容读入到程序数据中
	fscanf(pFile, "%d %lf %c %s", &n, &f, &c, arr);
	printf("n=%d f=%lf c=%c arr为%s\n", n, f, c, arr);
	fclose(pFile); 
	pFile = NULL;
	return 0;
}

在这里插入图片描述
代码3:

#include <stdio.h>
int main()
{
	int n = 0;
	double f = 0.0;
	char c = '0';
	char arr[10] = { 0 };
	printf("用户请输入数据:");
	fscanf(stdin, "%d %lf %c %s", &n, &f, &c, arr);//从stdin中格式化输入内容(相当于scanf函数)
	fprintf(stdout, "n=%d f=%lf c=%c arr为%s\n", n, f, c, arr);
	return 0;                                      //向stdout格式化输出内容(相当于printf函数)
}

在这里插入图片描述

1.4 fwrite和fread

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
1、功能:将 count 个元素(每个元素的大小为 size 字节)从 ptr 指向的内存块写入流中。
2、返回值:返回成功写入的元素个数。
如果返回值小于 count,证明写入过程出现错误,将设置错误指示符(ferror)。

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
1、功能:从流中读取 count 个元素(每个元素的大小为 size 字节),并将它们存储在 ptr 指定的内存块中。
2、返回值:返回成功读取的元素个数。
如果此数字与 count 参数不同,大概率是在读取时到达文件末尾,也可能是发生了读取错误。
如果发生读取错误,会设置错误指示符(ferror)。

代码1:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	int arr[5] = { 1,2,3,4,5 };
	pFile = fopen("myfile.txt", "wb");//wb:以二进制形式写
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	//向文件中写入内容
	fwrite(arr, sizeof(int), 5, pFile);//把arr数组中的5个整数以二进制形式写入文件中
	fclose(pFile);
	pFile = NULL;
	return 0;
}

代码运行之后,我们直接打开对应文件查看,发现是乱码,这是正常的,因为二进制文件直接查看就是看不懂的。
在这里插入图片描述

那么我们要如何才能查看一个二进制文件中存储的内容呢?
答案是使用专门的二进制编辑器查看

因为vs2022自带二进制编辑器,下面我将详细展示如何使用vs上的二进制编辑器查看二进制文件。

1.右键源文件,选择添加,再选择现有项
在这里插入图片描述
2.选择想查看的二进制文件,再点击确定
在这里插入图片描述
3.右键添加进的二进制文件,点击打开方式
在这里插入图片描述
4.选择二进制编辑器,点击确定
在这里插入图片描述
我们可以看到二进制文件中的内容正是我们写入的1,2,3,4,5这5个整数
在这里插入图片描述

代码2:
(代码2建立在代码1的基础上,我们要读取代码1在二进制文件中存储的内容)

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	int arr[5] = { 0 };
	pFile = fopen("myfile.txt", "rb");//rb:以二进制形式读
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	//把指定文件中的内容读入到程序数据中
	int i = 0;
	while (fread(arr + i, sizeof(int), 1, pFile))//fread函数会返回成功读取的元素个数
	{                                        //每次让fread函数读取1个整数
		printf("%d ", arr[i]);               //它读取成功就会返回1,循环就继续
		i++;                                 //当读取到文件末尾没有数据可读取时返回0,此时循环停止
	}
	printf("\n");
	fclose(pFile);
	pFile = NULL;
	return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

七、文件的随机读写

1.文件中的内部位置指示器

在介绍文件的顺序读写中的fgetc和fputc函数中提到了文件的内部位置指示器的概念。
在顺序读写中,我们不能主动改变内部位置指示器指向的位置,只能一直向前顺序读取文件中内容或向文件写入内容,例如:在写文件时,我们只要向文件中写入内容,内部位置指示器就会自动向前走,我们只能按顺序一直向前写入内容,而不能向后回退去修改已经写入文件中的内容。
如果我们想在文件中的任意位置读写数据,得使用到文件的随机读写函数,这些随机读写函数能够将文件的内部位置指示器按需要移动到任何位置,就可以实现随机读写。

2.fseek

int fseek ( FILE * stream, long int offset, int origin );
修改流对应文件中的内部位置指示器指向的位置,使其指向修改后的新位置。
新位置是通过向 origin 指定的起始位置添加 offset (偏移量,单位是字节)来确定的。

起始点名字用数字表示
文件开始位置SEEK_SET0
文件当前位置SEEK_CUR1
文件末尾位置SEEK_END2

注: 文件当前位置就是文件中的内部位置指示器此时正指向的位置

FILE * fp = fopen ( “example.txt” , “wb” );
fseek(fp,8,SEEK_SET); //将内部位置指示器移动到离文件开始位置向前偏移8字节处
fseek(fp,8,0);
fseek(fp,-12,1); //将内部位置指示器移动到离文件当前位置向后偏移12字节处
fseek(fp,-16,2); //将内部位置指示器移动到离文件末尾位置向后偏移16字节处

使用示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "w");
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	fputs("This is an apple.", pFile);
	fseek(pFile, 11, SEEK_SET);//将内部位置指示器移动到离文件开始位置向前偏移11字节处
	fputs("juice", pFile);
	fclose(pFile);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

3.ftell

long int ftell ( FILE * stream );
返回文件当前位置,就是文件中的内部位置指示器指向的位置。
当前位置用相对于文件开头的位移量来表示。

使用示例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "w");
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	fputs("This is an apple.", pFile);
	long int n = ftell(pFile);//返回内部位置指示器相对于文件开头的位移量
	printf("%ld\n", n);
	fclose(pFile);
	return 0;
}

在这里插入图片描述

4.rewind

void rewind ( FILE * stream );
让文件内部位置指示器回到文件的起始位置

#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "w");
	if (pFile == NULL)
	{
		perror("fopen");
		exit(1);
	}
	fputs("This is an apple.", pFile);
	rewind(pFile);
	fputs("What", pFile);
	fclose(pFile);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

八、文件读取结束的判定

1.feof函数

int feof ( FILE * stream );
1、功能:检测文件尾标志是否已被读取过
2、返回值:如果文件尾标志已被读取,返回非零值;否则返回0

在文件读取过程中,不能用feof函数的返回值直接来判断文件是否结束。
feof 函数的作用是:当文件读取结束的时候,判断读取结束的原因是否是:遇到文件尾结束。

判断文件读取是否结束应该以 fgetc、fgets 和 fread 函数的返回值为准:
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如: • fgetc 判断是否为 EOF . • fgets 判断返回值是否为 NULL .
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如: • fread判断返回值是否小于实际要读的个数。

文件读取结束有两种原因:
1、遇到文件末尾结束(文件尾标志已被读取,正常结束)
2、文件读取失败,出错,提前结束(文件尾标志未被读取)

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

2.ferror函数

在调用各种输入输出函数时,如果出现错误,除了函数返回值有所反映外,还会设置错误指示符 (ferror),可以使用 ferror 函数来检查。 ferror函数的函数原型如下:

int ferror(FILE * fp)
1、功能:测试输入输出函数是否出现错误
2、返回值:未出错是0;出错为非0。

在执行fopen函数打开文件时,ferror函数的初始值自动置为0。
对同一个文件每一次调用输入输出函数,均产生一个新的ferror函数值,因此,应当在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。

注:设置错误指示符 (ferror)的意思就是把ferror函数值置为非0



http://www.kler.cn/a/386251.html

相关文章:

  • Java I/O(输入/输出)——针对实习面试
  • Suricata
  • Shell 脚本中的大小写陷阱:为什么 ${PWD} 而不是 ${pwd}?
  • JavaWeb后端开发知识储备1
  • 将Excel文件的两个表格经过验证后分别读取到Excel表和数据库
  • 【stable diffusion部署】超强AI绘画Stable Diffusion,本地部署使用教程,完全免费使用
  • Jest进阶知识:React组件的单元测试
  • 万字长文详解JavaScript基础语法--前端--前端样式--JavaWeb
  • redis 写入权限配置
  • 常用的 Lambda 表达式案例解析
  • 《 C++ 修炼全景指南:十九 》想懂数据库?深入 B 树的世界,揭示高效存储背后的逻辑
  • 不加锁解决线程安全
  • AWS账号安全:如何防范与应对账号被盗风险
  • 【mysql相关】
  • 使用ChatGPT神速精读文献,12个高阶ChatGPT提示词指令,值得你复制使用
  • 哪些人群适合考取 PostgreSQL 数据库 PGCM 证书?
  • 【C++练习】使用海伦公式计算三角形面积
  • CDN到底是什么?
  • 《IDE 使用技巧与插件推荐》
  • 从xss到任意文件读取
  • vue组件传参的八种方式详细总结
  • qt QFile详解
  • 拓扑排序(C++类封装+数组模拟队列和邻接表)
  • 代码随想录之双指针刷题总结
  • wordpress判断page页与非page页
  • 【图论】图的C++实现代码