1. 需要了解的概念

需要理解的知识点包括:数据流、缓冲区、文件类型、文件存取方式

1.1 数据流:

指程序与数据的交互是以流的形式进行的。进行C语言文件的存取时,都会先进行“打开文件”操作,这个操作就是在打开数据流,而“关闭文件”操作就是关闭数据流。

1.2 缓冲区(Buffer):

指在程序执行时,所提供的额外内存,可用来暂时存放做准备执行的数据。它的设置是为了提高存取效率,因为内存的存取速度比磁盘驱动器快得多。

 C语言中带缓冲区的文件处理:

C语言的文件处理功能依据系统是否设置“缓冲区”分为两种:一种是设置缓冲区,另一种是不设置缓冲区。由于不设置缓冲区的文件处理方式,必须使用较低级的I/O函数(包含在头文件io.h和fcntl.h中)来直接对磁盘存取,这种方式的存取速度慢,并且由于不是C的标准函数,跨平台操作时容易出问题。下面只介绍第一种处理方式,即设置缓冲区的文件处理方式:

当使用标准I/O函数(包含在头文件stdio.h中)时,系统会自动设置缓冲区,并通过数据流来读写文件。当进行文件读取时,不会直接对磁盘进行读取,而是先打开数据流,将磁盘上的文件信息拷贝到缓冲区内,然后程序再从缓冲区中读取所需数据,如下图所示:

事实上,当写入文件时,并不会马上写入磁盘中,而是先写入缓冲区,只有在缓冲区已满或“关闭文件”时,才会将数据写入磁盘,如下图所示。

1.3 文件类型:

分为文本文件和二进制文件两种。

文本文件是以字符编码的方式进行保存的。二进制文件将内存中数据原封不至文件中,适用于非字符为主的数据。如果以记事本打开,只会看到一堆乱码。

其实,除了文本文件外,所有的数据都可以算是二进制文件。二进制文件的优点在于存取速度快,占用空间小,以及可随机存取数据。

1.4 文件存取方式:

包括顺序存取方式和随机存取方式两种。

顺序读取也就是从上往下,一笔一笔读取文件的内容。保存数据时,将数据附加在文件的末尾。这种存取方式常用于文本文件,而被存取的文件则称为顺序文件。

随机存取方式多半以二进制文件为主。它会以一个完整的单位来进行数据的读取和写入,通常以结构为单位。


2. 文本文件操作

C语言中主要通过标准I/O函数来对文本文件进行处理。相关的操作包括打开、读写、关闭与设置缓冲区。
相关的存取函数有:fopen(), fclose(), fgetc(), fputc(), fgets(), fputs(), fprintf(), fscanf()等。

2.1 打开文件

函数原型为:_CRTIMP FILE * __cdecl fopen(const char *, const char *);

第一参数为文件名,第二个参数为打开模式。

打开成功,fopen返回一个结构指针地址,否则返回一个NULL。如果没有指定文件路径,则默认为当前工作目录。如:

FILE *fp;
fp = fopen("c:\\temp\\test.txt", "r") //由于反斜杠\是控制字符,所以必须再加一个反斜杠

使用fopen()函数打开的文件会先将文件复制到缓冲区。注意:所下达的读取或写入动作,都是针对缓冲区进行存取而不是磁盘,只有当使用fclose()函数关闭文件时,缓冲区中的数据才会写入磁盘。

  谈文件打开模式

打开文本文件:

"r":只能从文件中读数据,该文件必须先存在,否则打开失败
"w":只能向文件写数据,若指定的文件不存在则创建它,如果存在则先删除它再重建一个新文件
"a":向文件增加新数据(不删除原有数据),若文件不存在则打开失败,打开时位置指针移到文件末尾
"r+":可读/写数据,该文件必须先存在,否则打开失败
"w+":可读/写数据,用该模式打开新建一个文件,先向该文件写数据,然后可读取该文件中的数据
"a+":可读/写数据,原来的文件不被删去,位置指针移到文件末尾

打开二进制文件的模式与打开文本文件的含义是一样的,不同的是模式名称里面多一个字母'b’,以表示以二进制形式打开文件。

2.2 关闭文件

函数原型为:_CRTIMP int __cdecl fclose(FILE *);

关闭成功返回值0,否则返回非零值。

注:在执行完文件的操作后,要进行“关闭文件”操作。虽然程序在结束前会自动关闭所有的打开文件,但文件打开过多会导致系统运行缓慢,这时就要自行手动关闭不再使用的文件,来提高系统整体的执行效率。

例1. 打开文件并进行判断和关闭文件

FILE *fp;
fp = fopen("c:\\temp\\test.txt", "r");if(fp == NULL)printf("fail to open the file! \n");
else
{printf("The file is open! \n");fclose(fp);
}

2.3 字符存取函数

函数原型为:

_CRTIMP int __cdecl fputc(int, FILE *);
_CRTIMP int __cdecl fgetc(FILE *);

字符读取函数fgetc()可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,并逐步将文件的内容读出。

如果字符读取成功,则返回所读取的字符,否则返回EOF(end of file)。EOF是表示数据结尾的常量,真值为-1。另外,要判断文件是否读取完毕,可利用feof()进行检查。未完返回0,已完返回非零值。

feof()函数原型为:_CRTIMP int __cdecl feof(FILE *);

例2. fgetc()函数的使用

版本1:利用feof()函数检查文件是否读取完毕

#include <stdio.h>main()
{FILE *fp;fp = fopen("c:\\temp\\test.txt", "r");if(fp != NULL){while(!feof(fp))printf("%c", fgetc(fp));}elseprintf("fail to open! \n");fclose(fp);return 0;
}

版本2:利用文件结束标志EOF(即-1)

#include <stdio.h>main()
{char ch;FILE *fp;fp = fopen("c:\\temp\\test.txt", "r");if(fp != NULL){ch = fgetc(fp);while(ch != EOF){putchar(ch);ch = fgetc(fp);}}elseprintf("fail to open! \n");fclose(fp);return 0;
}

版本3 - 重构版本2

#include <stdio.h>main()
{char ch;FILE *fp;if((fp = fopen("test.txt", "r")) != NULL)while((ch = fgetc(fp)) != EOF)putchar(ch);elseprintf("fail to open! \n");fclose(fp);return 0;
}

版本4 - 重构版本3 (不正确的重构)

#include <stdio.h>main()
{FILE *fp;if((fp = fopen("test.txt", "r")) != NULL)while(fgetc(fp) != EOF)putchar(fgetc(fp));elseprintf("fail to open! \n");fclose(fp);return 0;
}

若要将字符逐一写入文件,用fputc()函数。示例为:

例3. fputc()函数的使用

#include <stdio.h>
#include <conio.h>main()
{char filename[20], ch;FILE *fp;printf("Enter a filename: ");scanf("%s", filename);printf("Enter some characters to output to file: ");if((fp = fopen(filename, "w")) == NULL)printf("fail to open! \n");else{while((ch = getche()) != '\015')fputc(ch, fp);}fclose(fp);return 0;
}

2.4 字符串存取函数

函数原型为:

_CRTIMP int __cdecl fputs(const char *, FILE *);
_CRTIMP char * __cdecl fgets(char *, int, FILE *);

fgets函数的作用是从指定文件读入一个字符串,如:fgets(str, n, fp);

参数n为要求得到的字符个数,但只从fp指向的文件输入n-1个字符,然后在最后加一个'\0'字符,因此得到的字符串共有n个字符,把它们放在字符数组str中。如果在读完n-1个字符之前遇到换行符或EOF,读入结束。

fputs函数的作用是向指定文件输出一个字符串,如:fputs("Hey", fp);

把字符串"Hey"输出到fp指向的文件。fputs函数的第一个参数可以是字符串常量、字符数组名或字符型指针。若输出成功,则返回0,否则返回EOF。

实例略

2.5 格式化存取函数

函数原型为:

_CRTIMP int __cdecl fprintf(FILE *, const char *, ...);
_CRTIMP int __cdecl fscanf(FILE *, const char *, ...);

它们与printf和scanf函数相仿,都是格式化读写函数。不同的是:fprintf和fscanf函数的读写对象不是终端(标准输入输出),而是磁盘文件。printf函数是将内容输出到终端(屏幕),因此,fprintf就是将内容输出到磁盘文件了。

实例4. fprintf和fscanf函数的使用

#include <stdio.h>void main()
{FILE *fp;int num = 10;char name[10] = "Leeming";char gender = 'M';if((fp = fopen("info.txt", "w+")) == NULL)printf("can't open the file! \n");elsefprintf(fp, "%d, %s, %c", num, name, gender); //将数据格式化输出到文件info.txt中fscanf(fp, "%d, %s, %c", &num, name, &gender); //从文件info.txt中格式化读取数据printf("%d, %s, %c \n", num, name, gender); //格式化输出到屏幕fclose(fp);
}

2.6 指针重返函数

函数原型为:

_CRTIMP void __cdecl rewind(FILE *);;

rewind函数的作用是使位置指针重返回文件的开头,属于文件的定位。

3. 二进制文件操作

3.1 数据块存取函数

函数原型:

_CRTIMP size_t __cdecl fread(void *, size_t, size_t, FILE *);
_CRTIMP size_t __cdecl fwrite(const void *, size_t, size_t, FILE *);

当要求一次存取一组数据(如,一个数组、一个结构体变量的值),fread和fwrite函数可以解决该类问题。它们的调用形式一般为:

fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);

buffer:对于fread来说,指的是读入数据的存放地址;对于fwrite来说,是要输出数据的地址。
size:读写数据时,每笔数据的大小
count:读写数据的笔数
fp:文件指针

实例5. fread和fwrite函数的使用

#include <stdio.h>
#define SIZE 3typedef enum { MM, GG } Gender;typedef struct
{char name[10];int  age;Gender gender;
} Person;void write2file(Person emp[SIZE])
{FILE *fp;if((fp = fopen("emp.txt", "wb")) == NULL){printf("cannot open file! \n");return;}for(int i=0; i<SIZE; i++)if(fwrite(&emp[i], sizeof(Person), 1, fp) != 1)printf("file write error! \n");fclose(fp);
}void read_from_file(FILE *fp)
{Person emp_out[SIZE];if((fp = fopen("emp.txt", "rb")) == NULL){printf("cannot open file! \n");return;}printf("\n%d employee's information read: \n", SIZE);for(int i=0; i<SIZE; i++){if(fread(&emp_out[i], sizeof(Person), 1, fp) != 1)if(feof(fp)){fclose(fp);return;}printf("%-5s %4d %5d \n", emp_out[i].name, emp_out[i].age, emp_out[i].gender);}fclose(fp);
}void main()
{FILE *fp = NULL;Person employee[SIZE];printf("Enter %d employee's information: \n", SIZE);for(int i=0; i<SIZE; i++)scanf("%s %d %d", employee[i].name, &employee[i].age, &employee[i].gender);write2file(employee);read_from_file(fp);
}

3.2 随机存取函数fseek()

函数原型:

_CRTIMP int __cdecl fseek(FILE *, long, int);

对流式文件可以进行顺序读写,也可以进行随机读写。关键在于控制文件的位置指针,如果位置指针是按字节位置顺序移动的,就是顺序读写。如果能将位置指针按需要移动到任意位置,就可以实现随机读写。所谓随机读写,是指读完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读写文件中任意位置上所需要的字符(字节)。该函数的调用形式为:

fseek(fp, offset, start);

start:起始点。用0、1、2代替。0代表文件开始,名字为SEEK_SET,1代表当前位置,名字为SEEK_CUR,2代表文件末尾,名字为SEEK_END。

fseek()函数一般用于二进制文件,因为文本文件要发生字符转换,计算位置时往往会发生混乱。

调用实例如:

fseek(fp, i*sizeof(Person), 0);

C语言----文件存储相关推荐

  1. c语言采用文件存储数据,C语言读写文件大全 之 基础篇

    一直想着把C语言和C++关于文件操作的相关内容总结一下,这样在使用的时候就可以顺手拈来,嗯,这就是好记性不如烂笔头.既然是文件操作,那么开篇就从"文件"的介绍开始. 1.文件 文件 ...

  2. 基于存储的C语言文件操作常规问题分析(文本文件与二进制文件)

    基于存储的C语言文件操作常规问题分析(文本文件与二进制文件) 问题描述 文本文件与二进制文件 数据写入文本乱码问题 fopen和open 流式文件操作常用函数 直接I/O文件操作常用函数 问题描述 我 ...

  3. 赶紧进来看看---C语言实现学生信息管理系统(3.0文件存储版)

    本文主要介绍了将学生信息管理系统改造为文件存储版本- 主要涉及文件操作函数–将学生信息导入文件和从文件读取学生信息到程序中,从而达到数据持久化的效果 文章最后有源码展示 学生信息管理系统1.0静态版- ...

  4. 可文件存储的动态通讯录(C语言)

    目录 通讯录结构体 第一次启动通讯录 通讯录初始化 通讯录扩容 导出文件中存储的数据 数据导入文件 传送门(全代码) 通讯录是个结构体数组,而数组的特性就是一次给定一个固定的大小之后是不可以增容的,但 ...

  5. 对象存储与块存储、文件存储等对比

    看到 一篇文档, 讲 对象存储, 好奇,搜索文章,摘抄,学习记录 ! 背景: 传统存储在面对海量非结构化数据时,在存储.分享与容灾上面临很大的挑战,主要表现在以下几个方面:传统存储并非为非结构化内容设 ...

  6. BigData之MongoDB:MongoDB基于分布式文件存储数据库的简介、下载、案例应用之详细攻略

    BigData之MongoDB:MongoDB基于分布式文件存储数据库的简介.下载.案例应用之详细攻略 目录 MongoDB的简介 1.MongoDB的主要功能特性 2.对MongoDB进行增删改查( ...

  7. MongoDB 一个基于分布式文件存储的数据库

    简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. 特点 它的特点是高性能.易部署.易使用,存储数据非常方便.主要功能特性有: ...

  8. python 内存数据库下载,Python 文件存储和数据库

    Python 中数据存储的方式和其他语言没什么区别,主要分为两个方面:文件存储和数据库存储. 文件存储 文件存储的方法也分为很多种,主要包括:Python 内置方法 NumPy 模块方法 os 模块方 ...

  9. c语言计算文件摘要值,c语言文件操作摘要.ppt

    c语言文件操作摘要 第十章 文件 10.1 C文件概述 文件:存储在外部介质上数据的集合,是操作系统数据管理的单位 文件处理方法 缓冲文件系统:高级文件系统,系统自动为正在使用的文件开辟内存缓冲区 非 ...

最新文章

  1. 插入透明32位png格式图片支持ie5.5+、 FF、chrome、safari
  2. Hadoop与Spark常用配置参数总结
  3. SQL实战篇:SQL基础及执行顺序
  4. 站在物联网风口,如何抢滩千亿级智能家居市场?
  5. 现代控制理论输出y_现代控制理论线性系统入门(三)输入输出变量的稳定性
  6. Kaldi AMI数据集脚本学习1----cmd.sh和path.sh
  7. [译]用javascript实现一门编程语言-词法分析
  8. 2017华为软件精英挑战赛思路分析
  9. HTML——HTML基础语法
  10. 数据结构 --- c语言实现双向循环链表
  11. steam网络相关问题-社区错误代码118/无法自动登陆/短期内来自您网络的失败登录过多/无法连接至steam网络(2021/2/18更新)
  12. 3分钟阿里云无影云桌面全解析(云桌面/传统电脑/VDI/服务器区别对比)
  13. 手机企业邮箱客户端哪个好用?
  14. China Mobile”、“China Unicom”,请修改为中国移动、中国联通
  15. “指尖上的中华”非遗国技互动展亮相上海徐家汇站
  16. Green threads
  17. android+插入一条短信,android添加一条短信记录
  18. ZanProxy —— 本地代码调试线上页面,环境再也不是问题
  19. 免费资源丨全新版三维制作cinema4D自学零基础教程
  20. Greenplum集群安装配置及最佳实践

热门文章

  1. ABAP基本语法(一)附带思维导图学习材料等
  2. 宏基因组分析教程-Analysis of Metagenomic Data
  3. 分享几个appstore之外的iOS软件下载网址
  4. vue2.x中slot-scope插槽在vue3.x中的新写法
  5. qcow2快照合并或删除
  6. jboss简单使用--刚开始接触,感觉还是比较详细的
  7. nginx-rtmp 的部署
  8. 年轻的艺术家们是如何通过NFT赚取百万美元的?
  9. ngx-markdown 安装和使用
  10. 证明:1/n调和级数为何是发散的