C语言学习笔记——文件

目录

  • [1 文件的概念](#1 文件的概念)
  • [2 程序文件和数据文件](#2 程序文件和数据文件)
  • [3 二进制文件和文本文件](#3 二进制文件和文本文件)
  • [4 流](#4 流)
    • [4.1 流的概念](#4.1 流的概念)
    • [4.2 标准流](#4.2 标准流)
  • [5 文件信息区和文件指针](#5 文件信息区和文件指针)
  • [6 处理文件的库函数](#6 处理文件的库函数)
    • [6.1 fopen](#6.1 fopen)
    • [6.2 fclose](#6.2 fclose)
    • [6.3 fgetc](#6.3 fgetc)
    • [6.4 fputc](#6.4 fputc)
    • [6.5 fgets](#6.5 fgets)
    • [6.6 fputs](#6.6 fputs)
    • [6.7 fscanf](#6.7 fscanf)
    • [6.8 fprintf](#6.8 fprintf)
    • [6.9 fread](#6.9 fread)
    • [6.10 fwrite](#6.10 fwrite)
    • [6.11 fseek](#6.11 fseek)
    • [6.12 ftell](#6.12 ftell)
    • [6.13 rewind](#6.13 rewind)
    • [6.14 feof 和 ferror](#6.14 feof 和 ferror)
  • [7 文件缓冲区](#7 文件缓冲区)

1 文件的概念

在磁盘中,用于保存数据的工具就是文件

比如,下图的 test.txt 就是一个文件,保存了 1 2 3 4 5 6 7 8 9 10 这样的数据

2 程序文件和数据文件

从程序设计的角度上来说,文件主要分为程序文件和数据文件
程序文件:用于保存程序代码的文件,比如.c后缀的源文件,.h后缀的头文件
数据文件:用于保存其它数据的文件,比如上文提到的 test.txt 文件

3 二进制文件和文本文件

在C语言编程中,对文件进行操作时,主要有二进制文件和文本文件两个对象,它们都属于数据文件
文本文件:数据在保存到文件时,会先将该数据按字节为单位转换为字符,再以ASCII码的形式保存
二进制文件:数据在保存到文件时,会直接以二进制序列的形式保存

举例:1000在文本文件和二进制文件中的不同保存形式

4 流

4.1 流的概念

由于计算机在运行时,会与各种各样的外部设备交流,所以这会使得程序员需要掌握这些设备的输入输出方式,对程序员的要求大大提高,为避免这种现象的发生,提出了流的概念,流向程序员提供了一种便捷的输入输出方式,程序员只需要通过流,就可以直接与外部设备进行交互,不需要关心具体输入输出的细节

4.2 标准流

标准流主要有3个,分别是 stdin,stdout,stderr
stdin:标准输入流,用来从键盘上读取数据,scanf 所使用的流就是它
stdout:标准输出流,用来向屏幕输出数据,printf 所使用的流就是它
stderr:标准错误流,用来在屏幕上显示错误信息

C语言的程序启动时,默认就开启了这三个流

5 文件信息区和文件指针

打开文件时,会在内存中生成一个文件信息区,文件信息区本质是一个结构体(名为FILE ),保存了文件的各种信息

用户可以通过指向文件信息区的文件指针来访问对应的文件,文件指针的类型为FILE *

6 处理文件的库函数

6.1 fopen

fopen 的函数声明如下:

c 复制代码
FILE* fopen (const char* filename, const char* mode);

功能

打开一个文件

参数

filename 指要打开的文件的名称

mode 指文件的打开方式

返回值

若文件打开成功,则会返回指向该文件文件信息区的文件指针

若文件打开失败,则会返回空指针

文件的打开方式

值 功能 文件不存在时
"r" 指定本次打开要进行 读文件 操作,对象为文本文件 出错
"w" 指定本次打开要进行 写文件 操作并清空文件数据,对象为文本文件 创建新文件
"a" 在文件末尾添加新内容,对象为文本文件 创建新文件
"rb" 指定本次打开要进行 读文件 操作,对象为二进制文件 出错
"wb" 指定本次打开要进行 读文件 操作,对象为二进制文件 创建新文件
"ab" 在文件末尾添加新内容,对象为二进制文件 创建新文件
"r+" 指定本次打开要进行 读文件和写文件 操作,对象为文本文件 出错
"w+" 指定本次打开要进行 读文件和写文件 操作,对象为文本文件 创建新文件
"a+" 在文件末尾添加新内容,对象为文本文件 创建新文件
"rb+" 指定本次打开要进行 读文件和写文件 操作,对象为二进制文件 出错
"wb+" 指定本次打开要进行 读文件和写文件 操作,对象为二进制文件 创建新文件
"ab+" 在文件末尾添加新内容,对象为二进制文件 创建新文件

举例:以读的形式,打开一个名为 "test.txt" 的文本文件

c 复制代码
#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r"); //以读的方式打开文件
	if (pf == NULL) //防止使用空指针
		assert(pf);

	fclose(pf); //关闭文件
	pf = NULL;

	return 0;
}

6.2 fclose

c 复制代码
int fclose ( FILE * stream );

功能

用于关闭打开的文件

参数

stream 为指向要关闭文件的文件指针

6.3 fgetc

c 复制代码
int fgetc ( FILE * stream );

功能

用来从文件中获取一个字符

参数

stream 为指向要读取文件的文件指针

返回值

若读取成功,返回读取到的字符

若读取失败或者遇到文件末尾,返回EOF

举例:从文件中读取一个字符并输出

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
		assert(pf);

	printf("%c", (char)fgetc(pf));

	fclose(pf);
	pf = NULL;

	return 0;
}

6.4 fputc

c 复制代码
int fputc ( int character, FILE * stream );

功能

用来向文件中写入字符

参数

character 为要写入的字符

stream 为指向要操作的文件的文件指针

返回值

若操作成功,则返回写入的字符

若操作失败,则返回EOF

举例:写一个字符到文件内

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
		assert(pf);

	fputc('a', pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

6.5 fgets

c 复制代码
char * fgets ( char * str, int num, FILE * stream );

功能

用来从文件中读出 num-1 个字符,放入 str 指向的字符串内,最后自动补 '\0'

参数

str 为指向存放字符的字符串的指针

num 为要读的字符数量,实际读取数量为 num-1

stream 为指向要读的文件的文件指针

返回值

若操作成功,则返回存放读取字符的字符串首地址

若操作失败,则返回空指针

举例:读取文件中的 4 个字符到字符串内

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
		assert(pf);

	char str[5] = " ";
	fgets(str, 5, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

6.6 fputs

c 复制代码
int fputs ( const char * str, FILE * stream );

功能

将字符串的内容写到文件中

参数

str 为指向字符串的指针

stream 为指向要写的文件的指针

返回值

操作成功时,返回非负值

操作失败时,返回EOF

举例:读取一个字符串到文件内

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
		assert(pf);

	char str[20] = "hello world";
	fputs(str, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

6.7 fscanf

c 复制代码
int fscanf ( FILE * stream, const char * format, parameterList );

功能

根据给出的格式,从文件中读取内容到后方的变量中

参数

stream 为指向要读的文件的文件指针

format 为需要用户指定的格式,比如%d,%s等

paramterList 为要存放数据的变量,可以有1个或多个

返回值

返回成功读取的数据的量

如果在读取的中途出错或遇到文件尾,会设置 ferror 或 feof 指示器

如果一个数据都没有读取成功,返回EOF

举例:读取文件中的数据到变量中

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
		assert(pf);

	char str[20] = " ";
	fscanf(pf, "%s", str);

	fclose(pf);
	pf = NULL;

	return 0;
}

6.8 fprintf

c 复制代码
int fprintf ( FILE * stream, const char * format, parameterList);

功能

根据给出的格式,将对应变量中的值写入文件中

参数

stream 为指向要写的文件的指针

format 为需要用户指定的格式,比如%d,%s等

paramterList 为要读取的变量,可以有1个或多个

返回值

操作成功,则返回成功写入的字符的数量

操作失败,返回负数

举例:写入数据到文件中

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
		assert(pf);

	char str[20] = "hello world";
	printf("%d", fprintf(pf, "%s", str));

	fclose(pf);
	pf = NULL;

	return 0;
}

6.9 fread

c 复制代码
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

功能

从文件中读取 count 个 size 大小的数据到 ptr 指向的空间内,但是是以二进制的形式进行读取

参数

ptr 为指向最终存放数据的空间的指针

size 是读取的每个元素的大小,单位为字节

count 是读取的元素个数

stream 是指向要读取的文件的文件指针

返回值

返回成功读取的元素的个数

举例:从文件中读取5个4字节的数据至数组中

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
		assert(pf);

	int arr[5] = { 0 };
	fread(arr, sizeof(int), 5, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

6.10 fwrite

c 复制代码
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

功能

从 ptr 指向的空间内读取 count 个 size 大小的数据并以二进制的形式写到文件内,

参数

ptr 为指向要读取的空间的指针

size 每个元素的大小

count 为元素的个数

stream 为指向要写入数据的文件的指针

返回值

返回成功写入的元素的个数

举例:向文件中写入5个4字节的数据

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
		assert(pf);

	int arr[5] = { 1,2,3,4,5 };
	fwrite(arr, sizeof(int), 5, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

6.11 fseek

c 复制代码
int fseek ( FILE * stream, long int offset, int origin );

功能

通过文件指针的位置,偏移量来移动文件指针

参数

stream 是指向文件的文件指针

offset 是偏移量,用来计算新的文件指针位置

origin 是文件指针的位置,有以下三个取值

取值 含义
SEEK_SET 文件的起始位置
SEEK_CUR 当前文件指针的位置
SEEK_END 文件的末尾

返回值

操作成功,返回0

操作失败,返回非0

举例 :读取下面这个文件的d字符

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
		assert(pf);

	fseek(pf, 3, SEEK_CUR);
	printf("%c", fgetc(pf));

	fclose(pf);
	pf = NULL;

	return 0;
}

图解:

注意事项

若 origin 取值为 SEEK_END ,则偏移量需要给入负数,才能读取前面的值

6.12 ftell

c 复制代码
long int ftell ( FILE * stream );

功能

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

参数

stream 为指向文件的文件指针

返回值

成功返回当前文件指针的位置

错误返回 -1L

举例 :计算下面文件的文件尾相对于起始位置的偏移量

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
		assert(pf);

	while (fgetc(pf) != EOF); //使文件指针遍历到结尾
	printf("%d", ftell(pf));

	fclose(pf);
	pf = NULL;

	return 0;
}

图解:

6.13 rewind

c 复制代码
void rewind ( FILE * stream );

功能

将文件指针重置到文件的开始位置

参数

stream 为指向文件的文件指针

返回值

无

举例 :输出两次下列文件的内容

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
		assert(pf);

	char ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}
	printf("\n", ch);
	rewind(pf); //重置文件指针的位置
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

6.14 feof 和 ferror

c 复制代码
int feof ( FILE * stream );

功能

判断文件的读取是否因为遇到了文件尾而结束

参数

stream 为指向文件的文件指针

返回值

是遇到了文件尾而结束,则返回非0

不是则返回0

c 复制代码
int ferror ( FILE * stream );

功能

判断文件的读取是否因为遇到了错误而结束

参数

stream 为指向文件的文件指针

返回值

是遇到了错误而结束则返回非0

不是则返回0

我们可以通过使用 feof 和 ferror 来让文件的读取更加安全

c 复制代码
#include <stdio.h>
#include <assert.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
		assert(pf);

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

	if (feof(pf))
	{
		printf("遇到文件尾结束");
	}
	else if (ferror(pf))
	{
		perror("fault");
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

7 文件缓冲区

计算机在从文件内读取数据或向文件内写入数据时,会先将数据存入输入/输出缓冲区,待输入/输出缓冲区被填满时,才会将数据读入计算机或输入到文件内,如果没有缓冲区的话,那么就会出现输入/输出一次数据,操作系统就要介入进行处理的情况,操作系统不断被打断,无法服务于其它的进程,会使得操作系统的效率降低

相关推荐
Awh-28 分钟前
ARM 裸机开发 DAY9 学习笔记
arm开发·笔记·学习
学逆向的30 分钟前
C 控制台指令
c语言·开发语言·网络安全
CAIE研习社32 分钟前
CAIE认证与在线AI课程证书对比:完成条件、考试评价、学习支持怎么区分
学习
沫璃染墨32 分钟前
从零入门数据结构与算法(一):复杂度分析——从算法效率到时间与空间复杂度
c语言·开发语言·数据结构·笔记·算法·飞书
CCIE-Yasuo33 分钟前
个人电脑搭建AI-Qwen3-8B-学习记录
人工智能·python·学习
想做小南娘,发现自己是女生喵1 小时前
第三周学习文档 Linux C 多进程与多线程
linux·c语言·学习
北京盛世宏博1 小时前
异地机房采集踩坑笔记:以太网温湿度记录仪跨网段通信调试实战
开发语言·笔记·单片机·php·传感器·温湿度
奕鼎竜瑆1 小时前
无监督学习:聚类与降维
学习·数据挖掘·聚类
FakeOccupational2 小时前
【电路笔记 STM32】Cortex-M7 UART DMA模式(DMA&空闲中断&普通模式+DMA&半满/全满中断&循环模式)
笔记·stm32·嵌入式硬件