C二进制读写文件

C binary read and write file

我正在使用一个二进制文件来读取一个整数数组,那么每个偶数 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);
}
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) returns 一个无符号数,size_t。您的编译器可能会为您将它们转换为 signed long,但应该明确地这样做。

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

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

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

好吧,我真的不明白发生了什么,但在那种情况下,当与 read/write 文件一起使用时,你似乎不能相信 fseekSEEK_CUR(我' m 运行 Windows,并且标准函数与 Linux 有很大不同,这可能是问题所在)

编辑: 证实了我的怀疑。我的解决方案符合标准推荐的内容。

我为解决该问题所做的工作是自己管理文件位置并查找该位置,而不是在调用 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

所以这是一种解决方法,但至少它可以正常工作。

此代码:

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);
    }
}

违反了7.21.5.3 The fopen function, paragraph 7 of the C Standard中的限制(加粗我的):

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.

每个循环迭代以 fwrite() 调用结束,下一个循环迭代以 fread() 调用开始,"without an intervening call to a file positioning function".