C двоичный файл для чтения и записи - PullRequest
0 голосов
/ 16 ноября 2018

Я использую двоичный файл для чтения массива целых чисел, тогда каждое четное целое число x должно стать 2 * x, а каждое нечетное целое число x должно стать 3 * x. Когда я делаю это, он всегда читает 2-е целое число (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 голосов
/ 17 ноября 2018

Хорошо, я не совсем понимаю, что происходит, но кажется, что вы не можете доверять fseek с SEEK_CUR при использовании с файлами чтения / записи в этом случае (у меня Windows и стандартные функциикак известно, отличаются от Linux, что может быть проблемой)

РЕДАКТИРОВАТЬ: Ответ Эндрю подтверждает мои подозрения.Мое решение соответствует тому, что рекомендуют стандарты.

Что я сделал, чтобы обойти проблему, так это сам управлять позицией файла и искать ее, вместо того чтобы неявно полагаться на текущую позицию файла при вызове fseek. * 1010.*

#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

Так что это обходной путь, но, по крайней мере, он работает правильно.

0 голосов
/ 17 ноября 2018

Этот код:

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 Функция fopen , параграф 7 Стандарта C (шахтная установка):

Когда файл открывается в режиме обновления («+» в качестве второго или третьего символа в приведенном выше списке значений аргументов режима), ввод и вывод могут выполняться в связанном потоке.Однако за выводом не должен следовать непосредственно ввод без промежуточного вызова функции fflush или функции позиционирования файла (fseek, fsetpos или rewind), а за вводом не должен непосредственно следовать вывод без промежуточного вызовафункция позиционирования файла , если только операция ввода не встречает конец файла.

Каждая итерация цикла заканчивается вызовом fwrite(), а следующая итерация цикла начинается с вызова fread(), "без промежуточного вызова функции позиционирования файла".

0 голосов
/ 16 ноября 2018
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");
}
...