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 文件缓冲区

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

相关推荐
yuezhilangniao2 小时前
关于开发语言的一些效率 从堆栈角度理解一部分c java go python
java·c语言·开发语言
港港胡说2 小时前
机器学习(西瓜书)学习——绪论
人工智能·学习·机器学习
心一信息2 小时前
非常简单!从零学习如何免费制作一个lofi视频
学习·音视频
_Kayo_2 小时前
node.js 学习笔记2 进程/线程、fs
笔记·学习
小六学编程3 小时前
C语言库中的字符函数
c语言
Hello_Embed4 小时前
STM32HAL 快速入门(三):从 HAL 函数到寄存器操作 —— 理解 HAL 库的本质
c语言·stm32·单片机·嵌入式硬件·学习
●VON4 小时前
重生之我在暑假学习微服务第十天《网关篇》
学习·微服务·云原生·架构
学不动CV了5 小时前
FreeRTOS入门知识(初识RTOS任务调度)(三)
c语言·arm开发·stm32·单片机·物联网·算法·51单片机
_poplar_5 小时前
09 【C++ 初阶】C/C++内存管理
c语言·开发语言·数据结构·c++·git·算法·stl