「通俗易懂」C语言不得不提、不得不晓的文件操作!

文件操作是C语言的基础,但凡做项目都需要涉及到文件操作,因为我们需要将信息保存,否则一个进程结束后数据都丢失了。

虽然目前文件操作基本上都是使用数据库(毕竟功能强大),但是基本的文件操作还是有意义的(比如课设)。

因此,本篇文章将详细地讲解在C语言中的文件操作,相信会有很大的帮助。
「通俗易懂」C语言不得不提、不得不晓的文件操作!

目录

一、文件定义

文件:一般指存储在外部介质(如磁盘磁带)上数据的集合。

操作系统的角度

操作系统是以文件为单位对系统进行管理的。linux下万物皆文件。

从操作系统的角度看,每一个与主机相连的输入输出(IO)设备看做是一个文件。

  • 输入文件:终端、键盘
  • 输出文件:显示器、打印机

文件的分类

按数据的组织形式,可将文件分为两类:

  • 文本文件(ASCII文件):每一个字节放一个ASCII代码。
  • 二进制文件:把数据按其在内存中的存储形式原样输出到磁盘中存放。

下图是整数10000在两种方式下的存储形式。在内存中,10000的二进制源码为:0010011100010000,转化为十进制 = 2^13 + 2^10 + 2^9 + 2^8 + 2^4 = 8192 + 1024 + 512 + 256 + 16 = 10000。

若是按二进制文件存储,这以0010011100010000存储;若是按文本文件存储,则将整数10000分为5个字节,每个字节存储一个ASCII代码。

「通俗易懂」C语言不得不提、不得不晓的文件操作!

ASCII文件和二进制文件的比较

  • ASCII文件便于对字符进行逐个处理,也便于输出字符。缺点:占存储空间较多,而且要花费转换时间。
  • 二进制文件可以节省外存空间和转换时间。缺点:一个字节并不对应一个字符,不能直接输出字符形式。
  • 因此,如果数据经常在外存和内存间输入输出,使用二进制保存更好。

文件处理方法的分类

  • 缓冲文件系统:系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。用缓冲文件系统
    进行的输入输出又称为高级磁盘输入输出。
  • 系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。用非缓冲文件系统进行的输入输出又称为低级输入输出系统。

「通俗易懂」C语言不得不提、不得不晓的文件操作!

二、文件操作

2.1 文件类型指针(FILE)

头文件:#include <stdio.h>

文件指针定义:

FILE *fp;

缓冲文件系统中,每个被使用的文件都要在内存中开辟一FILE类型的区,存放文件的有关信息。

文件指针指向一个结构,该结构包含以下信息:

  • 文件名
  • 文件的当前位置
  • 文件是否正在读或写
  • 是否出错
  • 是否达到文件末尾

「通俗易懂」C语言不得不提、不得不晓的文件操作!

2.2 文件函数

文件函数可以分为4类:打开和关闭文件、文件定位、文件状态、文件读写

(1)打开和关闭文件

函数名 功能
fopen() 打开文件
fclose() 关闭文件

(2)文件定位

函数名 功能
fseek() 改变文件位置指针的位置
rewind() 使文件位置指针指向文件开头
ftell() 返回文件位置指针的当前值

(3)文件状态

函数名 功能
feof() 如果到文件末尾,函数值为真
ferror() 如果对文件操作出错,函数值为真
clearerr() 是feof()和ferror()函数值置零

(4)文件读写

函数名 功能
fgetc() / getc() 从指定文件取得一个字符
fputc() / putc() 把字符输出到指定文件
fgets() 从指定文件读取字符串
fputs() 把字符串输出到指定文件
getw() 从指定文件读取一个字(int)
putw() 把一个字输出到指定文件
fread() 从指定文件读取数据项
fwrite() 把数据项写到指定文件中
fscanf() 从指定文件按格式输入数据
fprintf() 按指定格式将数据写到指定文件中

三、打开和关闭文件

3.1 fopen——打开文件

函数定义:FILE *fopen(char *pname, char *mode)

函数说明:pname是文件名,mode是打开文件的方式

调用fopen文件,需要知道文件名(文件路径),并确定使用文件的方式,然后将返回值赋给文件类型指针。

FILE *fp;
fp = fopen(文件名,使用文件方式);

文件使用方式

文本文件

文件使用方式 含义
“r” 只读,打开一个文本文件
“w” 只写,打开一个文本文件
“r+” 读写,打开一个文本文件
“w+” 读写,创建一个新的文本文件
“a” 追加,向文本文件尾增加数据
“a+” 追加,为读写打开一个文本文件

二进制文件

可以看到,二进制文件只是在文本文件的基础上加上了b

文件使用方式 含义
“rb” 只读,打开一个二进制文件
“wb” 只写,打开一个二进制文件
“rb+” 读写,打开一个二进制文件
“wb+” 读写,创建一个新的二进制文件
“ab” 追加,向二进制文件尾增加数据
“ab+” 追加,为读写打开一个二进制文件

具体案例

创建一个名为data.txt的文件,读写方式打开。

FILE *fp;
fp = fopen("data.txt", "w+");//"w+"是以读写的方式打开一个文件

然而,实际工程中,我们可能需要反复对一个文件打开和关闭,若是不存在则创建,用w+;存在则直接打开,用r+。所以,可以封装一个函数:打开一个文件,如果不存在则创建,存在则直接打开。

/*
输入参数:文件路径
返回值:文件类型指针
功能:打开一个文件,如果不存在则创建
*/
FILE* fileOpen(const char *pName)
{
    FILE* fp;
    fp = fopen(pName, "r+");//首先,以读写的方式打开一个文件
    if (fp == NULL) 
    {
        fp = fopen(pName, "w+");//如果文件不存在,创建一个文件
    }

    return fp;
}

3.2 fclose(关闭文件)

函数定义:int fclose(FILE *fp);

函数说明:fp是一个已经打开文件的文件指针

返回值:关闭成功返回值为0;否则返回EOF(-1)

fclose(fp);

四、文件定位

  • FILE结构提供了一个指针,用于跟踪发生I/O操作位置。

  • 每当从流中读取或写入一个字符,当前活动指针(即curp)就会向前移动。

4.1 ftell——当前指针位置

long int ftell(FILE *fp);

返回值:成功时返回文件指针位置,否则返回-1L。

4.2 rewind——指向开头

void rewind(FILE * fp) ;

功能说明:将文件位置指示器置于文件开头,相当于reset(重置)文件指针。

4.3 fseek——重定位指针

修改文件指针的位置:从origin的位置开始往后偏移offset个字节。

「通俗易懂」C语言不得不提、不得不晓的文件操作!

其中,参数origin有三个值:

origin 文件文字
SEEK_SET 或 0 文件开头
SEEK_CUR 或 1 当前文件指针段的位置
SEEK_END 或 2 文件末尾

(1)偏移到文件末尾

fseek(fp, 0, SEEK_END);//文件指针偏移到文件末尾

(2)偏移到文件开头

fseek(fp, 0, SEEK_SET);

(3)从当前位置往后偏移4个字节

fseek(fp, 4, SEEK_CUR);

(4)计算文件字节数

fseek(fp, 0, SEEK_END);
length = ftell(fp);//length即为当前文件指针离文件开头的字节数

五、文件状态

5.1 feof——文件末尾判断

int feof(FILE *fp);

返回值:

  • 文件末尾:非0;
  • 非文件末尾:0。

feof()是ANSI C提供的标准函数,用于识别二进制文件是否结束,当然也可以用于文本文件。因为在文本文件中,用EOF(-1)作为文件的结束符,ASCII码不会出现**-1**所以对文本来说可行。但是,在二进制文件中-1往往可能是一个有意义的数据,所以在二进制文件使用feof()。

5.2 ferror——文件错误判断

int ferror(FILE * fp);

返回值:

  • 出错:非0;
  • 未出错:0。

在执行fopen()函数时,ferror()函数的初始值自动设置为0,表示文件正确。

在调用一个输入输出函数时,需要立即检查ferror()函数的值,否则信息丢失。

5.3 clearerr——清除标志

void clearerr(FILE *fp);

函数作用:使文件错误标志(ferror)和文件结束标志(feof)置位0。

只要出现错误标志,该标志就会一直保留,直到以下情况清除:

  • 调用clearerr()函数;
  • 调用rewind()函数;
  • 调用任意一个输入输出函数。

六、文件读写

6.1 fgetc——从文件读取一个字符

int fgetc(FILE *fp);

函数说明:从文件读取一个字符,该字符在返回值中返回。

返回值:

  • 读取成功,返回该字符;
  • 读取失败(文件末尾),返回EOF。

读取文本文件的所有内容

char ch = fgetc(fp);while (ch != EOF){    putchar(ch);    ch = fgetc(fp);}

6.2 fputc——将字符输出到文件

int fputc(int ch, FILE *fp);

函数说明:将字符ch写到文件中。

返回值:

  • 写入成功,返回写入的字符;
  • 写入失败,返回EOF。

往文件写入一个字符’1’,ASCII码为49,所以在中显示为1。

fputc('1', fp);

6.3 fgets——从文件读取一个字符串

char* fgets(char* str, int n, FILE* fp);

函数说明:从fp指向的文件读取n-1个字符,将它们存放在str对应的字符数组区域,加上’\0’。

返回值:

  • 正常返回:字符串str的首地址
  • 异常返回:返回一个NULL值,这时候用feof()和ferror()判断是文件末尾还是发生错误。

读取fp文件中9个字符到str。

char *str;
fgets(str, 10, fp);

如果,读取的字符数大于文件中的字符数,并不会报错,而是根据文件的内容输出到str。

6.4 fputs——将字符串写入文件

int fputs(char *str, FILE *fp);

函数说明:把str字符串写入到fp所指的文件中去。

返回值:

  • 输入成功,返回值为0;
  • 输入失败,返回值为EOF。

在文件末尾输入字符串"china"

fseek(fp, 0, SEEK_END);
int ans = fputs("china", fp);

6.5 fread——无格式读函数

「通俗易懂」C语言不得不提、不得不晓的文件操作!

功能描述:fread是无格式读函数,用于向文件读出整块的数据。经常用fwrite函数写入,读取时用fread函数。

返回值:成功时返回读出的单元数,否则返回0。

6.6 fwrite——无格式写函数

「通俗易懂」C语言不得不提、不得不晓的文件操作!

功能说明:fwrite是无格式写函数,用于向文件写入整块的数据。最有价值的一个应用就是读写用户定义的数据类型,尤其是结构。

返回值:成功时返回写入的单元数,否则返回0

实际案例

fwrite将结构体写入文件,而后用fread可以将结构体读出文件。

//结构定义
typedef struct user
{
    int id;
    char name[10];
}User;

int main()
{
	FILE *fp;
    fp = fileOpen("user5.txt");
    User user1[3] = {{1001, "aaa"}, {1002, "bbb"}, {1003, "ccc"}};
    fseek(fp, 0, SEEK_END);
    fwrite(&user1, sizeof(User), 3, fp);//写入3个User结构体
    User user2[3];
    rewind(fp);
    int size = fread(&user2, sizeof(User), 3, fp);//读出三个User结构体
    for (int i = 0; i < size; i++)
    {
        printf("%d %s\n", user2[i].id, user2[i].name);
    }
    fclose(fp);
    system("pause");
    return 0;
}

6.7 fscanf——从文件格式化输入

fscanf(FILE *fp, char *format, arg_list);

  • fp:文件指针
  • format:将arg_list的变量按format的格式从fp指定的文件中输入。若fp为stdin,就是从键盘等设备输入。
  • arg_list:写入文件的变量列表。

所以,fscanf和scanf无太大区别,参数格式参考scanf。

读取文件fp的字符串,直到遇到\n

char *str;
fscanf(fp, "%s", str);
printf("%s\n", str);

6.8 fprintf——从文件格式化输出

fprintf(FILE *fp, char *format, arg_list);

  • fp:文件指针
  • format:将arg_list的变量按format的格式输出到fp指定的文件中。若fp为stdout,就是输出到屏幕文件,即在屏幕中显示。
  • arg_list:写入文件的变量列表。

将一字符串输出至文件:

int grade = 80;
fprintf(fp, "grade = %d\n", grade);

而后用fscanf读取:

char *str;
int num;
fscanf(fp, "%s = %d", str, &num);
printf("%s\n", str);
printf("%d\n", num);

由此可见,fprintf写入的字符串grade是有字符串结束符'\0'的。

七、总结

最后,总结一下:fopen、fclose是基础操作,fseek移动指针好用,ftell可算长度以便偏移,feof文件末尾,fread、fwrite常用。


好了,本篇文章结束,感谢观看!

觉得不错的同学,一键三连
上一篇:C语言程序设计大作业 酒店管理系统 课程设计


下一篇:云计算系统中对开发者的API设计问题