【问题标题】:fread/ftell apparently broken under Windows, works fine under Linuxfread/ftell 在 Windows 下明显坏了,在 Linux 下工作正常
【发布时间】:2011-03-12 09:20:42
【问题描述】:

所以问题来了,我正在为我的游戏读取关卡文件,在 linux 下可以正常工作:

@0
@12
200x200 version 3
@16
973 blocks
@989
@993
18 zones

但在 windows 下我得到以下结果:

@0
@212
200x200 version 3
@216
973 blocks
@1200
@1204
18 zones

嗯?偏移量为 200 的 windows ftell 统计信息?读取文件显然会产生相同的数据,但 fread 使用(?) ftell 的值来确定文件中剩余的可以读取的字节数。所以当然我在文件末尾阅读时遇到了问题:

@1425
zone#9 2x3 @ 66/9
@1425
zone#10 2x3 @ 66/9
@1425
zone#11 2x3 @ 66/9
@1425
zone#12 2x3 @ 66/9
@1425
zone#13 2x3 @ 66/9
@1425
zone#14 2x3 @ 66/9
etc.

这是相应的代码(由于所有的调试打印,目前有点难看..):

void fread_all(void *ptr, size_t size, size_t count, FILE *stream) {
    fread(ptr, size, count, stream);
    printf("@%ld\n", ftell(stream));
}


bool map_load(struct Map *map, const char *file_name) {
    FILE *fp = fopen(file_name, "r");
    if (fp != NULL) {
        fseek(fp, 0, SEEK_SET);
        printf("@%ld\n", ftell(fp));

        // Header
        int *header = (int*)calloc(sizeof(int), 3);
        fread_all(header, sizeof(int), 3, fp);
        printf("%dx%d version %d\n", header[0], header[1], header[2]);

        map->pos_x = 0;
        map->pos_y = 0;
        map->map_x = 0;
        map->map_y = 0;
        map->size_x = header[0];
        map->size_y = header[1];
        map_zones_remove(map);        
        free(header);

        // Blocks
        unsigned int *block_size = (unsigned int*)malloc(sizeof(unsigned int));
        fread_all(block_size, sizeof(int), 1, fp);
        printf("%d blocks\n", *block_size);

        unsigned char *block_data = (unsigned char*)calloc(sizeof(unsigned char), *block_size);
        fread_all(block_data, sizeof(unsigned char), *block_size, fp);

        unsigned char *tmp = map->blocks;
        map->blocks = rle_decode(block_data, *block_size);
        free(tmp);
        free(block_size);
        free(block_data);

        // Zones
        int *zone_count = (int*)malloc(sizeof(int));
        fread_all(zone_count, sizeof(int), 1, fp);
        printf("%d zones\n", *zone_count);

        int *d = (int*)calloc(sizeof(int), 6);
        for(int i = 0, l = *zone_count; i < l; i++) {
            fread_all(d, sizeof(int), 6, fp);
            map_zone_create(map, d[0], d[1], d[2], d[3], d[4], d[5]);
            printf("zone#%d %dx%d @ %d/%d\n", i, d[2], d[3], d[0], d[1]);
        }
        map_platforms_create(map);

        free(zone_count);
        free(d);
        fclose(fp);
        return true;
    }
    return false;
}

我真的不知道这里发生了什么。编译器是 Linux 下的 Visual Studio 10 one 和 GCC 4.4。

【问题讨论】:

    标签: c windows linux file-io


    【解决方案1】:

    以二进制模式打开文件:

    FILE *fp = fopen(file_name, "rb");
    

    在文本模式下,可能会进行翻译以匹配依赖于操作系统的编码,例如换行到 C 库的。

    【讨论】:

    • 呃.....我的大脑在最后几天一定因为高温受到了严重的伤害...... -.-" 现在可以了,谢谢!
    【解决方案2】:

    ftellfseek 仅在以二进制模式打开文件时作为字节偏移量工作(即"rb" 而不是"r")。否则你只能fseek对之前ftell返回的东西; fseek 的结果不会是字节偏移量。

    在文本模式将两个字符回车、换行序列映射到单个换行符的窗口中,二进制模式会有所不同。 linux上不需要映射。

    【讨论】:

    • 成功了,尽管我仍然觉得 fread 只信任 ftell 的值有点奇怪。但是还有一个问题,为什么它使用文件中的第一个字节(C8)作为偏移量?
    【解决方案3】:

    您不应该使用ftellfseek 来确定文件的大小,因为它可能是漏洞的来源:

    https://www.securecoding.cert.org/confluence/display/c/FIO19-C.+Do+not+use+fseek%28%29+and+ftell%28%29+to+compute+the+size+of+a+regular+file

    猜你喜欢
    • 2023-03-11
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-07
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多