文件操作函数

文件操作函数

1. 文件及其分类

计算机上的各种资源都是由操作系统管理和控制的,操作系统中的文件系统,是专门负责将外部存储设备中的信息组织方式进行统一管理规划,以便为程序访问数据提供统一的方式。

文件是操作系统管理数据的基本单位 ,文件一般是指存储在外部存储介质上的有名字的一系列相关数据的有序集合。它是程序对数据进行读写操作的基本对象。在 C 语言中,把输入和输出设备都看作文件。

文件一般包括三要素: 文件路径、文件名、后缀

2. 流的概念及分类

I/O 设备的多样性及复杂性,给程序设计者访问这些设备带来了很大的难度和不便。为此,ANSIC 的 I/O 系统即标准 I/O 系统,把任意输入的源端或任意输出的终端,都抽象转换成了概念上的“标准 I/O 设备”或称“标准逻辑设备”。程序绕过具体设备,直接与该“标准逻辑设备”进行交互,这样就为程序设计者提供了一个不依赖于任何具体 I/O 设备的统一操作接口,通常把抽象出来的“标准逻辑设备”或“标准文件”称作“流”。

把任意 I/O 设备,转换成逻辑意义上的“标准 I/O 设备”或“标准文件”的过程,并不需要程序设计者感知和处理,是由标准 I/O 系统自动转换完成的。故从这个意义上,可以认为任意输入的源端和任意输出的终端均对应一个“流”。

流按方向分为: 输入流和输出流 。从文件获取数据的流称为输入流,向文件输出数据称为输出流。

例如,从键盘输入数据然后把该数据输出到屏幕上的过程,相当于从一个文件输入流(与键盘相关)中输入(读取)数据,然后通过另外一个文件输出流(与显示器相关)把获取的数据输出(写入)到文件(显示器)上。

流按数据形式分为:文本流和二进制流。 文本流 是 ASCII 码 字符序列 ,而 二进制流 字节序列

3. 文本文件与二进制文件

根据文件中数据的组织形式的不同,可以把文件分为:文本文件和二进制文件。

  • 文本文件:把要存储的数据当成一系列字符组成,把每个字符的 ASCII 码值存入文件中。每个 ASCII 码值占一个字节,每个字节表示一个字符。故文本文件也称作字符文件或 ASCII 文件,是字符序列文件。
  • 二进制文件:把数据对应的二进制形式存储到文件中,是字节序列文件。

4. 文件函数

4.1 fopen

4.1.1 描述

使用给定的模式 mode 打开 filename 所指向的文件。

4.1.2声明

FILE *fopen(const char *filename, const char *mode)

4.1.3参数

  • filename – 这是 C 字符串,包含了要打开的文件名称。
  • mode – 这是 C 字符串,包含了文件访问模式,模式如下:
模式 含 义 说 明
r 只读 文件 必须存在 ,否则打开失败
w 只写 若文件存在,则 清除原文件内容 后写入;否则,新建文件后写入
a 追加只写 文件存在 ,则位置指针移到文件末尾,在文件尾部 追加写入 ,故该方式不删除原文件数据;若文件 不存在 ,则 打开失败
r+ 读写 文件必须存在。在只读 r 的基础上加 ‘+’ 表示增加可写的功能。下同
w+ 读写 新建一个文件,先向该文件中写人数据,然后可从该文件中读取数据
a+ 读写 在” a”模式的基础上,增加可读功能
rb 二进制读 功能同模式”r”,区别:b表示以二进制模式打开。下同
wb 二进制写 功能同模式“w”。二进制模式
ab 二进制追加 功能同模式”a”。二进制模式
rb+ 二进制读写 功能同模式"r+”。二进制模式
wb+ 二进制读写 功能同模式”w+”。二进制模式
ab+ 二进制读写 功能同模式”a+”。二进制模式

调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为 "t" )。

读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:

  • 将读写方式放在读写权限的末尾:“rb”、“wt”、“ab”、“r+b”、“w+t”、“a+t”
  • 将读写方式放在读写权限的中间:“rb+”、“wt+”、“ab+”

整体来说,文件打开方式由 r、w、a、t、b、+ 六个字符拼成,各字符的含义是:

  • r(read):读
  • w(write):写
  • a(append):追加
  • t(text):文本文件
  • b(binary):二进制文件
  • +:读和写

4.1.4 返回值

该函数返回一个 FILE 指针。否则返回 NULL,且设置全局变量 errno 来标识错误。

4.2 fclose

4.2.1 描述

C 库函数 int fclose(FILE *stream) 关闭流 stream。刷新所有的缓冲区。

4.2.2 声明

int fclose(FILE *stream)

4.2.3 参数

  • stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了要被关闭的流。

4.2.4 返回值

如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。

4.2.5 实例

如果文件存在,以只写形式打开,若文件存在,则 清除原文件内容 后写入;否则,新建文件后写入

int main()
{
    //以写的形式打开text1.txt
    FILE *fp = fopen("text1.txt", "w");
    //如果打开失败,报错
    if (fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    //关闭流
    fclose(fp);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LM3F1NoC-1626762202806)(https://i.loli.net/2021/05/25/swpIrN5tqk2DvUy.png)]

如果文件不存在,但还以只读形式打开,将会报错

int main()
{
    //以写的形式打开text1.txt
    FILE *fp = fopen("text.txt", "r");
    //如果打开失败,报错
    if (fp == NULL)
    {
        perror("fopen");
        return -1;
    }
    //关闭流
    fclose(fp);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R9JOC8ep-1626762202808)(https://i.loli.net/2021/05/25/k2LBgFRpyzh8Vuf.png)]

按字符输入输出

4.3 fputc

4.3.1 描述

向stream指针所指向的文件中输出字符 char,输出成功,返回该字符;输出失败,则返回 EOF(-1)。

4.3.2 声明

int fputc(int char, FILE *stream)

4.3.3 参数

  • char – 这是要被写入的字符。该字符以其对应的 int 值进行传递。
  • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符的流。

4.3.4返回值

如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。

4.4 fgetc

4.4.1 描述

从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。

4.4.2 声明

int fgetc(FILE *stream)

4.4.3 参数

  • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要在上面执行操作的流。

4.4.4 返回值

该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。

4.4.5 实例(fgetc与fputc)

int main()
{
    //以只读模式打开文件
    FILE *fo = fopen("text1.txt", "w");
    //如果打开失败报错
    if (fo == NULL)
    {
        perror("fopen");
        return -1;
    }
    //将a写入文件
    int i = fputc('a', fo);
    //写入失败报错,并且关闭打开的文件
    if (i == EOF)
    {
        perror("fputc");
        fclose(fo);
        return -1;
    }
    //写入成功关闭文件
    fclose(fo);
    //以只读模式打开文件
    FILE *fo1 = fopen("text1.txt", "r");
    //从fo指向的文件中读取一个字节
    int fg = fgetc(fo1);
    //读取失败报错并关闭已经打开的文件
    if (fg == EOF)
    {
        perror("fgetc");
        fclose(fo1);
        return -1;
    }
    //打印去求到的字符
    printf("%c", fg);
    //读取成功关闭文件
    fclose(fo1);
    return 0;
}

按字符串输入输出

4.5 fgets

4.5.1 描述

从 str所指向的文件内,读取若干字符(一行字符串),并在其后自动添加字符串结束标志 ‘\0’ 后,存入str所指的缓冲内存空间中(str可为字符数组名),直到遇到回车换行符或已读取 size-1 个字符或已读到文件结尾为止。该函数读取的字符串最大长度为 size-1。

4.5.2 声明

char *fgets(char *str, int size, FILE *stream)

4.5.3 参数

  • str – 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
  • size – 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
  • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。

4.5.4 返回值

如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。

如果发生错误,返回一个空指针。

4.5.5 注意

读取到的字符串会在末尾自动添加 ‘\0’,n 个字符也包括 ‘\0’。也就是说,实际只读取到了 n-1 个字符,如果希望读取 100 个字符,n 的值应该为 101。

4.6 fputs

4.6.1 描述

把字符串写入到指定的流 stream 中,但不包括空字符。

4.6.2 声明

int fputs(const char *str, FILE *stream)

4.6.3 参数

  • str – 这是一个数组,包含了要写入的以空字符终止的字符序列。
  • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流。

4.6.4 返回值

该函数返回一个非负值,如果发生错误则返回 EOF。

4.6.5 实例

int main()
{
    //以只读模式打开文件
    FILE *fo = fopen("text1.txt", "w+");
    //如果打开失败报错
    if (fo == NULL)
    {
        perror("fopen");
        return -1;
    }
    //将hello写入文件
    int i = fputs("hello", fo);
    //写入失败报错,并且关闭打开的文件
    if (i == EOF)
    {
        perror("fputs");
        fclose(fo);
        return -1;
    }
    //将文件指针重置到文件开头
    rewind(fo);
    //创建字符缓冲空间数组
    char input[10];
    //从fo指向的文件中读取一个字节
    char *fg = fgets(input, 10, fo);
    //读取失败报错并关闭已经打开的文件
    if (fg == NULL)
    {
        perror("fgets");
        fclose(fo);
        return -1;
    }
    //打印去求到的字符
    printf("%s", fg);
    //读取成功关闭文件
    fclose(fo);
    return 0;
}

数据写入完毕后,位置指针在文件的末尾,要想读取数据,必须将文件指针移动到文件开头,这就是 rewind(fp); 的作用。

4.6.7 复制实现

int main()
{
    //打开要被复制的文件
    FILE *fo = fopen("text1.txt", "r");
    if (fo == NULL)
    {
        perror("text1 fopen");
        return -1;
    }
    //打开(或创建)要目标文件
    FILE *fo1 = fopen("text2.txt", "w");
    if (fo1 == NULL)
    {
        perror("text2 fopen");
        return -1;
    }
    //逐字符读取
    int input = 0;
    while ((input = fgetc(fo)) != EOF)
    {
        fputc(input, fo1);
    }
    //关闭打开的文件
    fclose(fo);
    fclose(fo1);
    return 0;
}

格式化输入输出

4.7 fprintf

4.7.1 描述

把输出表列中的数据按照指定的格式输出到文件中。

4.7.2 声明

int fprintf(FILE *stream, const char *format, ...)

4.7.3 参数

  • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
  • format – 这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。

4.7.4 返回值

如果成功,则返回写入的字符总数,否则返回一个负数。

4.8 fscanf

4.8.1 描述

从一个文件流中执行格式化输入,当遇到空格或者换行时结束。注意该函数遇到空格时也结束,这是其与 fgets 的区别,fgets 遇到空格不结束。

4.8.2 声明

int fscanf(FILE *stream, const char *format, ...)

4.8.3 参数

  • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
  • format – 这是 C 字符串,包含了以下各项中的一个或多个: 空格字符、非空格字符 format 说明符

4.8.4 返回值

如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF。

4.8.5 实例

typedef struct stu
{
    int id;
    char name[10];
} stuInfo;


int main()
{
    stuInfo s = {1, "张三"};
    stuInfo s2;
    FILE *fo = fopen("text1.txt", "w");
    if (fo == NULL)
    {
        perror("fopen");
        return -1;
    }
    fprintf(fo, "%d %s", s.id, s.name);
    fclose(fo);
    FILE *fo1 = fopen("text1.txt", "r");
    if (fo1 == NULL)
    {
        perror("fopen1");
        return -1;
    }
    fscanf(fo1, "%d %s", &(s2.id), s2.name);
    printf("%d %s", s2.id, s2.name);
    fclose(fo1);
    return 0;
}