深度剖析 —— 文件操作

系列文章目录

深度剖析:数据
深度剖析:递归
深度剖析:结构体
深度剖析:动态内存管理
深度剖析:文件操作
深度剖析:预处理


文章目录

  • 系列文章目录
  • 前言
  • 一、示例问题:通讯录的信息存储
    • 1.通讯录的需求
  • 二、文件的存储
    • 1.标准 I/O
    • 2.文件分类
    • 3.文件类型
    • 4.文件缓冲区
    • 5.文件指针
  • 三、文件的打开与关闭
    • 1.文件的开关函数
      • - fopen
        • 调用结构
      • - fclose
        • 调用结构
        • 调用示例
    • 2.文件的打开方式
  • 四、文件的顺序读写
    • 1.字符输入输出(fputc & fgetc)
      • - fputc
        • 调用结构
        • 调用示例
      • - fgetc
        • 调用结构
        • 调用示例
    • 2.文本行输入输出函数(fputs & fgets)
      • - fputs
        • 调用结构
        • 调用示例
      • - fgets
        • 调用结构
        • 调用示例
    • 3.格式化输入输出函数(fprintf & fscanf)
      • - fprintf
        • 调用结构
        • 调用示例
      • - fscanf
        • 调用结构
        • 调用示例
    • 4.二进制输入输出函数(fwrite & fread)
      • - fwrite
        • 调用结构
        • 调用示例
      • - fread
        • 调用结构
        • 调用示例
  • 五、文件的随机读写与结束判定
    • 1.文件的随机读写
      • - fseek
        • 调用结构
      • - fwind
        • 调用结构
    • 2.文件的结束判定
      • - feof
        • 调用结构
  • 总结

前言

文件是当今计算机系统十分重要的部分。文件用于存储程序、文档、数据、图形、照片、视频、书信、表格和许多其他种类的信息。作为程序员,必须会编写创建文件和从文件读写数据的程序。本文将介绍相关的内容。

一、示例问题:通讯录的信息存储

1.通讯录的需求

通讯录需要一块可以保存信息的空间,当需要时可随时调取

我们常用的通讯录在使用之后可以将信息进行存储,当我们再次需要调取信息时,里面的内容依然存在,这时我们就需要一个文件去存储相关信息。文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

二、文件的存储

1.标准 I/O

I/O 的两个级别 (即处理文件访问的两个级别) :

  1. 底层 I/O(low-level I/O):使用操作系统提供的基本 I/O 。
  2. 标准高级 I/O(standard high-level I/O):使用C库的标准包stdio.h头文件定义。

标准 I/O 的优势:

  1. 标准 I/O 有许多专门的函数简化了处理不同 I/O 的问题。
  2. 可移植性更好。
  3. 输入和输出都是缓冲的。也就是说,一次转移一大块信息而不是一字节信息(通常至少512字节)。
  4. 拥有更高的数据传输速率。

2.文件分类

在程序设计中,我们一般谈的文件有两种:程序文件、数据文件

程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

3.文件类型

根据数据的组织形式,数据文件被称为文本文件或者二进制文件

文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

小明男11111某某大学![请添加图片描述](https://img-blog.csdnimg.cn/e0eb22cafa364885a826c550de69704a.png)

二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。

4.文件缓冲区

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

5.文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”

FILE*pf;//文件指针变量

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及 文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关 心细节。

定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文 件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。

三、文件的打开与关闭

1.文件的开关函数

- fopen

调用结构
FILE*fopen("文件名.后缀","打开方式");

返回一个指针,需要判断是否为空指针

- fclose

调用结构
intfclose(文件地址);

关闭成功返回0,否则返回EOF(-1)

调用示例
FILE*P_File=fopen("文件名.后缀","打开方式");if(P_File==NULL){perror("打开文件失败:");}fclose(P_File);

2.文件的打开方式

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据出错
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

四、文件的顺序读写

1.字符输入输出(fputc & fgetc)

- fputc

调用结构
intfputc(int,FILE*stream);

向文件写入数据(从末尾开始写入)

调用示例
fputc('1',P_File);fputc('2',P_File);fputc('3',P_File);

- fgetc

调用结构
intfgetc(FILE*stream);

向文件读数据(从开头开始按顺序读,读一次向后走一格)

  1. 读取结束返回EOF
  2. 正常读取返回ASCII码值
调用示例
intret=fgetc(P_File);printf("%c",ret);

2.文本行输入输出函数(fputs & fgets)

- fputs

调用结构
intfputs(constchar*str,File*stream);

向文件写入数据(从末尾开始写入)

调用示例
fputs("abcdef\n",P_File);fputs("123456\n",P_File);

- fgets

调用结构
char*fgets(char*str,intn,FILE*stream)

读取数 n 包括'/0'的位置,所以最多读 n - 1 个

调用示例
fgets(arr,5,P_File);printf("%s\n",arr);fgets(arr,20,P_File);printf("%s\n",arr);

3.格式化输入输出函数(fprintf & fscanf)

- fprintf

调用结构
intfprintf(File*stream,后面与printf一致);

向文件写入数据(从末尾开始写入)

调用示例
fprintf(P_File,"%s\n","hello world!");

- fscanf

调用结构
intfscanf(File*stream,后面与scanf一致);

读取(从开始位置)

调用示例
fscanf(P_File,"%s",arr);printf("%s\n",arr);

4.二进制输入输出函数(fwrite & fread)

- fwrite

调用结构
intfwrite(要写入的元素的地址,每个元素的大小,元素的数量,File*stream);

以二进制的方式写入(字符串以二进制和文本写入结果一样)

调用示例
fwrite(arr,sizeof(char),20,P_File);

- fread

调用结构
intfread(要读取的元素的地址,每个元素的大小,元素的数量,File*stream);

返回读取到的完整元素的个数,如果读取到的个数 < 实际要读的个数,则为最后一次读取

调用示例
fread(arr,sizeof(char),20,P_File);

五、文件的随机读写与结束判定

1.文件的随机读写

- fseek

调用结构
intfseek("文件",偏移量,偏移的起始位置);

确定书写的位置

  1. SEEK_CUR - 当前位置
  2. SEEK_SET - 开头位置
  3. SEEK_END - 结尾位置(当指向结尾时,偏移量只能为负)

- fwind

调用结构
intfwind("文件");

让文件指针回到起始位置

2.文件的结束判定

- feof

调用结构
intfeof(文件地址);

是判断读取失败结束,还是遇到文件结尾结束

  1. 文本文件读取是否结束,判断返回值是否为EOF,或者是否为NULL等。每个函数有每个特定的结束标志。
  2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

总结

对于大多数的程序而言,文件的相关操作必不可少。标准 I/O 包自动创建输入和输出缓冲区以加快数据传输。多种输入输出方式使得程序更加灵活便捷,大大提高了程序的效率,同时也兼具良好的可移植性。