C二进制读写文件

时间:2018-11-16 20:31:40

标签: c windows file binaryfiles fseek

我正在使用一个二进制文件读取整数数组,然后每个偶数x都应该变成2 * x,每个奇数x都应该变成3 * x。当我这样做时,它总是读取第二个整数(即2)。有想法吗?

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

int main(void)
{    
FILE *f;

f = fopen("inputData.txt", "w+b");
int n = 5;
int i;
for (i = 1; i <= n; ++i) {
    fwrite(&i, sizeof(int), 1, f);
}
int x;
fseek(f, 0, SEEK_SET);
while (fread(&x, sizeof(int), 1, f) == 1) {
    printf("%d ", x);
    if (x % 2 == 0) {
        fseek(f, -sizeof(int), SEEK_CUR);
        x = x * 2;
        fwrite(&x, sizeof(int), 1, f);
    } else {
        fseek(f, -sizeof(int), SEEK_CUR);
        x = 3 * x;
        fwrite(&x, sizeof(int), 1, f);
    }
}

fclose(f);
}

3 个答案:

答案 0 :(得分:1)

好的,我不太了解发生了什么,但是在这种情况下使用读/写文件时,您似乎无法信任fseekSEEK_CUR。并且众所周知,标准功能与Linux可能有所不同

编辑:Andrew's answer证实了我的怀疑。我的解决方案符合标准的建议。

要解决该问题,我要做的是自己管理文件位置并寻求该位置,而不是在调用fseek时隐式依赖当前文件位置。

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

int main(void)
{
 FILE *f;

f = fopen("inputData.txt", "w+b");
if (!f) { perror("cannot create input"); exit(1); }

int n = 5;
int i;
for (i = 1; i <= n; ++i) {
    fwrite(&i, sizeof(int), 1, f);
}


int x;
int pos=0;
fseek(f, 0, SEEK_SET);
while (fread(&x, sizeof(int), 1, f) == 1) {
    if (fseek(f, pos, SEEK_SET)) {perror("cannot seek");exit(1);}
    pos += sizeof(int);
    printf("%d %lu\n", x, ftell(f));
    if (x % 2 == 0) {
        x = x * 2;
    } else {
        x = 3 * x;
    }
    if (fwrite(&x, sizeof(int), 1, f) != 1) {perror("cannot write");exit(1);}
    if (fseek(f, pos, SEEK_SET)) {perror("cannot seek");exit(1);}
}

fclose(f);
}

现在程序的输出为(具有当前偏移量)

1 0
2 4
3 8
4 12
5 16

现在二进制文件的内容(在小端架构上是预期的):

03 00 00 00 04 00 00 00 09 00 00 00 08 00 00 00 0F 00 00 00

这是一种解决方法,但至少可以正常使用。

答案 1 :(得分:1)

This code:

while (fread(&x, sizeof(int), 1, f) == 1) {
    printf("%d ", x);
    if (x % 2 == 0) {
        fseek(f, -sizeof(int), SEEK_CUR);
        x = x * 2;
        fwrite(&x, sizeof(int), 1, f);
    } else {
        fseek(f, -sizeof(int), SEEK_CUR);
        x = 3 * x;
        fwrite(&x, sizeof(int), 1, f);
    }
}

violates the restrictions in 7.21.5.3 The fopen function, paragraph 7 of the C Standard (bolding mine):

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end- of-file.

Each loop iteration ends with an fwrite() call, and the next loop iteration starts with fread() call, "without an intervening call to a file positioning function".

答案 2 :(得分:0)

f = fopen("inputData.txt", "w+b");

w将截断该文件(如果存在)。 +不会改变这种行为。

这是一个简单的演示。

$ cat test.c
#include <stdio.h>

int main() {
    FILE *f;

    f = fopen("inputData.txt", "w+b");
    if( f == NULL ) {
        perror("open failed");
    }

    fclose(f);
}

$ make
cc -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic -c -o test.o test.c
cc -fsanitize=address  test.o   -o test

$ cat inputData.txt 
abc
$ ./test
$ cat inputData.txt 

请使用r+b


另外两个问题。

sizeof(int)返回一个无符号数字size_t。您的编译器可能会为您将它们强制转换为signed long,但是应该明确地做到这一点。

fseek(f, -(long)sizeof(int), SEEK_CUR);

您不检查文件操作是否成功。在这种情况下,它们是可以的,但是它们将无声地失败并且很难调试。检查所有文件操作很重要。

if( fseek(f, -(long)sizeof(int), SEEK_CUR) != 0 ) {
    perror("fseek failed");
}