【C语言】文件操作揭秘:C语言中文件的顺序读写、随机读写、判断文件结束和文件缓冲区详细解析【图文详解】

欢迎来CILMY23的博客喔,本篇为【C语言】文件操作揭秘:C语言中文件的顺序读写、随机读写、判断文件结束和文件缓冲区详细解析【图文详解】,感谢观看,支持的可以给个一键三连,点赞关注+收藏。
前言

欢迎来到本篇博客,上一篇我们详细介绍C语言中的文件操作。

在计算机领域,文件是一种常见的存储和处理信息的方式。通过文件,我们可以将数据和程序永久保存在硬盘中,并随时读取和修改。本篇博客将深入探讨文件操作、文件的顺序读写、随机读写、判断文件结束和文件缓冲区详细解析。

上一篇博客链接:

【C语言】文件操作篇-----程序文件和数据文件,文件的打开和关闭,二进制文件和文本文件,fopen,fclose【图文详解】-CSDN博客

文章目录

一、文件的顺序读写

[1.1 文件顺序读写的函数介绍:](#1.1 文件顺序读写的函数介绍:)

[1.2 fgetc和fputc的使用](#1.2 fgetc和fputc的使用)

[1.3 fgets和fputs的使用](#1.3 fgets和fputs的使用)

[1.4 fprintf和printf的使用](#1.4 fprintf和printf的使用)

[1.5 fscanf和scanf的使用](#1.5 fscanf和scanf的使用)

[1.6 sscanf和sprintf的介绍和使用](#1.6 sscanf和sprintf的介绍和使用)

[1.7 fwrite和fread的使用](#1.7 fwrite和fread的使用)

[1.8 总结](#1.8 总结)

二、文件的随机读写

[2.1 fseek函数](#2.1 fseek函数)

[2.2 ftell函数](#2.2 ftell函数)

三、文件读取结束的判定

四、文件缓冲区


一、文件的顺序读写

文件顺序读写是指按照数据在文件中的顺序依次读取或写入数据的操作方式。在文件顺序读写中,数据按照其存储在文件中的顺序被逐个读取或写入。

对于顺序读取,程序会依次读取文件中的数据,从文件的开头一直读取到末尾,直到到达文件结束的位置或者读取到所需的数据为止。每次读取数据后,读取指针会自动向后移动到下一个数据的位置。

对于顺序写入,程序会依次将数据写入文件,从文件的结尾开始写入,每写入一个数据后,写入指针会自动移动到下一个位置,以便写入下一个数据。这样,数据会按照写入的顺序依次添加到文件中。

1.1 文件顺序读写的函数介绍:

上面说的使用于所有输入流⼀般指使用于标准输入流和其他输入流(如文件输入流);所有输出流⼀般指使用于标准输出流和其他输出流(如文件输出流)。

fgetcfputc: 这两个函数用于逐个字符地读取和写入文件。(一次读取(写入)一个)

它们的原型如下:

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

fgets:从指定文件中读取一行数据(包括换行符),并将其存储到指定的字符数组中。

原型如下:

cpp 复制代码
char *fgets(char *str, int n, FILE *stream);

fputs:将指定的字符串写入到指定文件中。(一次写一行数据)

原型如下:

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

fscanf: 该函数用于从文件中按照指定的格式读取数据。

它的原型如下:

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

fprintf: 该函数用于向文件中按照指定的格式写入数据。

它的原型如下:

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

**fread**函数:

  • 函数原型:

    cpp 复制代码
    size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
  • 功能:从指定文件中读取二进制数据,并将其存储到指定的内存位置。

  • 参数:

    • ptr:指向存储读取的数据的内存位置的指针。
    • size:每个数据项的字节数。
    • count:要读取的数据项的个数。
    • stream:指向已打开文件的指针,表示从该文件中读取数据。
  • 返回值:返回实际成功读取的数据项数目,如果返回值少于**count**,则可能表示已到达文件末尾或发生了错误。

**fwrite**函数:

  • 函数原型:
cpp 复制代码
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
  • 功能:将二进制数据写入指定文件中。
  • 参数:
    • ptr:指向要写入的数据的内存位置的指针。
    • size:每个数据项的字节数。
    • count:要写入的数据项的个数。
    • stream:指向已打开文件的指针,表示将数据写入到该文件中。
  • 返回值:返回实际成功写入的数据项数目,如果返回值少于**count**,则可能表示发生了错误。

1.2 fgetc和fputc的使用

我们现在在当前路径下:C:\Users\云山若汐\source\repos\test2\test2

放入两个文件,

write可以不放,然后我们在text中输入abcd,我们现在将要拷贝当中的字符到write中,所以写下以下代码:

cpp 复制代码
#include<stdio.h>

int main()
{
	char ch;
	FILE* pfread = fopen("text.txt", "r");
	if (pfread == NULL)
	{
		perror("fopen - 1");
		return 1;
	}
	FILE* pfwrite = fopen("write.txt", "w");
	if (pfwrite == NULL)
	{
		perror("fopen - 2");
		return 1;
	}

	while ((ch = fgetc(pfread)) != EOF)
	{
		fputc(ch, pfwrite);
	}
	fclose(pfread);
	pfread = NULL;
	fclose(pfwrite);
	pfwrite = NULL;
}

效果如下:

1.3 fgets和fputs的使用

假设此时text中有两行,它们分别是:

cpp 复制代码
int main()
{
	char arr[20];

	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	fgets(arr, 20, pf);
	printf("%s", arr);

	fclose(pf);
	pf = NULL;

	return 0;
}

我们使用fgets从文件中获取信息,注意fgets只会从指定文件中读取一行数据(包括换行符)也就是换行符是固定的,我们读取20只会读取前十九个字符因为最后一个字符要存放换行符。

1.4 fprintf和printf的使用

假设我们定义了一个结构体如下所示,那我们可以使用printf将其打印到屏幕上,也可以使用fprintf将其打印至屏幕上,或者将其写入文件中

cpp 复制代码
struct S
{
	int n;
	float f;
	char arr[100];
};

int main()
{
	struct S s = { 100,3.14f,"zhangsan" };

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

	//输出到屏幕上
	printf("%d %f %s\n", s.n, s.f, s.arr);
	fprintf(stdout, "%d %f %s", s.n, s.f, s.arr);
	//使用fprintf输出到文件中
	fprintf(pf, "%d %f %s\n", s.n, s.f, s.arr);

	fclose(pf);
	pf = NULL;

	return 0;
}

效果如下:

1.5 fscanf和scanf的使用

cpp 复制代码
struct S
{
	int n;
	float f;
	char arr[100];
};

int main()
{
	struct S s = {0};

	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	fscanf(pf,"%d %f %s",&(s.n), &(s.f), &(s.arr));
	printf("%d %f %s", s.n, s.f, s.arr);

	fclose(pf);
	pf = NULL;

	return 0;
}

我们用"fopen"打开一个名为"text.txt"的文件来读取数据。如果打开成功,你使用"fscanf"函数从文件中读取整数、浮点数和字符串,存储到结构体"s"的相应成员中。注意,在"%s"格式符后,使用"s.arr"而不是"&"操作符(因为数组名本身就是数组的首地址)

1.6 sscanf和sprintf的介绍和使用

在本文中,我们将介绍C++中两个常用的输入输出解析函数:sscanf和sprintf。这两个函数提供了灵活的方式来处理字符串和变量之间的格式化输入输出操作。

  • sscanf函数:
    • sscanf函数用于将字符串按照指定的格式解析,并将解析后的值存储到对应的变量中。
    • 语法:int sscanf(const char* str, const char* format, ...)
    • str为输入的字符串,format为格式化字符串,...为对应的变量列表。
cpp 复制代码
#include <stdio.h>

int main() {
   const char* str = "23.5 10";
   float f;
   int i;
   sscanf(str, "%f %d", &f, &i);

   printf("解析出的浮点数为:%.2f\n", f);
   printf("解析出的整数为:%d\n", i);

   return 0;
}
  • sprintf函数:
    • sprintf函数用于将格式化的数据输出到字符串中。
    • 语法:int sprintf(char* str, const char* format, ...)
    • str为输出的字符串,format为格式化字符串,...为对应的变量列表。
cpp 复制代码
#include <stdio.h>

int main() {
   char str[100];
   int i = 42;
   float f = 3.14;

   sprintf(str, "整数:%d,浮点数:%.2f", i, f);

   printf("格式化后的字符串:%s\n", str);

   return 0;
}

sscanf和sprintf函数是C语言中常用的输入输出解析函数,可以方便地进行字符串的解析和格式化输出

1.7 fwrite和fread的使用

fwrite的使用

cpp 复制代码
#include <stdio.h>

int main()
{
    int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    FILE* pf = fopen("text.txt", "wb");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }

    fwrite(arr, sizeof(int), 10, pf); // 写入整个数组

    fclose(pf);
    pf = NULL;

    return 0;
}

我们可以在项目中用二进制形式查看这个txt文件 ,右键该文件,选择打开方式,选择底部的二进制编辑器,

​​​​​

我们可以看到文件中写入了几个数

那如何验证我们是否写入了呢?这时候我们就需要用到fread

cpp 复制代码
int main()
{
    int arr[10] = {0};

    FILE* pf = fopen("text.txt", "rb");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }

    fread(arr, sizeof(int), 10, pf); 
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }

    fclose(pf);
    pf = NULL;

    return 0;
}

1.8 总结

二、文件的随机读写

C语言中的随机读写通常指的是以随机的方式访问文件中的数据,而不是按顺序逐个读取或写入。这种随机的读写通常涉及到文件指针的定位,可以根据需要在文件中的不同位置进行读写操作。

C语言提供了两个函数来进行文件的随机读写,它们分别是fseek和ftell

2.1 fseek函数

fseek - C++ Reference (cplusplus.com)

  • fseek函数:

    • 函数原型:
    cpp 复制代码
    int fseek ( FILE * stream, long int offset, int origin );
    • 功能:移动文件流中的读写位置,进行随机的文件定位。
    • 参数:
      • stream:指向文件的指针。
      • offset:偏移量,用于指定文件指针要移动的位置。
      • origin:指定起始位置,可以取三个值:
      • SEEK_SET :文件的开始位置
      • SEEK_CUR:文件指针的当前位置
      • SEEK_END:文件的末尾位置
    • 返回值:如果成功,返回0;否则,返回非0值(通常是-1)。

fseek的使用,fseek函数主要有三种origin的起始值

至于偏移量就是距离我的origin有多远了,从1字节开始算

例如:

假设我的text里存了CILMY23

cpp 复制代码
#include <stdio.h>

int main()
{

    FILE* pf = fopen("text.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
    fseek(pf, 0, SEEK_SET);//C
    char ch = fgetc(pf);
    printf("%c\n", ch);

    fseek(pf, -1, SEEK_END);//3
    ch = fgetc(pf);
    printf("%c\n", ch);

    fseek(pf, -5, SEEK_CUR);//L
    ch = fgetc(pf);
    printf("%c\n", ch);

    fclose(pf);
    pf = NULL;

    return 0;
}

2.2 ftell函数

ftell - C++ Reference (cplusplus.com)

ftell函数:

  • 原型:

    cpp 复制代码
    long int ftell ( FILE * stream );
  • 功能:获取当前文件位置指针的偏移字节数。

  • 参数:

    • stream:指向文件的指针。
  • 返回值:返回当前文件位置指针的偏移字节数。

例如:

cpp 复制代码
#include <stdio.h>

int main()
{

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

    fseek(pf, -1, SEEK_END);//3
    char ch = fgetc(pf);
    printf("%c\n", ch);

    int ret = ftell(pf);
    printf("%d", ret);

    fclose(pf);
    pf = NULL;

    return 0;
}

三、文件读取结束的判定

文件读取结束可以通过feof函数进行判定。feof函数用于检测文件流上的结束标志。当文件末尾已经读取,feof函数返回非零值;否则返回0。

文件读取结束的原因可能有:

1.文件遇到末尾

2.文件发生错误

cpp 复制代码
#include<stdio.h>

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

    int ch;
    while ((ch = fgetc(pf)) != EOF)
    {
        // 处理读取的字符
        if (feof(pf))
        {
            printf("文件读取结束\n");
        }
        else
        {
            printf("文件读取出错\n");
        }
    }
    fclose(pf);
    pf = NULL;
    return 0;
}

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

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

例如:

• fgetc 判断是否为 EOF .

• fgets 判断返回值是否为NULL .

  1. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。 例如:

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

四、文件缓冲区

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

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

通过本篇博客,相信你了解了C语言中文件操作的核心内容。文件的顺序读写、随机读写、文件读取结束的判定以及文件缓冲区等知识点,感谢你的阅读!如果你对文件操作还有任何疑问或需要进一步的帮助,请随时留言,如果你觉得还不错的话,可以给个一键三连,点赞关注加收藏,本篇博客就到此结束了。

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
aloha_7894 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
hikktn5 小时前
如何在 Rust 中实现内存安全:与 C/C++ 的对比分析
c语言·安全·rust
青花瓷5 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode