理解 C 语言中的 fseek()

Understanding fseek() in C

提问人:Chemical Brewster 提问时间:2/20/2023 最后编辑:John BollingerChemical Brewster 更新时间:2/20/2023 访问量:124

问:

我正在用 C 语言学习文件 I/O,并有兴趣使用它通过 和 函数将结构读取和写入文件,现在在我的代码成功运行后,我想知道我是否可以从结构数组中读取特定结构并将其放在某个给定的结构中。fwrite()fread()


这是我的尝试

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

typedef struct tools {
    int recordno;
    char toolname[50];
    int quantity;
    float cost;
} tools;

void recordprinter(tools a) {
    printf("%d %s %d %f\n", a.recordno, a.toolname, a.quantity, a.cost);
}

int main() {
    FILE * fp;
    
    fp = fopen("file.txt", "rb+");
    tools * a = (tools * ) malloc(100 * sizeof(tools));
    for (int i = 0; i < 100; i++) {
        a[i].cost = 0;
        a[i].toolname[0] = 'a';
        a[i].toolname[1] = '\0';
        a[i].quantity = 0;
        a[i].recordno = i + 1;
    }
    for (int i = 0; i < 100; i++) {
        fwrite(a + i, sizeof(tools), 1, fp);
        fseek(fp, sizeof(tools), SEEK_CUR);
        // I used fseek here just because fwrite doesnot move the cursor when\
        it writes something to the file.(and fwrite(a + i, sizeof(tools), 100, fp) gives weird gliches)
    }

    fseek(fp, 0, SEEK_SET); // to bring cursor back to start of the file.

    fread(a, sizeof(tools), 1, fp);

    fseek(fp, sizeof(tools) * 50, SEEK_SET); // now I expect the cursor to be at 51th structure.

    fread(a + 3, sizeof(tools), 1, fp); // I am now writing the 51th structure in a[3]    

    recordprinter(a[3]);
    // this gives output 26 and not 51   

    return 0;
}

现在,当我运行我期望的输出程序时, 但令我惊讶的是,它正在捡起第 26 个结构并将其放入 [3]
任何帮助都将得到应用!!
51 a 0 0.00000

C 文件 fwrite fread fseek

评论

2赞 Some programmer dude 2/20/2023
fwrite确实应该移动“游标”,所以连续两次调用要写入一个文件,应该先后写入两个不同的位置。与 相同。fwritefread
2赞 Some programmer dude 2/20/2023
这才是你真正应该做的。“行为不端”是什么意思?你 100% 确定这在你的程序中没有问题吗?也许你应该问一个关于这种“不当行为”的问题,因为它真的不应该发生。
2赞 n. m. could be an AI 2/20/2023
fwrite移动光标,句号。
3赞 John Bollinger 2/20/2023
C17 7.21.8.2/2(描述以下行为):“[...]流的文件位置指示器(如果已定义)按成功写入的字符数前进。如果发生错误,则流的文件位置指示器的结果值是不确定的。不涉及“感情”。fwrite
1赞 John Bollinger 2/20/2023
但是,您确实需要插入文件定位操作,例如从读取切换到写入同一流时。fseek()

答:

1赞 atl 2/20/2023 #1

尝试更改为使用而不是fopenw+rb+

此外,如前所述,在创建文件时删除 肯定会在写入数据后推进文件偏移量(前提是确实写入数据)。fseekfwritefwrite

以下是使用下面修改后的代码观察到的输出。

gcc main.c
./a.out
51 a 0 0.000000
// main.c

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

typedef struct tools {
    int recordno;
    char toolname[50];
    int quantity;
    float cost;
} tools;

void recordprinter(tools a) {
    printf("%d %s %d %f\n", a.recordno, a.toolname, a.quantity, a.cost);
}

int main() {
    FILE * fp;
    
    // recommend for this example using w+
    // w because it creates the file if the file doesn't exist
    // r fails if the file doesn't exist (and that doesn't seem useful here)
    // + because you are reading and writing
    // avoiding b and choosing POSIX - linux
    // may be wrong, if libc docs says b is needed then use b
    // my doc "man fopen" says b is ignored
    fp = fopen("file.txt", "w+");
    // check return values, file pointer exist? fail if not
    if (fp==NULL) { printf( "oops file not opened\n" ); return 1; }
    tools * a = (tools * ) malloc(100 * sizeof(tools));
    for (int i = 0; i < 100; i++) {
        a[i].cost = 0;
        a[i].toolname[0] = 'a';
        a[i].toolname[1] = '\0';
        a[i].quantity = 0;
        a[i].recordno = i + 1;
    }
    // alternative way to save 100 objects
    // if ( fwrite(a, sizeof(tools), 100, fp) != 100 )
    // {
    //   printf( "oops 100 objects not written to file\n" );
    //  return 1;
    // }
    for (int i = 0; i < 100; i++) {
        fwrite(a + i, sizeof(tools), 1, fp);
        // remove fseek, not needed, fwrite does what is needed here
        //fseek(fp, sizeof(tools), SEEK_CUR);
        // I used fseek here just because fwrite doesnot move the cursor when
        // it writes something to the file.(and fwrite(a + i, sizeof(tools), 100, fp) gives weird gliches)
    }

    // no review after this line, it seems to do what author intends

    fseek(fp, 0, SEEK_SET); // to bring cursor back to start of the file.

    fread(a, sizeof(tools), 1, fp);

    fseek(fp, sizeof(tools) * 50, SEEK_SET); // now I expect the cursor to be at 51th structure.

    fread(a + 3, sizeof(tools), 1, fp); // I am now writing the 51th structure in a[3]    

    recordprinter(a[3]);
    // this gives output 51 as desired   

    return 0;
}

评论

1赞 John Bollinger 2/20/2023
关于您为什么建议这些更改的一些评论将是有序的。
0赞 John Bollinger 2/20/2023
此外,将文件作为文本文件打开似乎不合适 ()。某些实现没有有意义地区分文本和二进制模式,但其他实现可以,在这些实现上,应用程序使用并且通常需要二进制模式。也许,那么。w+fread()fwrite()wb+
0赞 Chemical Brewster 2/20/2023
wb+似乎是一个不错的选择,谢谢@atl
0赞 atl 2/20/2023
@JohnBollinger,同意 - 添加了一些关于我为什么选择上面列出的内容的解释
1赞 John Bollinger 2/20/2023
@atl,为什么选择一种语义上更正确且适用于所有实现的替代方案如此容易,为什么还要选择仅适用于某些实现的替代方案呢?