C语言文件详解

文件

文件概述

存储在内存储器的集合,一般称为表,如数组;而存储在外部介质上的信息集合称为文件,如磁盘文件。

文件通常是驻留在外部介质(如磁盘等)上的,使用时才调入内存。

  • 文件的分类

    • 从用户的角度:普通文件和设备文件

      • 普通文件:驻留在磁盘或其他外部介质上的一个有序数据集,其又分为:

        • 程序文件:源文件(后缀为.c)、目标文件(后缀为.obj)、可执行程序(.exe);
        • 数据文件:一组待输入处理的原始数据,或者是一组输出的结果。
      • 设备文件:与主机相连的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看做一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。

        通常把显示器定义为标准输出文件,在屏幕上显示有关信息就是向标准输出文件输出;把键盘被指定为标准输入文件,从标准输入文件上输入数据。

    • 从文件编码的方式的角度:ASCII码文件和二进制文件

      • ASCII码文件:也称为文本文件,这种文件在磁盘中存放时每个字符对于1个字节,用于存放对应的ASCII码,ASCII码文件可在屏幕上按字符显示,因此能够读懂文件内容

        • ASCII码文件以字符形式存储,读写位复制,需要转换,传输效率低,占用外存空间较大

        • 例如:数1234按ASCII码存储

          00110001 00110010 00110011 00110100 //占4个字节
          
      • 二进制文件:是按二进制的编码方式来存放文件的,只占2个字节,二进制文件虽然也可以在屏幕上显示,但其内容无法读懂

        • 二进制文件的存储形式与数据在内存中的存储形式相同,读写位复制,不需要转换,传输效率高,节省外存空间

        • 例如:数1234按二进制存储

          00000100 11010010  //占2个字节
          
  • 读写文件

    • 写文件

      • 是从内存向磁盘输出数据。首先将内存中的数据送到文件缓冲区,待文件缓冲区满后写入磁盘
    • 读文件

      • 是从磁盘读取数据存入内存。首先从磁盘读出一批数据送到文件缓冲区,然后从文件缓冲区取出数据存入内存
  • 文件名

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

    文件名包含3个部分:文件路径+文件名主干+文件后缀 例如:c:\code\test.txt

    文件标识常被称为文件名

文件类型指针

  • 文件缓冲区:每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态以及文件当前的位置),这些信息是保存在一个结构体变量之中;

    该结构体类型是有系统声明的,取名FIEL

  • 文件指针:在C语言中用一个指针变量指向一个文件,通过文件指针就可以对它所指的文件进行各种操作。

  • 一般通过一个FILE的指针来维护这个FILE结构的变量

  • 文件指针定义的一般格式:FILE 文件指针

    其中:FILE是文件缓冲区的类型名,必须大写;“文件指针”是指向文件缓冲区的指针

    例如:

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

    定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。

    通过该文件信息区中的信息就能够访问该文件(通过文件指针能够找到与他关联的文件)

文件基本操作

文件处理必须包含3个基本过程:打开文件,读或写、关闭文件;

在C语言中,文件操作都是由库函数来完成的

文件的打开和关闭

  • 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件

    • 打开文件:建立文件的各种有关信息,并使文件指针指向该文件,以便进行其他操作
    • 关闭文件:断开指针与文件之间的联系,也就禁止了再对文件进行操作
  • 打开文件需调用库 fopen 函数:

    • 一般调用格式:FILE * fopen ( const char * filename, const char * mode );

    • filename是指定打开的文件名,可以包含盘符、路径、文件名,是字符串

    • mode指定打开的文件读写方式,是字符串,必须小写;

    • 返回指定打开的文件的指针

      • mode如下:

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

    • 一般调用形式:int fclose ( FILE * stream );
  • 例:

    #include <stdio.h>
    int main()
    {
        //打开文件
    	FILE* pf = fopen("test.dat", "w");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 0;
    	}
    	//关闭文件
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    

文件的顺序读写

  • 对文件的读和写是最常用的文件操作。在C语言中提供了多种文件读写的函数;使用这些函数需要包含头文件stdio.h

  • 注明:对这些函数的使用只是简单的说明,更加详细的内容可以到 cplusplus.com - The C++ Resources Network 查询

  • 在讲这些函数之前,我们先讲一下 “流” 是什么?

    流(stream)是一个很抽象的概念,通常认为:流是磁盘或其它外围设备关联的数据的源或目的地

    通俗的讲,我们写了一个程序,我们可以把它的数据传到屏幕、硬盘、U盘、光盘、网络、软盘上,而这些硬件的读写方式不同,所以我们在程序和这些硬件之间,抽象出一个空间,这个空间就是**“流”**,这样我们只需要将程序的数据传到“流”里面,然后再传到不同的硬件。

    在C语言程序中,我们默认打开了3个流,即

    ​ stdin - 标准输入流 - 键盘

    ​ stdout - 标准输出流 - 屏幕

    ​ stderr - 标准出错流 - 屏幕

    这3个流的类型是FIEL*

    • 文件读写函数:

      • 字符读写函数:fgetcfputc(适用于所有输入流和输出流)

      • 字符串读写函数:fgetsfputs(适用于所有输入流和输出流)

      • 格式化读写函数:fscanffprintf(适用于所有输入流和输出流)

      • 数据块读写函数:freadfwrite(只适用于文件)

    • fgetc函数:从指定的磁盘文件中读一字符。

      调用格式:int fgetc ( FILE * stream );

      返回值:读取成功,返回字符值;读取失败,返回EOF(-1)

      注意:EOF只适用于ASCII码文件

    • fputc函数:将一个字符写到指定的磁盘文件。

      调用格式:int fputc ( int character, FILE * stream );

      返回值:写成功,返回字符值;写失败,返回EOF(-1)

    • fgets函数:从指定的磁盘文件中读一字符串。

      调用格式:char * fgets ( char * str, int num, FILE * stream );

      返回值:读成功,返回字符串的首地址;读失败或者遇到文件结束符,返回EOF(-1)

      注意:其实只能读取 num-1 个字符数,因为有\0

    • fputs函数:将一个字符串写到指定的磁盘文件。

      调用格式:int fputs ( const char * string, FILE * stream );

      返回值:写成功,返回0;写失败,返回EOF(-1)

    • fprintf函数:将一个数据块写到指定的磁盘文件。

      调用格式:int fprintf ( FILE * stream, const char * format, ... );

    • fscanf函数:从指定的硬盘文件中读一个数据块

      调用格式:int fscanf ( FILE * stream, const char * format, ... );

    • fwrite函数:将一个数据块写到指定的磁盘文件。

      调用格式:size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

      返回值:写成功,返回数据项数目;写失败,返回0;

    • fread函数:从指定的磁盘文件读一个数据块。

      调用格式:size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

      返回值:读成功,返回数据项数目;读失败或遇到文件结束符,返回0;

  • 例:

    • 例1:

      #include <stdio.h>
      int main()
      {
      	FILE* pf = fopen("test.dat", "w");
      	if (pf == NULL)
      	{
      		perror("fopen");
      		return 0;
      	}
      	//写文件
      	fputc('d', pf);        //写单个字符
      	fputs("我爱美女", pf);  //写文本行
      	//关闭文件
      	fclose(pf);
      	pf = NULL;
      	return 0;
      }
      
    • 例2:

      //运用C语言标准输出流 stdout
      #include <stdio.h>
      int main()
      {
      	FILE* pf = fopen("test.dat", "w");
      	if (pf == NULL)
      	{
      		perror("fopen");
      		return 0;
      	}
      	//写文件
      	fputs("我爱美女", stdout);
      	//关闭文件
      	fclose(pf);
      	pf = NULL;
      	return 0;
      }
      

      例3:

      #include <stdio.h>
      int main()
      {
      	char arr[20] = { 0 };
      	FILE* pf = fopen("test.dat", "r"); 打开一个文件
      	if (pf == NULL)
      	{
      		perror("fopen");
      		return 0;
      	}
      	//写文件
      	fgets(arr, 20, pf);  //fgets函数的使用
      	printf("%s", arr);
      	//关闭文件
      	fclose(pf);
      	pf = NULL;
      	return 0;
      }//编译结果:我爱美女  在test.dat文件里面是 :我爱美女
      

文件的随机读写

  • 移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写
  • 实现随机读写的关键是要按照要求移动位置指针,这称为文件的定位

实现文件随机读写的函数

  • fseek函数:控制当前文件指针的移动

    调用格式:int fseek ( FILE * stream, long offset, int origin );

    返回值:操作成功,返回0;操作失败,返回非0值

    对于起始点:

    起始点名称代码
    文件开始SEEK_SET0
    文件当前位置SEEK_CUR1
    文件末尾SEEK_END2

    若以文件开始为基准,偏移量只能是正值,若以文件末尾为基准,偏移量只能是负值,若以文件当前位置(即文件当前指针)为基准,偏移量可以是正值,也可以是负值。

    例:

    #include <stdio.h>
    int main()
    {
    	//打开文件
    	FILE* pf = fopen("data.dat", "wb");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 0;
    	}
    	//读写文件
    	fputs("This is an apple.", pf);   //data.dat文件内容是:This is an apple 
    	fseek(pf, 9, 0);
    	fputs(" sam", pf);   //此时data.dat文件内容是:This is an sample 
    	//关闭文件
    	fclose(pf);
    	pf == NULL;
    	return 0;
    }
    
  • ftell函数:返回指针相对于起始位置的偏移量

    调用格式:long int ftell ( FILE * stream );

    返回值:偏移量

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

    调用格式:void rewind ( FILE * stream );

文本文件和二进制文件

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

文件读取结束的判定

  • feof函数:判断文件是否是结束符。

    调用格式:int feof ( FILE * stream );

    返回值:如果遇到文件结束符,返回值为非0;未遇到文件结束符,返回值为0

    从键盘读入数据,按 Ctrl+Z (显示器显示^Z),即输入文件结束符。feof函数可用于二进制文件和ASCII码文件

  • 在文件读取过程中,不能用feof函数的返回值直接用来判断文件是否结束,而是应用于当文件读取结束时,判断是读取失败而结束,还是遇到文件尾结束

    • 文本文件读取是否结束,判断返回值是否为EOF(fgetc)或者NULL(fgets)
    • 二进制文件的读取结束的判断,判断返回值是否小于实际要读的个数。

版权声明:本文为m0_70582947原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。