C 文件处理

文件是计算机存储设备中用于存储数据的容器。


为什么需要文件?

  • 当程序终止时,所有数据都会丢失。将数据存储在文件中可以保留您的数据,即使程序终止。
  • 如果您必须输入大量数据,输入所有数据将花费很多时间。
    但是,如果您有一个包含所有数据的文件,您可以使用 C 中的几个命令轻松访问文件内容。
  • 您可以轻松地将数据从一台计算机移动到另一台计算机,而无需进行任何更改。

文件类型

处理文件时,您应该了解两种文件类型

  1. 文本文件
  2. 二进制文件

1. 文本文件

文本文件是普通的 .txt 文件。您可以使用记事本等任何简单的文本编辑器轻松创建文本文件。

当您打开这些文件时,您会看到文件中的所有内容均为纯文本。您可以轻松编辑或删除内容。

它们维护起来最省力,易于阅读,安全性最低,并且需要更大的存储空间。

2. 二进制文件

二进制文件通常是您计算机中的 .bin 文件。

它们不存储纯文本数据,而是以二进制形式(0 和 1)存储数据。

它们可以容纳更多数据,不易读取,并且比文本文件提供更好的安全性。


文件操作

在 C 中,您可以对文本或二进制文件执行四种主要操作

  1. 创建新文件
  2. 打开现有文件
  3. 关闭文件
  4. 读写文件信息

处理文件

处理文件时,需要声明一个文件类型的指针。此声明对于文件与程序之间的通信是必需的。

FILE *fptr;

打开文件 - 用于创建和编辑

打开文件使用 `stdio.h` 头文件中定义的 `fopen()` 函数。

在标准 I/O 中打开文件的语法是

ptr = fopen("fileopen","mode");

例如,

fopen("E:\\cprogram\\newprogram.txt","w");

fopen("E:\\cprogram\\oldprogram.bin","rb");
  • 假设文件 `newprogram.txt` 不存在于 `E:\cprogram` 位置。第一个函数根据模式 **'w'** 创建一个名为 `newprogram.txt` 的新文件并打开它进行写入。
    写入模式允许您创建和编辑(覆盖)文件的内容。
  • 现在,假设第二个二进制文件 `oldprogram.bin` 存在于 `E:\cprogram` 位置。第二个函数以二进制模式 **'rb'** 打开现有文件进行读取。
    读取模式只允许您读取文件,不能写入文件。
标准 I/O 中的打开模式
模式 模式含义 文件不存在时
r 打开以供读取。 如果文件不存在,`fopen()` 返回 NULL。
rb 以二进制模式打开以供读取。 如果文件不存在,`fopen()` 返回 NULL。
w 打开以供写入。 如果文件存在,其内容将被覆盖。
如果文件不存在,它将被创建。
wb 以二进制模式打开以供写入。 如果文件存在,其内容将被覆盖。
如果文件不存在,它将被创建。
a 打开以供追加。
数据将被添加到文件末尾。
如果文件不存在,它将被创建。
ab 以二进制模式打开以供追加。
数据将被添加到文件末尾。
如果文件不存在,它将被创建。
r+ 打开以供读取和写入。 如果文件不存在,`fopen()` 返回 NULL。
rb+ 以二进制模式打开以供读取和写入。 如果文件不存在,`fopen()` 返回 NULL。
w+ 打开以供读取和写入。 如果文件存在,其内容将被覆盖。
如果文件不存在,它将被创建。
wb+ 以二进制模式打开以供读取和写入。 如果文件存在,其内容将被覆盖。
如果文件不存在,它将被创建。
a+ 打开以供读取和追加。 如果文件不存在,它将被创建。
ab+ 以二进制模式打开以供读取和追加。 如果文件不存在,它将被创建。

关闭文件

读写后应关闭文件(文本和二进制)。

关闭文件使用 `fclose()` 函数。

fclose(fptr);

这里,`fptr` 是与要关闭的文件关联的文件指针。


读写文本文件

对于文本文件的读写,我们使用 `fprintf()` 和 `fscanf()` 函数。

它们只是 `printf()` 和 `scanf()` 的文件版本。唯一的区别是 `fprintf()` 和 `fscanf()` 需要 FILE 结构体的指针。


示例 1:写入文本文件

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int num;
   FILE *fptr;

   // use appropriate location if you are using MacOS or Linux
   fptr = fopen("C:\\program.txt","w");

   if(fptr == NULL)
   {
      printf("Error!");   
      exit(1);             
   }

   printf("Enter num: ");
   scanf("%d",&num);

   fprintf(fptr,"%d",num);
   fclose(fptr);

   return 0;
}

此程序从用户那里获取一个数字并将其存储在文件 `program.txt` 中。

编译并运行此程序后,您可以在计算机的 C 盘中看到一个名为 `program.txt` 的文本文件。打开文件时,您可以看到输入的整数。


示例 2:从文本文件读取

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int num;
   FILE *fptr;

   if ((fptr = fopen("C:\\program.txt","r")) == NULL){
       printf("Error! opening file");

       // Program exits if the file pointer returns NULL.
       exit(1);
   }

   fscanf(fptr,"%d", &num);

   printf("Value of n=%d", num);
   fclose(fptr); 
  
   return 0;
}

此程序读取 `program.txt` 文件中存在的整数并将其打印到屏幕上。

如果您成功创建了**示例 1**中的文件,运行此程序将获取您输入的整数。

`fgetchar()`、`fputc()` 等其他函数可以类似地使用。


读写二进制文件

对于二进制文件,分别使用 `fread()` 和 `fwrite()` 函数从磁盘文件读取和写入。


写入二进制文件

要写入二进制文件,您需要使用 `fwrite()` 函数。该函数接受四个参数

  1. 要写入磁盘的数据地址
  2. 要写入磁盘的数据大小
  3. 此类数据的数量
  4. 您要写入的文件的指针。
fwrite(addressData, sizeData, numbersData, pointerToFile);

示例 3:使用 fwrite() 写入二进制文件

#include <stdio.h>
#include <stdlib.h>

struct threeNum
{
   int n1, n2, n3;
};

int main()
{
   int n;
   struct threeNum num;
   FILE *fptr;

   if ((fptr = fopen("C:\\program.bin","wb")) == NULL){
       printf("Error! opening file");

       // Program exits if the file pointer returns NULL.
       exit(1);
   }

   for(n = 1; n < 5; ++n)
   {
      num.n1 = n;
      num.n2 = 5*n;
      num.n3 = 5*n + 1;
      fwrite(&num, sizeof(struct threeNum), 1, fptr); 
   }
   fclose(fptr); 
  
   return 0;
}

在此程序中,我们在 C 盘中创建一个名为 `program.bin` 的新文件。

我们声明一个名为 `threeNum` 的结构体,包含三个数字 - `n1`、`n2` 和 `n3`,并在 `main` 函数中将其定义为 `num`。

现在,在 for 循环中,我们使用 `fwrite()` 将值存储到文件中。

第一个参数是 `num` 的地址,第二个参数是 `threeNum` 结构体的大小。

由于我们只插入 `num` 的一个实例,因此第三个参数是 `1`。最后一个参数 `*fptr` 指向我们正在存储数据的文件的指针。

最后,我们关闭文件。


从二进制文件读取

与上面的 `fwrite()` 函数类似,`fread()` 函数也接受 4 个参数。

fread(addressData, sizeData, numbersData, pointerToFile);

示例 4:使用 fread() 从二进制文件读取

#include <stdio.h>
#include <stdlib.h>

struct threeNum
{
   int n1, n2, n3;
};

int main()
{
   int n;
   struct threeNum num;
   FILE *fptr;

   if ((fptr = fopen("C:\\program.bin","rb")) == NULL){
       printf("Error! opening file");

       // Program exits if the file pointer returns NULL.
       exit(1);
   }

   for(n = 1; n < 5; ++n)
   {
      fread(&num, sizeof(struct threeNum), 1, fptr); 
      printf("n1: %d\tn2: %d\tn3: %d\n", num.n1, num.n2, num.n3);
   }
   fclose(fptr); 
  
   return 0;
}

在此程序中,您读取相同的文件 `program.bin` 并逐条记录进行循环。

简单来说,您从 `*fptr` 指向的文件中读取一个大小为 `threeNum` 的 `threeNum` 记录到 `num` 结构体中。

您将获得在**示例 3** 中插入的相同记录。


使用 fseek() 获取数据

如果文件中有许多记录,并且需要访问特定位置的记录,则需要循环遍历其之前的所有记录才能获取该记录。

这将浪费大量内存和操作时间。使用 `fseek()` 可以更轻松地获取所需数据。

顾名思义,`fseek()` 将光标定位到文件中的给定记录。


fseek() 的语法

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

第一个参数 stream 是指向文件的指针。第二个参数是要查找的记录的位置,第三个参数指定偏移量开始的位置。

fseek() 中的不同 whence
Whence 含义
SEEK_SET 从文件开头开始偏移。
SEEK_END 从文件末尾开始偏移。
SEEK_CUR 从文件光标的当前位置开始偏移。

示例 5:fseek()

#include <stdio.h>
#include <stdlib.h>

struct threeNum
{
   int n1, n2, n3;
};

int main()
{
   int n;
   struct threeNum num;
   FILE *fptr;

   if ((fptr = fopen("C:\\program.bin","rb")) == NULL){
       printf("Error! opening file");

       // Program exits if the file pointer returns NULL.
       exit(1);
   }
   
   // Moves the cursor to the end of the file
   fseek(fptr, -sizeof(struct threeNum), SEEK_END);

   for(n = 1; n < 5; ++n)
   {
      fread(&num, sizeof(struct threeNum), 1, fptr); 
      printf("n1: %d\tn2: %d\tn3: %d\n", num.n1, num.n2, num.n3);
      fseek(fptr, -2*sizeof(struct threeNum), SEEK_CUR);
   }
   fclose(fptr); 
  
   return 0;
}

此程序将以相反的顺序(从最后一个到第一个)开始读取文件 `program.bin` 中的记录并打印出来。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战