fseek中SEEK_SET的具体作用是什么?

99ANYc3cd6
预计阅读时长 18 分钟
位置: 首页 C语言 正文

核心概念

fseek 是 C 标准库中的一个函数,用于移动文件指针到指定的位置,文件指针是一个内部变量,它指向了文件中当前读/写操作将要发生的位置。

c语言fseekSEEKSET
(图片来源网络,侵删)

它的原型定义在 <stdio.h> 头文件中:

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

参数详解

  1. FILE *stream

    • 这是指向 FILE 对象的指针,也就是你要操作的文件流,这个指针通常是通过 fopen() 函数调用成功后返回的。
    • FILE *fp = fopen("my_file.txt", "r");,这里的 fp fseek 的第一个参数。
  2. long int offset

    • 这是一个偏移量,它是一个有符号的长整型,它表示从“起始点”(由第三个参数决定)移动多少个字节。
    • offset 是正数,表示向文件末尾方向移动。
    • offset 是负数,表示向文件开头方向移动。
    • offset 是 0,表示不移动,但配合不同的 origin 仍有意义。
  3. int origin

    c语言fseekSEEKSET
    (图片来源网络,侵删)
    • 这是起始点,它决定了 offset 是从哪里开始计算的,它必须是一个宏,有三个可选值:
      • SEEK_SET
      • SEEK_CUR
      • SEEK_END

SEEK_SET 及其他宏详解

这三个宏是 fseek 函数的灵魂,它们定义了计算偏移量的“零点”。

SEEK_SET

  • 含义文件开头 (Set the file position to the beginning of the file)。
  • 行为:将文件指针移动到距离文件开头 offset 个字节的位置。
  • 公式新位置 = 文件开头 + offset
  • 关键点offset 必须是一个非负数(0 或正数),你不能用 SEEK_SET 来移动到文件开头之前的位置,这是未定义行为。

示例: 假设文件 data.txt 内容为 "Hello, World!" (共13个字符)。

FILE *fp = fopen("data.txt", "r");
if (fp) {
    // 将文件指针移动到文件开头 + 7 的位置
    // 也就是指向 'W'
    fseek(fp, 7, SEEK_SET);
    // 现在可以读取 'W' 及其后面的内容
    char ch = fgetc(fp); // ch 将会是 'W'
    printf("Character at offset 7: %c\n", ch); // 输出: Character at offset 7: W
    fclose(fp);
}

SEEK_CUR

  • 含义当前位置 (Set the file position to the current position)。
  • 行为:从文件指针的当前位置开始,移动 offset 个字节。
  • 公式新位置 = 当前位置 + offset
  • 关键点offset 可以为正(向后移动)、负(向前移动)或零。

示例: 继续使用文件 "Hello, World!"

FILE *fp = fopen("data.txt", "r");
if (fp) {
    // 1. 先读取前6个字符 "Hello,"
    for (int i = 0; i < 6; i++) {
        fgetc(fp);
    }
    // 文件指针指向 ' ' (空格,第6个字符之后的位置)
    // 2. 从当前位置向前移动 2 个字节
    // 即从 ' ' 移动到 'o'
    fseek(fp, -2, SEEK_CUR);
    // 3. 读取字符,应该是 'o'
    char ch = fgetc(fp); // ch 将会是 'o'
    printf("Character 2 bytes back from current: %c\n", ch); // 输出: Character 2 bytes back from current: o
    fclose(fp);
}

SEEK_END

  • 含义文件末尾 (Set the file position to the end of the file)。
  • 行为:将文件指针移动到距离文件末尾 offset 个字节的位置。
  • 公式新位置 = 文件末尾 + offset
  • 关键点offset 通常为 0 或负数,将 offset 设为 0 是将指针移动到文件末尾的常用方法,使用正数 offset 会导致移动到文件末尾之后,这也是未定义行为。

示例: 继续使用文件 "Hello, World!"

c语言fseekSEEKSET
(图片来源网络,侵删)
FILE *fp = fopen("data.txt", "r");
if (fp) {
    // 将文件指针移动到文件末尾
    fseek(fp, 0, SEEK_END);
    // 文件指针已经指向了文件的最后一个字节之后
    // 我们可以用 ftell() 获取当前指针位置,它等于文件大小
    long file_size = ftell(fp);
    printf("File size: %ld bytes\n", file_size); // 输出: File size: 13 bytes
    // 将指针从末尾向前移动 6 个字节,指向 'W'
    fseek(fp, -6, SEEK_END);
    char ch = fgetc(fp); // ch 将会是 'W'
    printf("Character 6 bytes from the end: %c\n", ch); // 输出: Character 6 bytes from the end: W
    fclose(fp);
}

fseek 的返回值

fseek 函数执行成功时返回 0,失败时返回一个非零值(通常是 -1),在使用 fseek 后,最好检查其返回值以确保操作成功。

if (fseek(fp, 100, SEEK_SET) != 0) {
    perror("Failed to seek in file");
    // 处理错误
}

典型应用场景

  1. 获取文件大小 这是最常见的用法之一。

    long get_file_size(const char *filename) {
        FILE *fp = fopen(filename, "rb"); // "rb" 表示二进制读取,避免平台换行符问题
        if (fp == NULL) {
            return -1; // 打开失败
        }
        fseek(fp, 0, SEEK_END);
        long size = ftell(fp);
        fclose(fp);
        return size;
    }
  2. 随机访问文件 比如一个数据库文件,每条记录固定长度,你可以快速定位到某条记录。

    // 假设每条记录 100 字节,要读取第 5 条记录
    fseek(fp, (5 - 1) * 100, SEEK_SET);
    // 现在指针就在第5条记录的开头,可以读取了
  3. 重置文件指针到开头 当你读取完一个文件后,如果想从头再读一遍,而不是关闭再重新打开文件,可以使用:

    rewind(fp); // rewind(fp) 等价于 fseek(fp, 0, SEEK_SET);
    // 或者
    fseek(fp, 0, SEEK_SET);
  4. 清空输入缓冲区 在某些交互式程序中,如果用户输入了多余的数据(比如你只读一个字符,但他输入了一整行),可以用 fseek 来跳过这些未读取的字符。

    char c;
    scanf("%c", &c); // 用户输入 'a' 和回车
    // stdin 的缓冲区里还有 '\n'
    fseek(stdin, 0, SEEK_CUR); // 通常没什么用
    // 更好的方法是直接读取并丢弃剩余字符
    // while (getchar() != '\n'); // 这是一个更常见的做法

重要注意事项

  • 文本模式 vs. 二进制模式

    • 二进制模式 ("rb", "wb") 下,fseek 的行为是精确的,offset 就是字节数。
    • 文本模式 ("r", "w") 下,情况会变得复杂,因为不同平台对换行符的处理不同(Windows 是 \r\n,Linux/macOS 是 \n),在文本模式下进行非零的 fseek 操作是不推荐的,可能导致定位错误,如果需要进行精确的文件指针操作,强烈建议始终以二进制模式打开文件
  • 流的状态

    • 在调用 fseek 之前,如果上一次操作是 fwritefputc(写操作),必须先调用 fflush()fseek 来确保所有缓冲数据都已写入文件,否则 fseek 的行为是未定义的。
    • 在调用 fseek 之后,流的读写模式可能会改变(从读模式切换到写模式,反之亦然),为了安全起见,最好在 fseek 之后进行一次明确的读或写操作。
  • 错误处理

    • 永远不要忽略 fseek 的返回值,如果文件是只读的,而你试图用 SEEK_END 加上一个正数去移动,fseek 就会失败。

总结表格

宏 (Macro) 含义 (Meaning) 起始点 (Origin) offset 典型值 示例 (fseek(fp, offset, origin))
SEEK_SET 文件开头 (Beginning of file) 文件的第 0 个字节 非负数 (>= 0) fseek(fp, 10, SEEK_SET); -> 移动到第 10 个字节。
SEEK_CUR 当前位置 (Current position) 指针所在的位置 正数、负数或零 fseek(fp, 5, SEEK_CUR); -> 向后移动 5 字节。
fseek(fp, -3, SEEK_CUR); -> 向前移动 3 字节。
SEEK_END 文件末尾 (End of file) 文件最后一个字节之后 零或负数 (<= 0) fseek(fp, 0, SEEK_END); -> 移动到文件末尾。
fseek(fp, -5, SEEK_END); -> 从末尾向前移动 5 字节。
-- 展开阅读全文 --
头像
loadlibrary c语言
« 上一篇 01-02
c语言(floatfloat)
下一篇 » 01-02

相关文章

取消
微信二维码
支付宝二维码

目录[+]