简体   繁体   中英

Reading and Writing from a binary file in C

This question has been asked many times before, but I've been trying this for hours with a variety of methods and I have no clue why this is wrong.

I've been writing C structs to a binary file using fwrite() and I've been trying to read from them using fread() . However, after I read from the file, all I get is nonsense answers in my struct when I try to print out the results. I've done a dump of the binary file and it looks fine, so I'm not quite sure what's wrong. Can anyone help? I'm running all of this on Ubuntu 16.04 32bit.

Struct:

typedef struct
{
    char file_name[256];
    int data_length;
} FileInfo;

Reading from file:

void displayInformation(FILE *file) {
  FileInfo fileInfo;

  if (fread(&fileInfo, sizeof(FileInfo), 1, file) < 0)
    printf("something went wrong with reading the file\n");

  printf("FileInfo: %s\n", fileInfo.file_name);
  printf("FileInfo: %d\n", fileInfo.data_length);
}

Writing to file:

void writeToFile() {
  ....
  uint8_t outbuf[MAX_BUF_SIZE];
  uint8_t *buf_ptr = outbuf;

  strncpy(((FileHeader *)outbuf)->file_name, fileName, sizeof(((FileHeader *)outbuf)->file_name) - 1);
  buf_ptr += sizeof(FileHeader);
   ....
  fwrite(outbuf, sizeof(FileInfo) + ((FileInfo *)outbuf)->data_length, 1, outputFile);
}

A hexdump of the file:

hexdump ./bin/example.bin

0000000 2f2e 6962 2f6e 7865 6d61 6c70 0065 0000
0000010 0000 0000 0000 0000 0000 0000 0000 0000
*
0000100 0034 0000 0001 0000 003e 0000 0000 0000
0000110 f362 4e1d 86c3 0e91 49c2 8e6c 6df6 d1bb
0000120 0002 0000 c437 3eeb 0003 0000 0792 0000
0000130 0004 0000 0000 0000
0000138

Thanks!

EDIT: I figured it out, it was because I wasn't reading the file from the right directory. Thanks everyone for their help though! It was really informative.

Here's how I'd approach the problem as a first pass:

typedef struct
{
  // data_length should come first because it's not an arbitrary length and needs
  // to be aligned on a 4-byte boundary. This avoids ugly padding issues.
  int data_length;
  char file_name[256];
} FileInfo;

size_t writeFileInfo(FILE* f, FileInfo* fi) {
  return fwrite((void*)&fi->data_length, sizeof(int), 1, f) &&
    fwrite((void*)&fi->file_name, 1, fi->data_length, f);
}

size_t readFileInfo(FILE* f, FileInfo* fi) {
  size_t rv = 0;

  rv += fread((void*)&fi->data_length, sizeof(int), 1, f);
  rv += fread((void*)&fi->file_name, 1, fi->data_length, f);

  // Don't forget to NULL-terminate this string
  fi->file_name[fi->data_length] = 0;

  return rv;
}

These two functions break out the reading and writing operations into self-contained chunks of code, but they could just as easily be incorporated as part of another read/write function respectively.

The trick here is to write out a variable length field in your header as a length + string pair. Older file formats will use 8-bit or 16-bit values for these fields to avoid file bloat, but two extra bytes is probably not the end of the world here. You can trim those off by switching your length field to uint16_t if you feel that's necessary.

Here's a quick demonstration:

#include <stdio.h>
#include <string.h>

void testWrite(char* path) {
  FILE* f = fopen(path, "w");

  FileInfo example;

  strcpy(example.file_name, "example.txt");
  example.data_length = strlen(example.file_name);

  writeFileInfo(f, &example);

  fclose(f);
}

void testRead(char* path) {
  FILE* f = fopen(path, "r");

  FileInfo example;

  readFileInfo(f, &example);

  printf("Read: %d / %s\n", example.data_length, example.file_name);

  fclose(f);
}

int main(int argc, char** argv) {
  char* testDump = "dump.bin";

  testWrite(testDump);
  testRead(testDump);

  return 0;
}

You should get a simple binary file with the right content in it. Pay very close attention to correctly NULL terminating your strings when reading in to raw buffers. C is always one tiny mistake away from a massive buffer overflow bug.

You'll want to get rid of that fixed-length character buffer and use something like malloc() to read in whatever size the size field dictates. Be careful with possibly huge values though. A single bit flip corruption error could trick your program into allocating 2GB of memory for no reason.

It was a really simple error - it was because I wasn't reading the file at all. Changing the if statement to be <=0 helped me track that down.

From:

if (fread(&fileInfo, sizeof(FileInfo), 1, file) < 0)
    printf("something went wrong with reading the file\n");

To:

if (fread(&fileInfo, sizeof(FileInfo), 1, file) <= 0)
    printf("something went wrong with reading the file\n");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM