【C语言】文件的操作与文件函数的使用(详细讲解)

简介: 【C语言】文件的操作与文件函数的使用(详细讲解)

前言:我们在学习C语言的时候会发现在编写一个程序的时候,数据是存在内存当中的,而当我们退出这个程序的时候会发现这个数据不复存在了,因此我们可以通过文件把数据记录下来,使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。


d6dc0126edd141a985d72de501ef756b.jpg

什么是文件

磁盘上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

程序文件

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

数据文件

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

文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如:

为了方便起见,文件标识常被称为文件名。

文件的打开和关闭

文件指针

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

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

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。

一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

下面我们一起来创建一个文件指针变量:

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

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

代码实例:

int main()
{
  FILE* pf = fopen("text.txt", "w");//打开一个文件,以写的形式
  if (pf == NULL)
  {
    perror("fopen");
    return 1;
  }
  //写文件
  char ch = 0;
  fputc('a', pf);//写入字符a到文件中
  fclose(pf);
  pf = NULL;
  return 0;
}

运行结果:

我们注意此时的光标是指向的字符a,也就是文件指针是指向了a的位置,且随着你追加的字符,文件指针的位置也会随着改变。

文件的打开和关闭

  • 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
  • 在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
  • ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。

文件的打开(fopen函数)

fopen函数用来打开一个文件,具体如何使用我们看下图:

具体代码演示:

//以写的形式打开一个名称为:test的文本
FILE* pf = fopen("test.txt", "w");

文件的关闭(fclose函数)

fclose函数,用来关闭打开的文件。具体用法如下

具体代码演示:

FILE* pf = fopen("test.txt", "w");
fclose(pf);//关闭打开的文件

关于文件的使用方式

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

实例代码演示:

int main()
{
    //打开文件
    FILE* pFile = fopen("myfile.txt", "w");
    //文件操作
    if (pFile != NULL)//判断文件是否打开失败,fopen函数打开失败会返回NULL
    {
        fputs("fopen example", pFile);//写入信息(输出流)
        //关闭文件
        fclose(pFile);//关闭文件
        pFile = NULL;//置为空指针,防止野指针
    }
    return 0;
}

运行结果:

注:在你对应的程序的文件夹下面会生成此文件,并如下图所示


文件的顺序读写

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

什么是输入流和输出流、标准错误流?

  1. 输出流(stdout):简单的理解就是你把你C语言所写的程序,传递给了某个文件就是输出流。如下图:

  2. 输入流(stdin):你用这C语言程序读取文件,就是输入流,如上图所示。
  3. 标准错误流(stderr)

文件函数的用法

fgetc函数

fgetc字符输入函数,可以通俗的理解从文件中读取字符信息

来看代码实例演示:

我们先创建一个text的文本,在里面放入如下图所示的信息:

我们来编写一个程序来读取该文本中的信息

int main()
{
  FILE* pf = fopen("text.txt", "r");
  if (pf == NULL)//判断是打开文件失败
  {
    perror("fopen");
    return 1;
  }
  //读文件
  int ch = fgetc(pf);//将读取到的字符保存在ch中
  printf("%c ", ch);//打印读取的字符
  ch = fgetc(pf);
  printf("%c ", ch);
  ch = fgetc(pf);
  printf("%c ", ch);
  ch = fgetc(pf);
  printf("%c ", ch);
  ch = fgetc(pf);
  printf("%c ", ch);
  fclose(pf);
  pf = NULL;
  return 0;
}

运行结果:


fputc函数

fputc字符输出函数,可以通俗的理解成在C语言程序输出字符到文件中去。

代码实例演示:

//读写文件
int main()
{
  FILE* pf = fopen("weiwei.txt", "w");//以写的形式打开文件
  if (pf == NULL)//判断打开是否成共
  {
    perror("fopen");//找出错误原因
    return 1;
  }
  //写文件
  char ch = 0;
  for (ch = 'a'; ch <= 'z'; ch++)
  {
    if (ch % 5 == 0)
    {
      fputc('\n', pf);//每个五的倍数的ASCII值换行一次
    }
    fputc(ch, pf);//把ch中的字符写入文件指针pf中
  }
  fclose(pf);//关闭文件
  pf = NULL;
  return 0;
}

运行结果:


fgets函数

fgetc函数文本行输出函数,通俗的理解从文件中读取行数据

代码实例演示:

int main()
{
    char mystring[100] = { 0 };//创建一个字符数组用来存放读取的数据
    FILE* pFile = fopen("myfile.txt", "r");//以读的形式打开文件
    if (pFile == NULL) //判断是否打开成功
        perror("Error opening file");
    else 
    {
        if (fgets(mystring, 20, pFile) != NULL)//读取20个字符数据(20个字节,一个汉字是两个字节)
            puts(mystring);//输出字符数组
        fclose(pFile);//关闭文件
        pFile = NULL;//置为空指针
    }
    return 0;
}

文本中的数据:

运行结果:


fputs函数

fputc函数,可以通俗的理解成将数组中的字符串写入文件中。

代码实例演示:

int main()
{
  char sentence[256] = { 0 };
  printf("请追加一个句子: ");
  fgets(sentence, 256, stdin);//将输入流中(即文件中)数据放在字符数组中
  FILE* pFile = fopen("zhouzhou.txt", "a");
  fputs(sentence, pFile);//将字符数组中的数据写入文件当中
  fclose(pFile);
  return 0;
}

运行结果:


fscanf函数

fscanf函数,可以通俗的理解成以格式化的形式读取文件中的数据

代码示例演示:

int main()
{
  char str[80];
  float f;
  FILE* pFile = fopen("mywei.txt", "w+");//以读和写的形式打开一个文件
  fprintf(pFile, "%f %s", 3.1416, "PI");//把该数据格式化写入到文件中去
  rewind(pFile);//将指针初始化
  fscanf(pFile, "%f", &f);//格式化读取文件中的数据
  fscanf(pFile, "%s", str);//同理
  fclose(pFile);//关闭文件
  pFile = NULL;
  printf("I have read: %f and %s \n", f, str);
  return 0;
}

运行结果:


fprintf函数

fprintf函数,可以通俗的理解成从C语言程序中格式化的写入数据到文件去。

代码实例演示:

int main()
{
    int n;
    char name[100];
    FILE* pFile = fopen("myfile.txt", "w");
    for (n = 0; n < 3; n++)
    {
        puts("please, enter a name: ");
        gets(name);//读取字符
        fprintf(pFile, "Name %d [%-10.10s]\n", n + 1, name);//格式化写入数据到文件中
    }
    fclose(pFile);
    return 0;
}

运行结果:


fread函数

fread函数,可以通俗的理解成用二进制的形式读取文件

代码实例演示:

int main()
{
  int arr[10] = { 1222,1222,33,19,5,6,7,8,9,10 };
  //int arr[10] = {0};
  FILE* pf = fopen("wwwagou.bin", "wb");//读写
  if (pf == NULL)
  {
    perror("fopen");
    return 1;
  }
  //二进制的写文件
  fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
  //二进制的读文件
  fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  fclose(pf);
  pf = NULL;
  return 0;
}

运行结果:


fwrite函数

fwrite函数,可以通俗的理解成用二进制的形式写入数据到文件中。

代码实例演示:

int main()
{
  char buffer[] = { 'x' , 'y' , 'z' };
  FILE* pFile = fopen("myfile.bin", "wb");//以二进制的形式写入
  fwrite(buffer, sizeof(char), sizeof(buffer), pFile);//写入数据
  fclose(pFile);
  return 0;
}

运行结果:

在记事本中打开会发现我们可以看懂这个代码

假若修改一下:

int main()
{
  int buffer[] = {1,2,3};
  FILE* pFile = fopen("myfile.bin", "wb");//以二进制的形式写入
  fwrite(buffer, sizeof(int), sizeof(buffer), pFile);//写入数据
  fclose(pFile);
  return 0;
}

我们此时会发现在记事本中是一串乱码,因此我们要在Vscode中去读取


fseek函数

feek函数,可以理解成根据文件指针的位置和偏移量来定位文件指针,也就是随机在哪里读写数据。

代码实例演示

int main()
{
  FILE* pFile = fopen("example.txt", "wb");
  if (pFile == NULL)
  {
    perror("fopen");
    return 1;
  }
  fputs("This is an apple.", pFile);//写入数据到文件中
  fseek(pFile, 9, SEEK_SET);//调整光标(指针为起始位置往后第九个的位置)
  fputs(" sam", pFile);//从光标(指针指向第九个字符的位置)追加新的数据
  fclose(pFile);//关闭文件
  pFile = NULL;
  return 0;
}

运行结果:


ftell函数

ftell函数,通俗的讲就是返回当前位置的光标距离起始位置的距离的值

代码实例演示:

int main()
{
    long size;
    FILE* pFile = fopen("myfile.txt", "rb");//读写一个二进制文件
    if (pFile == NULL) 
        perror("Error opening file");
    else
    {
        fseek(pFile, 0, SEEK_END);   // non-portable
        size = ftell(pFile);//计算举例起始位置的距离
        fclose(pFile);
        printf("Size of myfile.txt: %1d bytes.\n", size);
    }
    return 0;
}


文件中原本的样子:

运行结果:


rewind函数

rewind函数,让文件指针的位置回到文件的起始位置

代码实例演示:

int main()
{
    int n;
    FILE* pFile;
    char buffer[27];
    pFile = fopen("myfile1.txt", "w+");
    for (n = 'A'; n <= 'Z'; n++)
        fputc(n, pFile);//将数据写入文件
    rewind(pFile);//回到起始位置
    fread(buffer, 1, 26, pFile);//读取
    fclose(pFile);
    buffer[26] = '\0';
    puts(buffer);//输出数据
    return 0;
}


运行结果:


feof函数

feof函数,在文件读取结束后,用来判断文件是否遇到文件末尾而结束

代码实例演示:

int main()
{
    FILE* pFile;
    int n = 0;
    pFile = fopen("myfile.txt", "rb");//读写的形式打开文件
    if (pFile == NULL) 
        perror("Error opening file");
    else
    {
        while (fgetc(pFile) != EOF)//读取里面的数据 
        {
            ++n;//判断有几个bytes
        }
        if (feof(pFile))//判断是否正常结束
        {
            puts("End-of-File reached.");//正常结束
            printf("Total number of bytes read: %d\n", n);//计算总字节数
        }
        else 
            puts("End-of-File was not reached.");
        fclose(pFile);
    }
    return 0;
}

记事本中的数据:

运行结果:


ferror函数

ferror函数,用来判断在文件读取结束后,用来判断文件是否因为读取过程当中遇到错误而结束!

代码实例演示:

int main()
{
    FILE* pFile;
    pFile = fopen("myfile.txt", "r");
    if (pFile == NULL) 
        perror("Error opening file");
    else 
    {
        fputc('x', pFile);//写入数据
        if (ferror(pFile))//判断在读取的过程中的错误
            printf("Error Writing to myfile.txt\n");
            perror("pFile");//判断错误原因
        fclose(pFile);//因为是以读的形式,所以失败
    }
    return 0;
}

运行结果:


文本文件和二进制文件

  • 根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
  • 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
  • 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
  • 一个数据在内存中是怎么存储的呢?
  • 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2013测试)。

文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。


结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


相关文章
|
2月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
225 15
|
8月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
357 23
|
3月前
|
存储 编译器 程序员
c语言的文件操作与文件缓冲区
如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。磁盘(硬盘)上的文件是文件。但是在程序设计中,我们⼀般谈的⽂件有两种:程序文件、数据文件(从文件功能的角度来分类 的)。就比如说我们电脑中以.txt为后缀的就是文件的一种,他就是数据文件。.exe为后缀的就为程序文件。函数名功能适用范围fgetc字符输入函数所有输入流fputc字符输出函数所有输出流fgets。
92 0
|
7月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
156 1
一文彻底搞清楚C语言的函数
|
7月前
|
人工智能 C语言
|
8月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
299 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
|
8月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
138 24
|
8月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
344 16
|
8月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
193 3
|
8月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
164 2