Zapisywanie struktury w pliku binarnym, a pola char *

0

Dzień dobry, mam mały problem z zapisywaniem do pliku binarnego struktury, która zawiera wskaźnik na char. Moje pytanie brzmi, czy kod poniżej poprawnie zapisze C-string, double i int do pliku binarnego? Wiem, że najłatwiej by było spróbować odczytać, ale nie bardzo mi to wychodzi. Dlatego pytam, bo może powinienem całkowicie zmienić podejście. Pozdrawiam.

 typedef struct data {

	char * string;
	double value;
	int number;

} data;
}
 void WriteDataObjectToFile(data * element, FILE * dest){
	fwrite(element->string, sizeof(char), strlen(element->string) + 1, dest); //poprawka
	fwrite(&element->value, sizeof(double), 1, dest);
	fwrite(&element->number, sizeof(int), 1, dest);
}
1

Jeśli się nie mylę to nie ma potrzeby wyciągania adresu zmiennej string w strukturze data (ponieważ jest już ona wskaźnikiem na pierwszy element zmiennej tekstowej).

Tutaj kod:

void WriteDataObjectToFile(data *element, FILE *dest)
{
  fwrite(element->string, sizeof(char), strlen(element->string) + 1, dest);
  fwrite(&element->value, sizeof(double), 1, dest);
  fwrite(&element->number, sizeof(int), 1, dest);
}
0

Dziękuję za odpowiedź. Tak, zgadza się, bez znaku ampersand. Sprawdziłem już, że ten kod poprawnie ( w sensie, że jestem w stanie odczytać dane) zapisuje.

Mam jeszcze pytanie odnośnie odczytywania C-stringa z pliku binarnego. Obecnie robię to za pomocą funkcji fread znak po znaku, aż do pierwszego występienia \0. Czy jest lepszy sposób?

1

Ja bym spróbował zrobić to w ten sposób: przed zapisaniem wartości tekstowej zapisujesz długość tekstu. Następnie przy wczytywaniu już wiesz jaką długość ma tekst.

Tutaj masz przykład:

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


typedef struct
{
  char *string;
} node;

node *node_new()
{
  node *self = malloc(sizeof(node));
  if (!self) {
    // error
    return NULL;
  }

  self->string = NULL;
  return self;
}

void node_set(node *self, const char *value)
{
  assert(self != NULL);

  self->string = malloc(strlen(value) + 1); // tutaj także wypada sprawdzić błąd
  strcpy(self->string, value);
}

void node_free(node *self)
{
  if (self->string) {
    free(self->string);
  }

  free(self);
}


void w_node(node *entry, const char *file) {
  FILE *f = fopen(file, "w");
  if (!f) {
    // error
    return;
  }

  assert(entry->string != NULL); // dodać kod, który obsłuży pustą zmienną tekstową 

  int size = strlen(entry->string);
  fwrite(&size, sizeof(int), 1, f);
  fwrite(entry->string, sizeof(char), size, f);

  fclose(f);
}

node *r_node(const char *file) {
  FILE *f = fopen(file, "r");
  if (!f) {
    // error
    return NULL;
  }

  int size = 0;
  fread(&size, sizeof(int), 1, f);

  assert(size != 0); // to też warto obsłużyć

  char *buffer = malloc(size + 1);
  if (!buffer) {
    // error
    return NULL;
  }

  fread(buffer, sizeof(char), size, f);
  buffer[size + 1] = 0;

  node *entry = node_new();
  if (!entry) {
    // error
    return NULL;
  }

  // buffer jest podwójnie kopiowany, wolno to zoptymalizować
  node_set(entry, buffer);

  free(buffer);
  return entry;
}

int main(int argc, char **argv)
{
  FILE *f = fopen("test.txt", "r");
  if (!f) {
    // error
    return 1;
  }

  node *entry = node_new();
  if (!entry) {
    // error
    return 1;
  }

  node_set(entry, "test");
  w_node(entry, "test.txt");

  node_free(entry);

  node *loaded_entry = r_node("test.txt");
  printf("%s\n", loaded_entry->string);
  node_free(loaded_entry);

  fclose(f);
  return 0;
}
3

Najprościej to tak:

void WriteDataObjectToFile(data * element, FILE * dest)
  {
   int size=strlen(element->string)+1;
   fwrite(&size,sizeof(int),1,dest);
   fwrite(element->string,sizeof(char),size,dest);
   fwrite(&element->value,sizeof(double),1,dest);
   fwrite(&element->number,sizeof(int),1,dest);
  }

data *ReadDataObjectFromFile(FILE *dest)
  {
   data *element=malloc(sizeof(data));
   int size;
   fread(&size,sizeof(int),1,dest);
   element->string=malloc(size);
   fread(element->string,sizeof(char),size,dest);
   fread(&element->value,sizeof(double),1,dest);
   fread(&element->number,sizeof(int),1,dest);
   return element;
  }
0

Dziękuję za odpowiedzi. Będę musiał je przeanalizować, zwłaszcza tę pierwszą. :) Z początku też próbowałem z zapisaniem długości C-stringa do pliku, ale widocznie coś przekombinowałem, bo nie chciało to działać.

Mam jeszcze pytanie: czy to normalne, że jeżeli nie ustaliłem warunku stopu w pętli, która odczytuje kolejne obiekty, to wczytywanie ciągnie się w nieskończoność? Po prostu C-stringi są puste, a wartości liczbowe to tzw. śmieci.
Czyj eżeli zapisuje do pliku i go zamykam fclose, to znak EOF jest dodawany automatycznie? Pytam, ponieważ jak włączyłem, tę moją pętlę do wczytywania znak po znaku, to w rozsądnym czasie, nigdy nie znajduje ona końca pliku i wypisuje te puste i śmieciowe wartości.

1

Znak EOF nie jest dodawany ale mimo to próba odczytu za końcem pliku zwraca znaki EOF.
Tuż po otwarciu pliku przenieś się na koniec fseek(), zanotuj sobie rozmiar pliku ftell, wróć na początek pliku.
Przed każdym odczytem sprawdzaj czy doszedłeś do końca pliku.
Pętla sama się nie zatrzyma ponieważ twórcy C popełnili gafę zapominając dodać do standardu moduł czytający myśli twórcy programu.

0

Dzięki. Proste, a nie wpadłem, ale dopiero zaczynam z funkcjami wyjścia/wejścia. Jak już opanuję C na przyzwoitym poziomie, to napiszę ten brakujący moduł, co by inni mieli łatwiej w przyszłości. :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1