【问题标题】:Trouble understanding fseek offset无法理解 fseek 偏移量
【发布时间】:2021-11-29 06:40:07
【问题描述】:

我有一个文本文件,其中每一行都是一个带有换行符的整数。我也有一个相同的 .bin 文件。

10
20
30
40
50
60
70

运行此代码...

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int input;
    FILE *infile_t = fopen("numbers.txt", "r");
    FILE *infile_b = fopen("numbers.bin", "rb");

    if (infile_t == NULL) {
            printf("Error: unable to open file %s\n", "numbers.txt");
            exit(1);
    }

    if (infile_b == NULL) {
            printf("Error: unable to open file %s\n", "numbers.bin");
            exit(1);
    }

    printf("Enter an integer index: ");
    while(scanf("%d",&input) != EOF){
        int ch;

        fseek(infile_t, (input*sizeof(int))-1, SEEK_SET);
        fscanf(infile_t, "In text file: %d\n", &ch);
        printf("In text file: %d\n", ch);

        fseek(infile_b, (input*sizeof(int))-1, SEEK_SET);
        fscanf(infile_b, "%d\n", &ch);
        printf("In binary file: %d\n", ch);

        printf("Enter an integer index: ");
    }

    fclose(infile_t);
    fclose(infile_b);

    return 0;
}

并连续输入 0、1、2、3、4,我得到输出: 10 0 40 50 0

我试图一次读取文件 4 个字节(每个 int)并打印整数。我做错了什么,如果这是不好的做法,什么会更好?

【问题讨论】:

  • "每次 4 个字节(每个 int)" - 每个 int 占用 2 个字节 + 1 或 2 个字节,用于 \n\r\n您显示的文本文件
  • numbers.bin 是什么?是您在问题中显示的文本文件吗?
  • 为什么 4input*4
  • @Jabberwocky 显然,numbers.bin 是示例文件。而如果文件是在Windows下创建的,一个换行符是两个字节长,所以每行是2个字符+2个字节换行符。
  • 我刚刚在 Ubuntu 下测试了你的程序(使用numbers.bin 中的 DOS 行结尾):它工作正常。在其他程序中,我发现FILE *fopen()-type 文件(与int open()-type 文件不同)有时会导致fseek() 出现问题。作为一种解决方法,我在fseek() 之前和之后使用了fflush()fflush(f); fseek(f,...); fflush(f);

标签: c stdio


【解决方案1】:

数字的文本表示和它们的二进制表示之间存在差异。

您的输入是一个文本文件,它是一个字符序列:

“10lf20lf30lf40lf50lf60lf70lf

它的大小为 21 字节,您可以使用文件浏览器查看。

作为表格形式的字节,它看起来像这样,假设您使用的是 ASCII 和类似 unix 的系统:

Offset Bytes Text
0 31 30 0A "10lf"
3 32 30 0A "20lf"
6 33 30 0A "30lf"
9 34 30 0A "40lf"
12 35 30 0A "50lf"
15 36 30 0A "60lf"
18 37 30 0A "70lf"

您的输入文件中没有以二进制形式存储的整数。

函数fseek()将“光标”放入文件中指定的偏移量。

然后您调用scanf() 来扫描并解释(!)从该偏移量开始的字符序列。

Input Offset set by fseek() Text Resulting value
0 0 "10lf..." 10
1 4 "0lf..." 0
2 8 "lf40lf..." 40
3 12 "50lf..." 50
4 16 "0lf..." 0

由于scanf() 会跳过前导空格,因此在第三种情况下您会得到“40”。

在一般情况下,您不能使用fseek()“跳转”到文本文件中的某一行。除了,您知道每行有多长。在你的情况下,这是已知的,如果你使用 3 而不是 4 的因子,你会得到你想要的。

【讨论】:

    【解决方案2】:

    我不知道你的“numbers.bin”中有什么,你以 infile_t 的形式打开了“numbers.txt”但没有使用它。

    假设'numbers.bin'中的内容是你问题中的文本内容,并且你以二进制方式打开它进行阅读,文件中存储的内容如下(改为以一个字节'\n'结尾两个字节'\r\n'):

    \x31\x30\x0a\x32\x30\x0a\x33\x30\x0a\x34\x30\x0a\x35\x30\x0a\x36\x30\x0a\x37\x30

    此时文件指针在文件头部,指向文本内容'1'(ascii码为0x31)。

    \x31\x30\x0a\x32\x30\x0a\x33\x30\x0a\x34\x30\x0a\x35\x30\x0a\x36\x30\x0a\x37\x30
    ↑
    

    当你使用scanf("%d",&amp;input)并输入'0'时,整数变量input将为0,然后你通过fseek(infile_b, input*4, SEEK_SET)设置文件指针,文件指针将指向相对于开头的偏移量0文件。

    下一行fscanf(infile_b, "%d\n", &amp;ch) 将读取一个整数值到变量ch,然后ch 将存储值 10 并通过 printf 将其打印到标准输出 (stdout)。

    当你输入'1'时,文件指针会被设置为4,这将指向相对于文件开头的第五个字节位置,如下:

    \x31\x30\x0a\x32\x30\x0a\x33\x30\x0a\x34\x30\x0a\x35\x30\x0a\x36\x30\x0a\x37\x30
                    ↑
    

    文本值'0'的ASCII码是0x30。它将读取一个整数值 0 并将其存储在 ch 中。

    您可以将fseek(infile_b, input*4, SEEK_SET) 替换为fseek(infile_b, input*3, SEEK_SET),将得到预期的输出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-18
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 2018-01-08
      相关资源
      最近更新 更多