欢迎光临
我们一直在努力

新手入门指南:认识 C 语言文件操作(下)

1. ⽂件的随机读写详细解释

在C语言中,文件操作是数据处理的核心部分。随机读写允许程序直接访问文件的任意位置,提高效率和灵活性。本部分将详细解释用户提供的函数和概念,包括fseek、ftell、rewind、fflush,以及文件缓冲区和更新文件行为。

1.1 fseek函数详解

fseek函数用于移动文件指针的位置,实现随机访问。其原型为:

int fseek(FILE *stream, long int offset, int origin);

  • 功能:根据文件指针的当前位置和偏移量定位指针(即文件内容的“光标”)。例如,可以从文件开头、当前位置或末尾移动。
  • 参数:
    • stream:指向FILE结构体的指针,指定文件流。
    • offset:偏移量,单位为字节。正数向文件末尾移动,负数向文件开头移动,0表示不移动。
    • origin:基准位置,有三个取值:
      • SEEK_SET:文件开始位置。
      • SEEK_CUR:文件指针当前位置。
      • SEEK_END:文件末尾位置。
  • 返回值:
    • 成功:返回0。
    • 失败:返回非零值(如-1),通常因无效偏移或文件流错误。
  • 注意事项:
    • 二进制 vs 文本文件:在二进制文件中,fseek行为直观,可精确移动到任何字节位置。在文本文件中,由于换行符转换(如Windows的"\\r\\n" vs Linux的"\\n"),行为可能不确定。唯一安全用法是:
      • fseek(stream, 0, SEEK_SET):移动到文件开头。
      • 配合ftell使用:先获取位置,再恢复。
    • 常见错误:移动超出文件范围(如负偏移)会失败。

代码示例分析:

#include <stdio.h>
int main() {
FILE* ps = fopen("test.txt", "r");
if (ps == NULL) {
perror("fopen");
return 1;
}
int c = fgetc(ps); // 读取第一个字符
fputc(c, stdout); // 输出到屏幕,例如'a'

// 定位文件指针:三种方式示例
// fseek(ps, 5, SEEK_SET); // 从文件开头移动5字节
// fseek(ps, 4, SEEK_CUR); // 从当前位置移动4字节
fseek(ps, 9, SEEK_END); // 从文件末尾移动-9字节(向开头方向)

c = fgetc(ps); // 读取新位置的字符
fputc(c, stdout); // 输出,例如如果文件内容为"abcdefghij",移动到-9位置可能读'b'

fclose(ps);
ps = NULL;
return 0;
}

  • 逐步解释:
  • 打开文件test.txt用于读取。
  • 读取并输出第一个字符(如’a’)。
  • 使用fseek移动指针:示例中从末尾移动-9字节,假设文件有10字节(如"abcdefghij"),则移动到第1字节(索引从0开始)。
  • 读取并输出新位置的字符(如’b’)。
  • 重点:移动后读取字符,演示随机访问。但需注意文本文件的不可靠性。

1.2 ftell函数详解

ftell函数用于获取当前文件指针位置。其原型为:

long int ftell(FILE *stream);

  • 功能:返回文件指针相对于文件开头的偏移量(字节数)。
  • 参数:stream,指向已打开文件的指针。
  • 返回值:
    • 成功:返回偏移量,类型为long int。
    • 失败:返回-1L(如文件流无效)。
  • 注意事项:
    • 二进制 vs 文本文件:在二进制文件中,返回值是精确字节数。在文本文件中,返回值可能不等于字符数(因换行符处理),但通常可安全与fseek配合使用。
    • 使用场景:记录位置以便恢复,如编辑文件时。

代码示例分析:

int main() {
FILE* ps = fopen("test.txt", "r");
if (ps == NULL) {
perror("fopen");
return 1;
}
int c = fgetc(ps); // 读取第一个字符,如'a'
fputc(c, stdout); // 输出'a'

int pos = ftell(ps); // 记录当前位置,偏移量为1(假设文件内容为"abcdef")

fseek(ps, 4, SEEK_CUR); // 从当前位置移动4字节,如从第1字节到第5字节
c = fgetc(ps); // 读取新位置字符,如'f'
fputc(c, stdout); // 输出'f'

fseek(ps, pos, SEEK_SET); // 恢复到之前记录的位置(第1字节)
c = fgetc(ps); // 读取字符,如'b'
fputc(c, stdout); // 输出'b'

fclose(ps);
ps = NULL;
return 0;
}

  • 逐步解释:
  • 打开文件并读取第一个字符(输出’a’)。
  • ftell记录当前位置(偏移量1)。
  • fseek移动4字节后读取字符(输出’f’)。
  • 使用ftell的返回值恢复位置,读取原位置的下一个字符(输出’b’)。
  • 重点:ftell与fseek配合实现位置标记和恢复,避免手动计算偏移。

1.3 rewind函数详解

rewind函数用于重置文件指针到开头。其原型为:

void rewind(FILE *stream);

  • 功能:将文件指针移动到文件起始位置,并清除文件错误标志和结束标志。
  • 参数:stream,指向文件流的指针。
  • 注意事项:
    • 与fseek区别:fseek(stream, 0, SEEK_SET)只移动指针,不清除错误标志;rewind移动指针并清除标志。
    • 使用场景:重新读取文件或重置状态。

代码示例分析:

int main() {
FILE* ps = fopen("test.txt", "r");
if (ps == NULL) {
perror("fopen");
return 1;
}
int c = fgetc(ps); // 读取第一个字符,如'a'
fputc(c, stdout); // 输出'a'

int pos = ftell(ps); // 记录位置

fseek(ps, 4, SEEK_CUR); // 移动4字节
c = fgetc(ps); // 读取,如'f'
fputc(c, stdout); // 输出'f'

fseek(ps, pos, SEEK_SET); // 恢复位置
c = fgetc(ps); // 读取,如'b'
fputc(c, stdout); // 输出'b'

rewind(ps); // 重置指针到开头
c = fgetc(ps); // 重新读取第一个字符,如'a'
fputc(c, stdout); // 输出'a'

fclose(ps);
ps = NULL;
return 0;
}

  • 逐步解释:
  • 类似前例,读取和移动指针。
  • rewind后,指针重置到开头,读取第一个字符(输出’a’)。
  • 重点:rewind确保文件状态重置,适合重复操作。

2. ⽂件缓冲区解释

ANSI C标准采用“缓冲文件系统”:数据在内存缓冲区中暂存,减少磁盘访问次数。

  • 机制:
    • 输出:数据先写入缓冲区,满后才写入磁盘。
    • 输入:从磁盘读取数据到缓冲区,再分批送到程序。
  • 缓冲区大小:由C编译器决定,通常为512字节或更大。
  • 重要性:提高效率,但可能导致数据延迟写入(如程序崩溃时丢失数据)。

2.1 fflush函数详解

fflush函数强制刷新缓冲区。其原型为:

int fflush(FILE *stream);

  • 功能:刷新指定流的缓冲区。
    • 对输出流:立即将未写入数据写入文件。
    • 对输入流:行为非标准(可能清空缓冲区)。
    • 参数为NULL:刷新所有输出流。
  • 参数:stream,如stdout或文件指针。
  • 返回值:
    • 成功:返回0。
    • 失败:返回EOF。
  • 注意事项:
    • 仅输出流有明确行为;输入流刷新不可移植。
    • 程序正常终止(如exit或fclose)时自动刷新,但崩溃时数据可能丢失。

代码示例分析:

#include <windows.h>
int main() {
FILE* ps = fopen("test.txt", "w");
fputs("abcdef", ps); // 数据写入缓冲区,未立即写入文件

printf("睡眠10秒 – 已经写好数据,打开test.txt文件,发现文件没有内容\\n");
Sleep(10000); // 暂停10秒,模拟延迟

printf("刷新缓冲区\\n");
fflush(ps); // 强制刷新,数据写入文件
// 注意:高版本Visual Studio中fflush可能受限

printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\\n");
Sleep(10000); // 再次暂停

fclose(ps); // fclose也会自动刷新缓冲区
ps = NULL;
return 0;
}

  • 逐步解释:
  • 打开文件写入数据,但数据停留在缓冲区。
  • Sleep暂停后,文件可能无内容。
  • fflush刷新缓冲区,数据写入磁盘。
  • fclose关闭文件时也刷新缓冲区。
  • 重点:fflush确保数据及时写入,避免丢失。

3. 更新⽂件行为详解

更新模式允许读写结合,如"r+"、"w+"、"a+"。关键要点:

  • 读写切换规则:
    • 写后读:需先fflush刷新缓冲区,再用rewind或fseek重置指针。
    • 读后写:需用fseek或rewind重置指针。
  • 原因:缓冲区未刷新或指针位置错误会导致数据不一致。

以下表格比较三种模式:

模式文件不存在时文件存在时初始指针位置写入是否覆盖原有数据典型用途
"r+" 打开失败 保留内容 文件开头 是(可定位覆盖) 修改文件部分内容
"w+" 创建新文件 清空内容 文件开头 是(从头写入) 创建新文件或完全重写
"a+" 创建新文件 保留内容 文件末尾 否(默认追加) 追加数据,如日志记录

关键要点:

  • "r+":适合就地修改,如覆盖部分数据。
  • "w+":清空文件后从头读写。
  • "a+":只追加,不覆盖。

综合代码示例分析:

int main() {
FILE* ps = fopen("test.txt", "w+"); // 以w+模式打开
if (ps == NULL) {
perror("fopen for w+");
return 1;
}

fputs("abcdefghi", ps); // 写入字符串到缓冲区

fflush(ps); // 刷新缓冲区,确保数据写入文件

fseek(ps, 1, SEEK_SET); // 移动指针到第1字节(如指向'b')
int ch = fgetc(ps); // 读取字符,如'b'
printf("%c\\n", ch); // 输出'b'

fseek(ps, 1, SEEK_CUR); // 从当前位置后退1字节(因读取后指针在'c')
fputs("hello", ps); // 从当前位置写入"hello",覆盖部分数据

// 结果:原"abcdefghi"变为"ahelloghi"
fclose(ps);
ps = NULL;
return 0;
}

  • 逐步解释:
  • w+模式打开文件:如果存在,清空内容;写入"abcdefghi"。
  • fflush确保数据写入磁盘。
  • fseek移动指针到位置1(索引0为’a’,1为’b’)。
  • 读取并输出'b'。
  • fseek后退1字节(从’c’回到’b’),写入"hello",覆盖从'b'开始的数据。
  • 最终文件内容变为"ahelloghi"。
  • 重点:演示读写切换:写后刷新和移动指针,避免错误。

总结

文件随机读写是C语言文件操作的核心,通过fseek、ftell、rewind实现指针定位,fflush管理缓冲区。关键点:

  • 区分二进制和文本文件行为。
  • 读写切换时,务必刷新缓冲区并重置指针。
  • 选择合适文件模式(如"r+"用于修改)。
  • 代码示例展示了实际用法,强调错误处理和效率。
赞(0)
未经允许不得转载:171主机测评 » 新手入门指南:认识 C 语言文件操作(下)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址