跳过正文
P11 文件
  1. S/
  2. C/C++/
  3. C/
  4. C语言程序设计/

P11 文件

Li
作者
Li
往前走,别回头!
目录
C 语言程序设计笔记 - 这篇文章属于一个选集。

知识点
#

12.1 文件简介及文本、二进制文件区别
#

  • @ 文件基础图
    • ![[白板/P34 文件基础.canvas|P34 文件基础]]

12.1.1 文件简介
#

[!tip] 文件

  • @ 文件: 保存在硬盘、U盘等存储介质上的数据
  • 操作系统以文件为单位对数据进行管理
  • 文件可以看成字符序列

12.1.2 文本文件和二进制文件区别
#

[!tip] 文本文件 vs 二进制文件

  • @ 文本文件(ASCII 文件): 每个字节存放一个 ASCII 码,代表一个字符,人类能看懂
  • @ 二进制文件: 把内存中的数据按存储形式原样输出到磁盘,人类可能看不懂
对比项文本文件二进制文件
存储方式每个 ASCII 码占 1 字节按内存存储形式原样保存
可读性人类能看懂人类可能看不懂(乱码)
存储空间较大(如 10000 占 5 字节)较小(如 10000 占 2 字节)
转换开销需要二进制↔ASCII 转换无需转换
  • ! 对于计算机来讲,==没有文本文件和二进制文件之分==,都是二进制数据
  • ! 文本文件可以理解为"人类能看懂的二进制文件"

大端存储与小端存储
#

[!tip] 字节序

  • @ 小端存储: 低字节存低地址,高字节存高地址
  • @ 大端存储: 低字节存高地址,高字节存低地址
  • 与 CPU 体系结构有关
// 10000 = 0x2710
// 小端存储: 10 27(低地址→高地址)
// 大端存储: 27 10(低地址→高地址)

12.2 文件的打开、关闭、读写与实战操练
#

  • @ 文件操作图
    • ![[白板/P35 文件操作.canvas|P35 文件操作]]

12.2.1 文件的打开
#

[!tip] fopen 函数

  • 一般形式: FILE *fp = fopen(文件名, 文件使用方式);
  • FILE 是一个结构体,fp 是指向 FILE 的指针变量(文件指针)
  • ! 打开失败时返回 NULL
FILE *fp;
fp = fopen("A1", "r");  // 以只读方式打开 A1 文件
if(fp == NULL) {
    // 文件打开失败的处理代码
}

常用文件使用方式
#

文件使用方式含义
"r"只读,打开文本文件,文件必须存在
"w"只写,打开文本文件,存在则覆盖,不存在则创建
"a"追加,向文本文件末尾增加数据
"rb"只读,打开二进制文件
"wb"只写,打开二进制文件
"ab"追加,向二进制文件末尾增加数据
"r+"读写,打开文本文件
"w+"读写,建立新的文本文件

规律记忆
#

  • % 有 w → 往文件中写入
  • % 有 r → 从文件中读出
  • % 有 b → 二进制文件
  • % 有 a → 追加内容到文件末尾
  • % 有 + → 既能读又能写

文件位置指针
#

  • 每个打开的文件都有一个当前位置指针
  • 代表当前从文件的哪个位置开始读/写数据
  • 每读出 1 字节数据,位置指针自动后移 1 字节

12.2.2 文件的关闭
#

[!tip] fclose 函数

  • 一般形式: fclose(文件指针);
  • ! 必须关闭文件,原因:
    1. 释放文件占用的内存资源
    2. 将缓冲区中的数据写入磁盘,避免数据丢失
if(fp != NULL)
    fclose(fp);
  • ! 关闭后 fp 不能再使用
  • fclose() 返回 0 表示成功,非 0 表示失败

12.2.3 文件的读写
#

fputc — 写一个字符
#

[!tip] fputc

  • 调用形式: fputc(ch, fp);
  • 将字符 ch 写到 fp 所指向的文件中
  • 成功返回字符的 ASCII 码,失败返回 EOF
FILE *fp;
fp = fopen("FTest.txt", "w");
if(fp == NULL) {
    printf("文件打开失败");
} else {
    char reco = fputc('a', fp);
    if(reco == EOF) {
        // 写失败的处理代码
    }
    fputc('d', fp);
    fputc('e', fp);
    fclose(fp);
}

fgetc — 读一个字符
#

[!tip] fgetc

  • 调用形式: char reco = fgetc(fp);
  • fp 所指向的文件中读入一个字符
  • 成功返回读入的字符,失败或到文件末尾返回 EOF
FILE *fp;
fp = fopen("FTest.txt", "r");
if(fp == NULL) {
    printf("文件打开失败");
} else {
    char reco = fgetc(fp);
    while(reco != EOF) {
        putchar(reco);       // 在屏幕上输出
        reco = fgetc(fp);    // 继续读
    }
    fclose(fp);
}

fgets — 读一行字符串
#

[!tip] fgets

  • 调用形式: fgets(str, n, fp);
  • fp 指向的文件中读入 n-1 个字符到 str
  • 遇到 \nEOF 提前结束
  • ! 自动在末尾加 \0

fputs — 写一行字符串
#

[!tip] fputs

  • 调用形式: fputs(str, fp);
  • 将字符串 str 写到 fp 指向的文件中

12.2.4 文件读写实战操练
#

// 读取配置文件 config.txt
FILE *fp;
fp = fopen("config.txt", "r");
if(fp == NULL) {
    printf("文件打开失败\n");
} else {
    char LineBuf[1024];
    while(fgets(LineBuf, sizeof(LineBuf), fp) != NULL) {
        // 处理每一行数据
        printf("%s", LineBuf);
    }
    fclose(fp);
}

12.3 将结构体数据写入二进制文件和从二进制文件读出
#

  • @ 结构体文件操作图
    • ![[白板/P36 结构体文件操作.canvas|P36 结构体文件操作]]

12.3.1 将结构体数据写入二进制文件
#

[!tip] fwrite 函数

  • 一般形式: fwrite(buffer, size, count, fp);
  • buffer: 要写入数据的地址
  • size: 要写入的字节数
  • count: 要写入多少个 size 字节的数据项
  • fp: 文件指针
  • 返回值: 失败返回 0,成功返回 count
#pragma pack(1)  // 1字节对齐,避免跨平台问题
struct stu {
    char name[30];
    int age;
    double score;
};
#pragma pack()   // 恢复默认对齐

struct stu student[2] = {
    {"张三", 18, 80.5},
    {"李四", 20, 90.5}
};

FILE *fp;
fp = fopen("structfile.bin", "wb");  // 二进制写方式
if(fp != NULL) {
    fwrite(student, sizeof(struct stu), 2, fp);
    fclose(fp);
}

结构体写入的坑点
#

  • ! 结构体成员中不要出现指针类型,指针地址在程序再次运行时会失效
  • ! 结构体字节对齐问题:不同编译器/平台对齐方式不同
    • Windows VS 可能按 8 字节对齐
    • Linux GCC 可能按 4 字节对齐
    • 解决方案: #pragma pack(1) 设置 1 字节对齐

12.3.2 从二进制文件中读出结构体数据
#

[!tip] fread 函数

  • 一般形式: fread(buffer, size, count, fp);
  • buffer: 读出数据存放的地址
  • size: 要读出的字节数
  • count: 要读出多少个 size 字节的数据项
  • fp: 文件指针
  • 返回值: 失败返回 0,成功返回 count
FILE *fp;
fp = fopen("structfile.bin", "rb");  // 二进制读方式
if(fp != NULL) {
    struct stu studentnew[2];
    int retresult = fread(studentnew, sizeof(struct stu), 2, fp);
    fclose(fp);
}

12.3.3 文件使用方式中 "r""rb""w""wb" 的区别
#

[!warning] b 标记的重要性

  • ! 写和读是配套操作,写用了 "b" 标记,读也必须用 "b" 标记
  • ! 不带 "b" 标记读文件时,\r 可能被丢弃
  • ! 不带 "b" 标记写文件时,\n 可能被自动替换为 \r\n(Windows 平台)
  • ! 带 "b" 标记会原封不动地读/写,不做任何转换
操作不带 bb
可能缺失 \r原封不动读出
\n 可能变成 \r\n原封不动写入
  • ! 结论: 希望原封不动地读写文件内容,请在 fopen 中使用 "b" 标记
Reply by Email
C 语言程序设计笔记 - 这篇文章属于一个选集。

相关文章