Чтение/запись бинарных файлов в C

Итак, у меня есть файл input.bin, который содержит следующее

IK-SZH;jdl72u;John Doe;2013-03-28 11:05
IK-GRR;kat91n;Jane Doe;2013-03-21 15:41
IK-GRR;oat62f;Jane Doe;2013-03-24 08:08

Что я делаю, так это читаю его в структуру. Что-то делать с данными. Добавить/удалить строки. Затем я хотел бы записать содержимое структуры обратно в файл input.bin в том же формате, что и выше.

Но вместо того, чтобы появиться, как это выше. Вот так (без пробелов):

IK-SZH NUL jdl72u NUL John Doe NUL NUL NUL NUL NUL 2013-03-28 NUL NUL NUL IK-GRR NUL kat91n NUL Jane Doe NUL NUL NUL NUL ...

Когда я перечитываю файл (с NUL), он помещает только 1-ю строку в структуру

Мой код

typedef struct foo {
    char kid[7];    
    char jid[7];    
    char name[21];  
    char time[20];  

} Foo;

Foo foo[200];
FILE* fp;
int size;

------- Читатель файлов

void read(char* filename){
    fp = fopen(filename, "rb"); 
    int i = 0;

    while (!feof(fp)) {

        if (fp==NULL){perror("File opening error\n"); exit(1);}

        fscanf(fp,"%[^;]; %[^;]; %20[^\;]; %s\n", foo[i].kid, foo[i].jid,
                foo[i].name, foo[i].time);
        i++;
    }

    size = i;

    print();

    fclose(fp);
}


void print(){
    int i;
    for (i = 0; i < size; ++i){
        printf("%s\t %s\t %s\t %s\n", foo[i].kid, foo[i].jid,
               foo[i].name, foo[i].time);
    }
}

----- Писатель

void write(){
    char str[1000];

    FILE* f = fopen("input.bin", "wb");
    fseek(f, 0, SEEK_SET);
    int i;
    for (i = 0; i < jel_size; i++)
            fwrite(&foo[i], sizeof(struct foo), 1, f);
    fclose(f);

}

Пробовал это, но это ничего не записало в файл:

    char str[1000];
    sprintf(str,"%s;%s;%s;%s\n", jelent[i].kazon,
                    jelent[i].jazon,jelent[i].nev,  jelent[i].ido );

        write(f,&str,sizeof(str))!=sizeof(str);

person VZKiss    schedule 01.04.2013    source источник
comment
input.bin кажется мне текстовым файлом, а не двоичным файлом.   -  person Marcos    schedule 02.04.2013
comment
Вы можете рассмотреть возможность использования fprintf() вместо fwrite() или write().   -  person Mackie Messer    schedule 02.04.2013
comment
Вы должны открывать текстовый файл в текстовом режиме, а не в двоичном режиме.   -  person Code-Apprentice    schedule 02.04.2013


Ответы (2)


Ваше определение структуры:

typedef struct foo
{
    char kid[7];    
    char jid[7];    
    char name[21];  
    char time[20];  
} Foo;

Код, записывающий структуры в файл, находится в write():

for (i = 0; i < jel_size; i++)
    fwrite(&foo[i], sizeof(struct foo), 1, f);

Если структуры инициализируются со всеми нулевыми байтами, то для первой строки данных вы указываете:

IK-SZH;jdl72u;John Doe;2013-03-28 11:05

мы можем сделать вывод, что структура содержит:

IK-SZH\0
jdl72u\0
John Doe\0\0\0\0\0\0\0\0\0\0\0\0\0
2013-03-28 11:05\0\0\0\0

что более или менее согласуется с тем, что вы говорите, файл содержит:

IK-SZH NUL jdl72u NUL John Doe NUL NUL NUL NUL NUL 2013-03-28 NUL NUL NUL 

По моим подсчетам, вам не хватает нескольких NUL после имени и времени и одного NUL после даты.

Если вы хотите вывести текст, вам придется отформатировать данные в write(), используя fprintf() аналогично оператору fscanf(), который у вас есть в функции read(). Вероятно, вам следует вернуть статус из функции, чтобы указать, была ли она успешной. Вы также можете привести доводы в пользу проверки того, что fclose() прошла успешно.

void write(void)
{
    FILE *fp = fopen("input.bin", "w");
    if (fp != 0)
    {
        for (int i = 0; i < jel_size; i++)
            fprintf(fp, "%s;%s;%s;%s\n", foo[i].kid, foo[i].jid, foo[i].name, foo[i].time);
        fclose(fp);
    }
}

Если вам нужен двоичный вывод, вы должны использовать двоичный ввод в read(). Опять же, вам следует подумать о возвращении индикации успеха или неудачи; избегание глобальных переменных тоже хорошо.

void read(const char *filename)
{
    FILE *fp = fopen(filename, "rb"); 
    if (fp != 0)
    {
        for (i = 0; fread(&foo[i], sizeof(foo[i]), 1, fp) == 1; i++)
            ;
        fclose(fp);
        size = i;
    }
}

Любой метод будет работать; попытка использовать один на выходе, а другой на входе приведет к хаосу и путанице.

Обратите внимание, что функции read() и write() используются POSIX с другим интерфейсом. Пока вы придерживаетесь стандарта C, имена вам подходят, но вам, вероятно, лучше использовать альтернативные имена, такие как reader() и writer() или foo_read() и foo_write().

person Jonathan Leffler    schedule 02.04.2013
comment
Не могу поверить, что я не думал о fprintf. Я уже знал о fread, fwrite. Просто не хотел использовать это как есть; разделители в файле и не был уверен, как решить проблему с этим. Большое спасибо за вашу помощь! - person VZKiss; 02.04.2013

https://gist.github.com/anonymous/5288805

Немного изменен код, прокомментирован, а также протестирован, так что он точно работает.

person Sava Vranešević    schedule 01.04.2013