Write/Read structure with pointers to file - c

I have to write a structure to a file so i can read it in later. The struct is:
struct prog{
char* title;
char* channel;
struct tm* start;
struct tm* end;
float review;
enum soort sort;
union type *type;
};
union type{
struct serie *ep;
struct film *mov;
};
struct serie{
int seznum;
int epnum;
char* eptitle;
};
struct film{
char* reg;
char* act;
char* genre;
};
enum sort { film, serie }
So you can see that there are pointers to strings and the struct tm (from time.h). I can't figure out how to write it to a binary file, all i can do is write it string by string but there must be a more efficient way to write the previous struct.
I think the problem starts with the char* and the pointers to the tm structs because the program is now going to write the adress of the start of a string or the adress of the tm struct. I wan't to write the string and not the adress to a file. I tried with record I/O but it writes the adresses so i can't read them later.
Thanks!

You may want to look at time_t or other alternatives which can store time in a single integer timestamp. You can also use a database or a simple file storage class.
Otherwise you can't really improve this with native C functions. You could remove pointers and use fixed character arrays like so:
struct T_film
{
char text[50];
int i;
};
struct T_prog
{
char title[50];
tm start;
tm end;
T_film film;
};
T_prog data;//initialize...
fwrite(&data, 1, sizeof(T_prog), fout);
fseek(fin,0,0);
fread(&data, 1, sizeof(T_prog), fin);
Now the structure has a fixed pattern and everything can be stored in binary. BUT, this can be even more inefficient. The size of text is too short, or too long with extra blank space which takes too long to read. It's not portable either. So you may want to stick to pointers and reading/writing with makeshift methods.
You can also separate fixed sized data and put them in a separate structure. Write the fixed sized data in one block, then write the character strings one by one.

Related

Convert struct in byte array and store in db. Read db and get byte array to recreate the struct in C

Hello everyone sorry for asking but I could find any appropriate solution.
I am working on a file system where I save each file node as key-value pair in a GDBM database.
I have a struct which has some attributes and I convert this to a byte array
struct mystruct:
typedef struct nold{
char* name;
char* surname;
int age;
}mystruct;
I convert it as following to a byte array
dead.name="john";
dead.surname="doe";
dead.age=22;
//copy bytes of the our struct
char buffer[sizeof(dead)];
memcpy(buffer, &dead, sizeof(dead));
to store in a database we use a datum structure which is as following:
typedef struct {
char *dptr;
int dsize;
} datum
I fill the datum structure as following:
//create a key datum
char* k="file.txt";
key.dptr=k;
key.dsize=strlen(k)+1;
//create a value datum here I assign bytes
value.dptr=buffer;
value.dsize = sizeof(dead);
Now I store in GDBM as key-value pair
Then in another file I read the data I store and try to recast it back to a struct
datum result;
//read
result=gdbm_fetch(file,key);
char* bytes=result.dptr;
mystruct* reborn;
reborn=(mystruct*)bytes;
//print from our new struct
printf("%s\n",reborn->name);
printf("%s\n",reborn->surname);
printf("%d\n",reborn->age);
It prints the following:
E���D$�$ˈ�k����E��
$�$ˈ�k����E��
22
It manages to bring back the struct but data of char* is lost. Only the integer data is covered. Any idea why that happening? How to solve it? It it cant be solved by storing a byte array what about converting the byte array to hex or base64 and store it as such.
I really am struggling with this one.Thank you in advance.
A pointer inside struct is just a pointer to character array not the character array itself:
typedef struct nold{
char* name;
char* surname;
int age;
}mystruct;
mystruct s;
s.name = "Salam";
This will reserver a memory space for string "Salam", put string Salam into and return back the pointer to s.name.
now you're copying entire struct into something other, youre copying from struct address with sizeof(mystruct) which isn't hold string "Salam" actually, it just holds a pointer to "Salam".
If you want to do this way, you have to preallocate some space for name :
#define MAX_NAME_LEN 50
typedef struct nold{
char name[MAX_NAME_LEN];
char surname[MAX_NAME_LEN];
int age;
}mystruct;
mystruct s;
strcpy(s.name, "Salam");
now memcpy will work
mystruct d;
memcpy(&d, &s, sizeof(mystruct);
Pointer values are meaningful only in the context of a specific run of a specific program. It is possible to write them and read them back, but it is essential to remember that a pointer designates an address, not (directly) the data residing at that address, if indeed the address is valid at all for the program accessing it. You do not convey the pointed-to data between processes by conveying the pointer.
You could perhaps solve the problem by putting the data you want to convey directly into the struct:
typedef struct {
char data[MAX_DATA_SIZE];
int dsize;
} datum;
The drawback of this approach is that you have to put a fixed upper bound on the size of the data, but in many cases that's not a problem. If you need unbounded data, then you need an altogether different approach.
The problem is that name and surname are pointers to string literals. So your struct does not contain the string itself but rather pointers to the data section where the literals reside. As soon as you copy that to your buffer, you are in fact simply copying the pointer values. Upon reconstruction, those pointer values would now point to some arbitrary area that don't contain your strings.
EDIT: Here's a function that transfers the data to a buffer:
size_t copyMyStructToBuffer(mystruct *aPerson, char **buffer) {
size_t nameLen = strlen(aPerson->name);
size_t surnameLen = strlen(aPerson->surname);
size_t structLen = nameLen + 1 + surnameLen + 1 + sizeof(int);
*buffer = malloc(structLen);
memcpy(*buffer, aPerson->name, nameLen + 1); // w/ terminator
memcpy((*buffer)[nameLen+1], aPerson->surname, surnameLen + 1); // w/ terminator
memcpy((*buffer)[nameLen+1+surnameLen+1], &aPerson->age, sizeof(int));
return structLen;
}
And how you can use it:
mystruct dead;
dead.name = "John";
dead.surname = "Doe";
dead.age = 22;
char *buff;
size_t buffLen;
buffLen = copyMyStructToBuffer(&dead, &buff);
// use buff here
free(buff);
Admittedly though, this code is difficult to maintain.

read struct objects from a file and point to the object read using void* pointer

I have the following four structs in my program
struct SType{
int type;//struct type
};
struct S1{
};
struct S2{
};
struct S3{
};
I am saving the states of these structs in a file using the following code:
void store(struct SType s1,void *s){
//open file and stuff
//s points to either one of the last three structs
fwrite(&s1,sizeof(s1),1,file); fwrite(s, size, 1, file);
//structs are always saved in the file in pairs of SType and either one of the last three structs
}
Now when i am trying to retrieve the second struct of the pair from the file using the following code, i get segmentation fault. So how do i retreive an object of an arbitary struct type using fread()?
void read(){
void *record;
//read struct SType object from the file
//now read the second struct of the pair
fread(record,size,1,file);
}
You have to read into valid memory. void means "I don't know" and the system cannot and will not guess that value for you!!
What you have is:
void read(){
void *record;// This pointer currently is a random value - this will cause a fault when you try and copy data into it by calling:
fread(record,size,1,file);
}
It should be:
void read(){
void *record;
len = ?; // Your program needs to know this. You must know which structure it is somehow if you are to cast it later. Therefore you should know its size.
record = malloc(len); // Where len has been previously found to be AT LEAST big enough to read into
fread(record,size,1,file);
}
As you say your code is not psuedocode, then also put something in your structs so they're not empty. It would also be advisable to do something with the structure once you've read it, for example return the void * from your fread.
Hope that helps.
You read a record to uninitialized pointer, I guess you should alloc memory first.
void *record = maloc( size )
And do not forget to free ...
May I suggest you to use a Union?
Your type definitions will look like this:
struct SType{
int type;//struct type
};
struct S1{
};
struct S2{
};
struct S3{
};
union S{
S1 s1;
S2 s2;
S3 s3;
};
now read and write can be done like this:
SType stype;
S s;
fread(&stype,sizeof(stype),1,file);
size = ??? //get according to type
fread(&s,size,1,file);
// your data will be stored according to type in s.s1, s.s2 or s.s3
size = ??? //get according to type
fwrite(&stype,sizeof(stype),1,file);
fwrite(&s,size,1,file);
Next stage, is to unify the Type with the rest:
struct S{
int type;//struct type
union {
S1 s1;
S2 s2;
S3 s3;
}s;
};
/* in case you don't care about loosing file space,
you can read\write all in a single operation like this:
*/
S s;
fread(&s,sizeof(s),1,file);
// now according to the type you take s.s.s1, s.s.s2 or s.s.s3.
fwrite(&s,sizeof(s),1,file);
/* if you do care about loosing file space, you work like before */
S s;
fread(&s.type,sizeof(int),1,file);
size = ??? //get according to type
fread(&s.s,size,1,file);
// as before, you take s.s.s1, s.s.s2 or s.s.s3.
size = ??? //get according to type
fwrite(&s.type,sizeof(int),1,file);
fwrite(&s.s,size,1,file);

How to save a dynamic struct to file

I have something like this, in fact more complex struct than this:
typedef struct _sample {
unsigned char type;
char *name;
test *first;
} sample;
typedef struct _test {
test *prev;
test *next;
char *name;
int total;
test_2 **list;
} test;
typedef struct _test_2 {
char *name;
unsigned int blabla;
} test_2;
sample *sample_var;
I want to backup this struct into a file and after restore it.
I also try with fwrite(sample_var, sizeof(sample), 1, file_handle); but the real problem is sizeof(sample) that return wrong size, not real variable size.
There is a way to save it into file & restore without knowing the size?
You are trying to serialize, or marshal the structure. You can't just fwrite the data (having pointers is the most obvious stopper). The sizeof problem is really minor when compared to storing pointers in a file (a pointer is meaningless outside the program where it originated).
You will have to define your own serialization / deserialization functions. You could either use your own simple format or use JSON, XML, XDR or something like that.
Personally I would go with JSON, since it's all the rage these days anyway.
As an aside, here is a C FAQ vaguely linked to your own question (though it discusses interoperabillity issues).
There is no easy approach to save such a structure into a file. For instance, even the sample.name field has a size of 4 (depending on architecture), while what you probably want to save is the content of the memory pointed by sample.name.
Here is a sample code that will do such a thing. You will have to duplicate the process to save the entire structure.
void saveToFile(FILE *fh, sample s)
{
fwrite(s.type, sizeof(char), fh);
int nameSize = strlen(s.name); // get the length of the name field
fwrite(nameSize, sizeof(size_t), fh); // write the length of the name field
frwite(s.name, nameSize * sizeof(char), fh); // write the content of the name field
// continue with other fields
}
The idea is to store the size of the next structure and then writting the content. To get the information from the file, you read the size, and then get the data.
sizeof(sample) is not incorrect: it returns the size of a char followed by two pointers. If you need to save such a recursive data type, you have to manually follow dereference the pointers.
It seems like what you really want to do is store the struct and what it's pointer's are referring to, not the pointers themselves.
You will need to write some logic the determine the size of the the data being pointed at, and write that data to the file instead of the pointers.

Reading mixed data into C struct

I am trying to read mixed data into a C struct
usually, I do something like this
typedef struct data {
uint32_t value;
float x,y,z;
} __attribute__((__packed__));
and read it in like so:
data x;
fread(&x, 1, sizeof(data), filePointer);
and that works just fine for fixed length data, however, I need to load a ASCIIZ string, which is variable length, and I was wondering if there was a easy way to read that into a struct
Sorry, but there is no built-in serialization for C. This has been asked on SO before with some very good answers.
If that doesn't give you what you want, then search for C serialize or C serialization in your favorite search engine.
There are two ways you could be storing your ASCIIZ string in the structure, exemplified by:
struct asciiz_1
{
char asciiz[32];
};
struct asciiz_2
{
size_t buflen;
char *buffer;
};
The first (struct asciiz_1) can be treated the same way as your struct data; even though the string may be of variable length with garbage after the null (zero) byte, the structure as a whole is a fixed size and can be handled safely with fread() and fwrite().
The second (struct asciiz_2) is a lost cause. You have to allocate the extra space to receive the string (presumably after reading the length), and the pointer value should not be written to the file (it won't have any meaning to the reading process). So, you have to handle this differently.
Your data structure - your choice.

C: copying struct/array elements

I have a file in a known format and I want to convert it to a new format, eg.:
struct foo {
char bar[256];
};
struct old_format {
char name[128];
struct foo data[16];
};
struct new_format {
int nr;
char name[128];
struct foo data[16];
};
static struct old_format old[10];
static struct new_format new[10];
Problem: after filling 'old' with the data I don't know how to copy its content to 'new'. If I do
new[0].name = old[0].name;
new[0].data = old[0].data;
I get a compile error about assigning char * to char[128] (struct foo * to struct foo[16], respectively).
I tried a solution I found via Google for the string part:
strcpy (new[0].name, old[0].name);
new[0].data = old[0].data;
but I have no idea how to handle the struct. Seems I lack basic understanding of how to handle arrays but I don't want to learn C - I just need to complete this task.
If you don't want to learn C, you should be able to read the old file format in any language with a half-decent IO library.
To complete what you're trying to do in C, you could use memcpy.
So instead of:
new[0].data = old[0].data;
Use
memcpy(new[0].data, old[0].data, sizeof(foo) * 16);
You can also wrap the C arrays in a struct. Then copying elements will copy the array automatically.
typedef struct {
char name[100];
} name_array_t;
struct {
name_array_t name_struct;
...
} x;
struct {
name_array_t name_struct;
... other members ...
} y;
x.name_struct = y.name_struct;
(too obvious solution may be)
As we are dealing with the array, we can not do this kind of operation
new.name = old.name;
so i suppose you have to write a function
void Function (char *name , struct new_format *new );
where you need to assign charecter one by one.
Obviously you will Call like this : Function (old.name , &new)

Resources