【C语言】文件操作全解析

文章目录

    • 一、为什么需要文件操作?
    • 二、认识文件:不止是磁盘上的存储
      • 2.1 程序文件
      • 2.2 数据文件
      • 2.3 文件名的构成
    • 三、文本文件与二进制文件:数据的两种形态
      • 3.1 存储方式差异
      • 3.2 实例对比:整数10000的存储
      • 3.3 二进制文件操作示例
    • 四、文件的打开与关闭:操作的开始与结束
      • 4.1 流的概念
      • 4.2 文件指针
      • 4.3 打开与关闭函数
      • 4.4 文件打开模式
      • 4.5 示例代码
    • 五、文件的顺序读写:按部就班的数据处理
      • 5.1 常用顺序读写函数
      • 5.2 函数对比
    • 六、文件的随机读写:自由定位数据位置
      • 6.1 fseek函数:定位文件指针
      • 6.2 ftell函数:获取当前位置偏移量
      • 6.3 rewind函数:重置文件指针
    • 七、文件读取结束的判定:正确识别结束条件
      • 7.1 feof函数的正确用法
      • 7.2 正确的判断方式
    • 八、文件缓冲区:提升效率的中间层
      • 8.1 缓冲区演示
      • 8.2 重要结论
    • 总结

在C语言编程中,文件操作是实现数据持久化存储的关键技术。无论是保存用户数据、配置信息还是处理大量数据,都离不开文件操作。本文将详细讲解C语言文件操作的方方面面,从基本概念到实际应用,帮助你全面掌握这一重要技能。

一、为什么需要文件操作?

在程序运行过程中,我们处理的数据通常存储在内存中。然而,内存具有临时性的特点——当程序退出或计算机断电时,内存中的数据会全部丢失。如果我们希望数据能够长期保存,以便下次运行程序时继续使用,就必须使用文件将数据存储到磁盘等外部存储设备中。

简单来说,文件操作让程序的数据拥有了"记忆"能力,是实现数据持久化的核心手段。

二、认识文件:不止是磁盘上的存储

在C语言中,我们通常从功能角度将文件分为两类:

2.1 程序文件

程序文件是用于构成程序本身的文件,包括:

  • 源程序文件(.c后缀)
  • 目标文件(Windows环境下为.obj后缀)
  • 可执行程序(Windows环境下为.exe后缀)

2.2 数据文件

数据文件是程序运行时读写的数据载体,比如:

  • 程序需要读取的配置文件
  • 程序输出的日志文件
  • 存储用户信息的数据库文件

本文重点讨论的是数据文件的操作。

2.3 文件名的构成

一个完整的文件名包含三个部分:

  • 文件路径:指示文件在磁盘中的位置
  • 文件名主干:文件的核心标识
  • 文件后缀:指示文件类型

例如:c:\code\test.txt中,c:\code\是路径,test是文件名主干,.txt是后缀。

三、文本文件与二进制文件:数据的两种形态

根据数据的存储形式,数据文件可分为文本文件和二进制文件,它们的区别如下:

3.1 存储方式差异

  • 二进制文件:数据在内存中以二进制形式存储,不加转换直接输出到外存
  • 文本文件:数据在存储前被转换为ASCII码形式,以字符形式存储

3.2 实例对比:整数10000的存储

以整数10000为例:

  • 文本文件存储:占用5个字节(每个字符一个字节),存储内容是’1’,‘0’,‘0’,‘0’,'0’的ASCII码
  • 二进制文件存储:在VS2019环境下仅占用4个字节,直接存储其二进制形式00000000 00000000 00100111 00010000

3.3 二进制文件操作示例

#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("test.txt", "wb");  // 以二进制写入模式打开文件fwrite(&a, 4, 1, pf);  // 将a以二进制形式写入文件fclose(pf);pf = NULL;return 0;
}

在VS中查看二进制文件时,需要使用二进制编辑器,10000会显示为10270000

四、文件的打开与关闭:操作的开始与结束

4.1 流的概念

C语言通过"流"(stream)来操作各种外部设备(包括文件)。流可以想象成"流淌着字符的河",程序通过流与外部设备进行数据交换。

C程序启动时会默认打开3个标准流:

  • stdin:标准输入流(通常对应键盘)
  • stdout:标准输出流(通常对应显示器)
  • stderr:标准错误流(通常对应显示器)

这就是为什么我们可以直接使用scanfprintf函数进行输入输出,而无需手动打开流。

4.2 文件指针

缓冲文件系统中,通过文件指针(FILE*类型)来管理文件。每个被打开的文件在内存中都有一个对应的文件信息区(结构体),存储文件名、状态、当前位置等信息,文件指针就指向这个结构体。

定义文件指针的方式:

FILE* pf;  // 声明一个文件指针变量

4.3 打开与关闭函数

  • 打开文件:使用fopen函数

    FILE * fopen ( const char * filename, const char * mode );
    
  • 关闭文件:使用fclose函数

    int fclose ( FILE * stream );
    

4.4 文件打开模式

文件打开模式决定了对文件的操作权限,常见模式如下:

文件使用方式含义如果指定文件不存在
“r”(只读)打开文本文件用于输入出错
“w”(只写)打开文本文件用于输出创建新文件
“a”(追加)向文本文件末尾添加数据创建新文件
“rb”(只读)打开二进制文件用于输入出错
“wb”(只写)打开二进制文件用于输出创建新文件
“ab”(追加)向二进制文件末尾添加数据创建新文件
“r+”(读写)打开文本文件用于读写出错
“w+”(读写)创建文本文件用于读写创建新文件
“a+”(读写)打开文本文件用于读写,从末尾开始创建新文件

4.5 示例代码

#include <stdio.h>
int main ()
{FILE * pFile;// 打开文件pFile = fopen ("myfile.txt","w");// 文件操作if (pFile!=NULL){fputs ("fopen example",pFile);// 关闭文件fclose (pFile);}return 0;
}

五、文件的顺序读写:按部就班的数据处理

顺序读写是指从文件的开头到结尾依次进行读写操作,C语言提供了一系列函数实现这一功能:

5.1 常用顺序读写函数

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

5.2 函数对比

  • 输入函数家族

    • scanf:从标准输入流读取格式化数据
    • fscanf:从指定输入流读取格式化数据
    • sscanf:从字符串读取格式化数据
  • 输出函数家族

    • printf:向标准输出流输出格式化数据
    • fprintf:向指定输出流输出格式化数据
    • sprintf:将格式化数据输出到字符串

六、文件的随机读写:自由定位数据位置

随机读写允许程序直接定位到文件的任意位置进行操作,主要通过以下函数实现:

6.1 fseek函数:定位文件指针

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

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

参数origin可以是:

  • SEEK_SET:从文件开头开始计算
  • SEEK_CUR:从当前位置开始计算
  • SEEK_END:从文件末尾开始计算

示例:

#include <stdio.h>
int main ()
{FILE * pFile;pFile = fopen ( "example.txt" , "wb" );fputs ( "This is an apple." , pFile );fseek ( pFile , 9 , SEEK_SET );  // 定位到第10个字符位置fputs ( " sam" , pFile );  // 从该位置开始写入fclose ( pFile );return 0;
}
// 最终文件内容为:"This is a sample."

6.2 ftell函数:获取当前位置偏移量

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

long int ftell ( FILE * stream );

示例:计算文件大小

#include <stdio.h>
int main ()
{FILE * pFile;long size;pFile = fopen ("myfile.txt","rb");if (pFile==NULL) perror ("Error opening file");else{fseek (pFile, 0, SEEK_END);  // 定位到文件末尾size=ftell (pFile);  // 获取偏移量,即文件大小fclose (pFile);printf ("Size of myfile.txt: %ld bytes.\n",size);}return 0;
}

6.3 rewind函数:重置文件指针

将文件指针重新定位到文件开头:

void rewind ( FILE * stream );

示例:

#include <stdio.h>
int main ()
{int n;FILE * pFile;char buffer [27];pFile = fopen ("myfile.txt","w+");for ( n='A' ; n<='Z' ; n++)fputc ( n, pFile);rewind (pFile);  // 回到文件开头fread (buffer,1,26,pFile);fclose (pFile);buffer[26]='\0';printf(buffer);  // 输出ABCDEFGHIJKLMNOPQRSTUVWXYZreturn 0;
}

七、文件读取结束的判定:正确识别结束条件

很多初学者会错误地使用feof函数来判断文件是否结束,这是需要避免的。

7.1 feof函数的正确用法

feof函数的作用是:当文件读取结束时,判断结束的原因是否是"遇到文件尾"。它并不能直接用来判断文件是否结束。

7.2 正确的判断方式

  1. 文本文件

    • 使用fgetc时,判断返回值是否为EOF
    • 使用fgets时,判断返回值是否为NULL

    示例:

    #include <stdio.h>
    #include <stdlib.h>
    int main(void)
    {int c;  // 注意:必须是int类型,以处理EOFFILE* fp = fopen("test.txt", "r");if(!fp) {perror("File opening failed");return EXIT_FAILURE;}// fgetc读取失败或遇到文件结束时返回EOFwhile ((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);
    }
    
  2. 二进制文件

    • 使用fread时,判断返回值是否小于实际要读取的个数

    示例:

    #include <stdio.h>
    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);  // 写入数组fclose(fp);double b[SIZE];fp = fopen("test.bin","rb");size_t ret_code = fread(b, sizeof *b, SIZE, fp);  // 读取数组if(ret_code == SIZE) {puts("Array read successfully, contents: ");for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);putchar('\n');} else {  // 错误处理if (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp);
    }
    

八、文件缓冲区:提升效率的中间层

ANSI C标准采用"缓冲文件系统",系统会自动为每个正在使用的文件在内存中开辟一块"文件缓冲区":

  • 输出数据时:先送到内存缓冲区,缓冲区满后再一起写入磁盘
  • 输入数据时:先从磁盘读取到缓冲区,再从缓冲区送到程序数据区

缓冲区的大小由C编译系统决定。

8.1 缓冲区演示

#include <stdio.h>
#include <windows.h>  // VS2019 WIN11环境
int main()
{FILE* pf = fopen("test.txt", "w");fputs("abcdef", pf);  // 数据先存入输出缓冲区printf("睡眠10秒-此时打开test.txt,文件无内容\n");Sleep(10000);printf("刷新缓冲区\n");fflush(pf);  // 手动刷新缓冲区,数据写入磁盘printf("再睡眠10秒-此时打开test.txt,文件有内容\n");Sleep(10000);fclose(pf);  // 关闭文件时也会刷新缓冲区pf = NULL;return 0;
}

8.2 重要结论

由于缓冲区的存在,操作文件时必须注意:

  • 及时刷新缓冲区(使用fflush函数)
  • 操作结束后关闭文件(fclose会自动刷新缓冲区)

否则可能导致数据未能正确写入文件,造成数据丢失或不一致。

总结

文件操作是C语言编程中的重要技能,掌握它可以让你的程序具备数据持久化能力。本文介绍了文件的基本概念、类型划分、打开关闭、顺序读写、随机读写、结束判定以及缓冲区机制等内容。

在实际编程中,要注意以下几点:

  • 始终检查文件是否成功打开
  • 操作完成后及时关闭文件
  • 正确判断文件读取结束的条件
  • 理解并合理利用缓冲区机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/92187.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92187.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C结构体的几种定义形式 + typedef结合使用的好处

struct 语句定义了一个包含多个成员的新的数据类型&#xff0c;struct 语句的格式如下&#xff1a; struct tag { member-list member-list member-list ... } variable-…

SPICE电容矩阵

SPICE电容矩阵: 如果有许多条传输线,就可以用下标来标记每一条线。例如,如果有5条线,就用1~5分别标记,依惯例把返回路径导体标记为导线0。图10.6给出了5条导线和一个公共返回平面的横截面图。首先研究电容器元件,下一节再讨论电感器元件。 在这个线的集合中,每对导线之间…

【Java】栈和队列

文章目录1.栈1.1 栈的定义1.2 栈的使用1.3 栈的模拟实现2.队列2.1 队列的定义2.2 队列的使用2.3 队列的模拟实现3.循环队列3.1 循环队列的概念3.2 循环队列判断空和满4.双端队列Deque1.栈 1.1 栈的定义 栈是一种特殊的线性表&#xff0c;其只允许在固定的一段进行数据的插入或…

【性能测试】---测试工具篇(jmeter)

目录 1、安装并启动jemeter 2、重点组件 2.1、线程组&#xff1a; 2.2、HTTP取样器​编辑 2.3、查看结果树 2.4、HTTP请求默认值 2.5、HTTP信息头管理器 2.6、JSON提取器 2.7、JSON断言 2.8、同步定时器 2.9、CSV数据文件设置 2.10、HTTP Cookie管理器 3、测试报告…

机器学习(12):拉索回归Lasso

- 拉索回归可以将一些权重压缩到零&#xff0c;从而实现特征选择。这意味着模型最终可能只包含一部分特征。 - 适用于特征数量远大于样本数量的情况&#xff0c;或者当特征间存在相关性时&#xff0c;可以从中选择最相关的特征。 - 拉索回归产生的模型可能更简单&#xff0c;因…

Redis持久化存储

Redis持久化存储详解 一、核心持久化机制 Redis提供两种主要持久化方式&#xff1a;RDB&#xff08;快照&#xff09; 和 AOF&#xff08;追加文件&#xff09;&#xff0c;以及两者的混合模式。 RDB&#xff08;Redis Database&#xff09;快照持久化 工作原理 RDB通过创建数据…

python学智能算法(三十四)|SVM-KKT条件回顾

【1】引言 前序学习进程中&#xff0c;对软边界拉格朗日方程进行了初步构建。 其中约定了两个拉格朗日乘子要非负&#xff0c;其本质是要满足KKT条件。 今天就乘此次机会&#xff0c;在回顾一下KKT条件。 【2】定义 当问题无约束的时候&#xff0c;只要让函数梯度为零&#…

【网络基础】计算机网络发展背景及传输数据过程介绍

本文旨在帮助初学者建立起计算机网络的基础认知&#xff0c;从网络的发展背景到网络协议的分层模型&#xff0c;再到IP与MAC地址的基本概念&#xff0c;全面覆盖第一阶段学习重点。 &#x1f4cc; 本节重点 了解计算机网络的发展背景&#xff0c;掌握局域网&#xff08;LAN&am…

阿里云-通义灵码:解锁云原生智能开发新能力,让云开发更“灵”~

免责声明&#xff1a;此篇文章所有内容皆是本人实验&#xff0c;并非广告推广&#xff0c;并非抄袭&#xff0c;如有侵权&#xff0c;请联系笔者。 每日一句 信念其实就是相信未来&#xff0c; 相信内在&#xff0c; 以及坦然美好的心境。 目录 每日一句 一. 引言 二.通义…

lesson33:Python协程详解:从原理到实战的异步编程指南

目录 一、协程核心概念&#xff1a;轻量级并发的本质 1.1 什么是协程&#xff1f; 1.2 协程与线程/进程的对比 二、协程工作原理&#xff1a;事件循环与协作式调度 2.1 事件循环&#xff08;Event Loop&#xff09;&#xff1a;协程的"调度中心" 2.2 协作式调度…

深入理解C++模板进阶:非类型参数、特化与分离编译

前言C模板是泛型编程的核心&#xff0c;它允许我们编写与类型无关的代码。在掌握了模板的基础知识后&#xff0c;我们需要进一步了解模板的高级特性&#xff0c;以便更灵活地使用它们。本文将深入探讨三个重要的模板进阶主题&#xff1a;非类型模板参数、模板特化以及模板的分离…

使用winsw把SpringBoot项目注册成window服务

目录 一、使用winsw注册 1.1、项目打jar包 1.2、下载winsw 1.3、把 WinSW.NET4.exe 重新命名 1.4、编写m配置文件用于配置注册信息 1.5、创建文件夹存放你的文件 1.6、安装服务 1.7、启动服务 1.8、卸载服务 1.8、停止服务 一、使用winsw注册 1.1、项目打jar包 例如项目jar包名…

进阶向:Python开发简易QQ聊天机器人

数字化时代的聊天机器人应用在当今数字化时代&#xff0c;聊天机器人已经成为日常生活和商业活动中不可或缺的一部分。根据市场研究数据显示&#xff0c;全球聊天机器人市场规模预计将在2026年达到102亿美元&#xff0c;年复合增长率达到34.75%。这些智能助手正广泛应用于以下场…

基于开源链动2+1模式AI智能名片S2B2C商城小程序的用户留存策略研究

摘要&#xff1a;在数字化商业竞争白热化的当下&#xff0c;用户留存成为企业可持续发展的核心命题。本文聚焦开源链动21模式AI智能名片S2B2C商城小程序这一创新技术组合&#xff0c;通过分析其技术架构、模式创新与生态闭环的协同效应&#xff0c;揭示其在降低用户决策成本、提…

单词的划分(动态规划)

题目描述有一个很长的由小写字母组成字符串。为了便于对这个字符串进行分析&#xff0c;需要将它划分成若干个部分&#xff0c;每个部分称为一个单词。出于减少分析量的目的&#xff0c;我们希望划分出的单词数越少越好。你就是来完成这一划分工作的。输入第一行&#xff0c;一…

C语言学习笔记——文件

目录1 文件的概念2 程序文件和数据文件3 二进制文件和文本文件4 流4.1 流的概念4.2 标准流5 文件信息区和文件指针6 处理文件的库函数6.1 fopen6.2 fclose6.3 fgetc6.4 fputc6.5 fgets6.6 fputs6.7 fscanf6.8 fprintf6.9 fread6.10 fwrite6.11 fseek6.12 ftell6.13 rewind6.14 …

C++语法与面向对象特性(2)

一.inline函数1.inline的基本特性被inline修饰的函数被称为内联函数。inline函数设计的初衷是为了优化宏的功能&#xff0c;编译器会在编译阶段对inline函数进行展开。然而需要注意的是&#xff0c;inline对于编译器而言是一种建议&#xff0c;它通常会展开一些简短的&#xff…

Linux中grep命令

Linux 中的 grep 用法详解grep 是 Linux 中强大的文本搜索工具&#xff0c;用于在文件或输入流中查找匹配指定模式的行。其基本语法为&#xff1a;grep [选项] "模式" [文件]核心功能基础搜索在文件中查找包含特定字符串的行&#xff1a;grep "error" log.…

【遥感图像入门】遥感中的“景”是什么意思?

在遥感成像中,“3景城市影像”和“5景城市影像”中的“景”是遥感数据的基本单位,通常指一次成像过程中获取的独立遥感影像块。这一概念的具体含义需结合技术背景和应用场景理解: 一、“景”的技术定义 单次成像的独立覆盖区域 遥感平台(如卫星、飞机)在特定时间和位置对…

Pytorch-07 如何快速把已经有的视觉模型权重扒拉过来为己所用

下载&#xff0c;保存&#xff0c;加载&#xff0c;使用模型权重 在这一节里面我们会过一遍对模型权重的常用操作&#xff0c;比如&#xff1a; 如何下载常用模型的预训练权重如何下载常用模型的无训练权重&#xff08;只下载网络结构&#xff09;如何加载模型权重如何保存权…