2. 文本文件的读写操作

fopen 函数打开文件时,第二个参数 mode(打开模式)中含有 t 标志或 没有 b 标志时则为以文本方式打开文件。当以文本文件打开文件时,建议使用文本文件读写相关的函数进行操作。

文本文件的写操作

文本文件写入操作常用的函数有 fputcfputsfprintf

文本文件写入相关的函数

函数
说明
int fputc(int c, FILE *stream);
将一个字符串 c 转为无符号unsigned char 后写入文件 stream
int fputs(const char *str, FILE *stream);
一个字符串 str 写入文件 stream,不写入字符串的尾零 \0
int fprintf(FILE *stream, const char *format, ...);
使用 printf 函数的方式将格式化后的数据写入文件 stream

示例:

// filename: text_write.c
#include <stdio.h>

int main(int argc, char * argv[]) {
    FILE * pf = NULL;  // 用于保存已经打开的文件

    pf = fopen("myinfo.csv", "w");
    if (NULL == pf) {
        perror("打开文件 myinfo.csv");
        return -1;
    }

    // 以下写入一行 "1,weimingze,35\r\n"。
    fputc('1', pf);  //  写入一个 "1"
    fputc(',', pf);  // 写入一个 逗号
    fputs("weimingze,35\r\n", pf);  // 写入 14 个字节
    // 以下写入一行 "2,laowei,18\r\n"。
    fprintf(pf, "%d,%s,%d\r\n", 2, "laowei", 18);

    // 关闭文件
    fclose(pf);

    return 0;
}

编译和运行结果如下:

weimingze@mzstudio:~$ ls
text_write.c
weimingze@mzstudio:~$ gcc -o text_write text_write.c
weimingze@mzstudio:~$ ./text_write
weimingze@mzstudio:~$ ls
myinfo.csv  text_write  text_write.c
weimingze@mzstudio:~$ cat myinfo.csv
1,weimingze,35
2,laowei,18

说明:

可见上述程序生成的 myinfo.csv 文件的内容是

1,weimingze,35
2,laowei,18

Linux/Mac OS 终端中使用 cat myinfo.csv 命令是显示文件 myinfo.csv 文件的内容。此时的 mycsv 文件是标准的 csv 格式的文件,这个文件可以使用电子表格软件(如:wps)打开。打开后的效果如下:

使用 wps 打开 myinfo.csv 文件的效果

文本文件的读操作

文本文件读取操作常用的函数有 fgetcfgetsfscanf

文本文件读取相关的函数

函数
说明
int fgetc(FILE *stream);
从文件流(stream)中读取下一个字符,成功则返回 unsigned char 转为 int 的值,失败则返回 EOF(值为-1)
char *fgets(char *s, int size, FILE *stream);
从文件流(stream)中读取最多 size -1 个字符形成字符串放入 s 指向的内存缓冲区中,读取到文件结束(EOF)或换行符则结束读取。在缓冲区末尾添加尾零 '\0'
int fscanf(FILE *stream, const char *format, ...);
从文件流 stream 中使用 scanf 函数的方式读取数据到相应的变量中。成功返回读取的数据的个数,失败返回 EOF。

示例:

将上述 myinfo.csv 文件的内容读取出来,将其打印在终端中。

// filename: text_read.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    FILE * pf = NULL;  // 用于保存已经打开的文件
    int number;  // 用于保存编号
    char name[100];  // 用于保存性命
    int age;  // 用于保存年龄。

    pf = fopen("myinfo.csv", "r");
    if (NULL == pf) {
        perror("打开文件 myinfo.csv");
        return -1;
    }
    // 循环 读取,直至失败时才退出循环
    while (1) {
        // 读取一行数据并打印,数据的格式为: "1,weimingze,35\r\n"。
        int temp; // 用来保存临时的数据
        char buf[120];  // 用于保存一段文件的内容。
        char * find;  // 用于记录查找字符时找到的位置。
        // 使用scanf读取前面的数字,如果返回 EOF 说明没有读取到数据
        if (EOF == fscanf(pf, "%d", &number)) {
            break;  // 没有数据了
        }
        temp = fgetc(pf); // 读取一个逗号分隔符
        if (temp != ',') {
            printf("文件格式出错!\n");
            break;
        }
        // 读取一行中剩余的部分。格式为: "weimingze,35\r\n"
        if (NULL == fgets(buf, sizeof(buf), pf)) {
            printf("文件格式有错!");
            break;
        }
        // 查找逗号的位置,用于解析出姓名。
        find = strstr(buf, ",");
        if (NULL == find) {
            printf("姓名后没有找到逗号!\n");
            break;
        }
        // 先将 find 指向的逗号该成 `\0`, 作为姓名字符串的尾零。
        *find = '\0';
        // 将buf开始,到 find 之间的字符复制到name
        strcpy(name, buf);
        find++;  // find 指向 下一个字符(年龄的开始)。
        age = atoi(find);  // 将字符串转为整数,末尾的"\r\n" 会被忽略。
        // 打印获取到的姓名等信息
        printf("编号: %d, 姓名: %s, 年龄: %d\n", number, name, age);
    }

    // 关闭文件
    fclose(pf);

    return 0;
}

编译和运行结果如下:

weimingze@mzstudio:~$ gcc -o text_read text_read.c
weimingze@mzstudio:~$ ./text_read
编号: 1, 姓名: weimingze, 年龄: 35
编号: 2, 姓名: laowei, 年龄: 18

上述程序中可以看出。当我们从文本文件中读取文件的信息时,需要手动解析文件的格式来获取每一行,每一列的数据。

练习

写两个程序

  1. 第一个程序连续输入多个学生的姓名、年龄、成绩,当输入姓名给空字符串时结束输入。将输入的内容保存到文件 student.txt 中。
  2. 第二个程序从文件 student.txt 中获取之前保存的所有学生的 姓名、年龄、成绩 等信息。然后打印到控制台终端上。

要求:使用文本文件的读写方式。自己定义文件 student.txt 的格式。