C: Saving dynamic array of structs along with string - c

I am trying to save a struct (listed)
typedef struct tupleStruct{
int element[eMax];
char * id;
int eCount;
}tuple_t;
typedef struct {
tuple_t * array;
int used;
int size;
} DynamicArray;
As part of an assignment I was instructed to save tuples that are stored in a dynamic array in a file. Unfortunately since strings don't exist in c (at least not like they do in other languages). Whenever I try to save an element of the dynamic array in a file, the string is not stored or loaded properly as it's seen as a pointer. I've even tried by initializing it like so in the struct:
char id[256];
Is there any way possible to save the struct and the string in a single file? (Given that I need to store multiple of these)
Edit: Saving and loading code
Loading
DynamicArray loadAllTuples(){
FILE *filePointer;
DynamicArray tempArray;
if((filePointer=fopen("SavedTuples.bin","rb"))==NULL)
{
fputs("Something went wrong while loading!\nA blank Array will be loaded instead\n", stderr);
setbuf(stdout, 0);
//In case of error, blank array is initalised and loaded
fclose(filePointer);
intialiseDynamicArray(&tempArray);
return tempArray;
}
fread(&tempArray, sizeof(DynamicArray),1,filePointer);
//Freeing filePointer memory
free(filePointer);
return tempArray;
}
saving
void saveAllTuples(DynamicArray ToSave){
trimArray(&ToSave,0); //Removing extra space from array
FILE * filePointer;
if((filePointer=fopen("SavedTuples.bin","wb"))==NULL)
{
fputs("Something went wrong while saving!\n", stderr);
setbuf(stdout, 0);
return;
}
fwrite(&ToSave, sizeof(ToSave), 1,filePointer);
fclose(filePointer);
}
called by
saveAllTuples(dynaArray);
and
dynaArray=loadAllTuples();

Instead of writing the whole struct in one go, write out the various parts of it utilising your knowledge of what they contain. So for example, if eCount is the amount of values in element you could write this
fwrite(&ToSave.eCount,sizeof(int), 1, filepointer);
fwrite(ToSave.element,sizeof(int), ToSave.eCount, filepointer);
and then to store the string component
size_t length=strlen(ToSave.id);
fwrite(&length,sizeof(int), 1, filepointer);
fwrite(ToSave.id,sizeof(char), length, filepointer);
Note: sizeof(char) is typically always 1, so you could assume that and put 1 rather than sizeof(char) but I find it makes the code look more uniform to leave it in.
And then you reverse the process when you do the reading
fwrite(&ToLoad.eCount,sizeof(int), 1, filepointer);
fwrite(ToLoad.element,sizeof(int), ToLoad.eCount, filepointer);
// etc...

To whom it may help in the future: So after asking around a bit, some people got it to work by treating the identifier as an array instead of a pointer.
char id[256];
I said this didn't work before, but it was probably due to another mistake which I didn't spot. Saving each tuple will keep the string identifier intact as long as an array is used.

Related

How can I write an array of struct of 20 structs despite not being entirely populated?

This is my struct:
typedef struct file {
char name[20];
int size;
int offset;
} file;
So basically I'm writing
4bytes for num of files
array of structs --> each 28 bytes
contents of files
// writing num of files
fwrite(&numFiles, sizeof(numFiles), 1, binFile);
size_t bytesW = 0;
for (int i = 0; i < MAX; i++) {
// writing structs of information
bytesW += fwrite(list, sizeof(file), 1, binFile);
}
printf("bytes written: %d\n", bytesW);
fclose(binFile);
Is it possible to write the entire array, if I only have a couple struct elements filled? I want to do this so that whenever I want to add new elements I can fseek into the end of dir with sizeof(file) * numFiles.
EDIT This is for a binary file
Yes, it is possible to write your entire array to a file even if only some of it is filled with meaningful data.
To avoid undefined behavior, I recommend that you at least initialize everything in the array before writing it to the file. You can simply run memset after allocating the memory for the array in order to initialize everything to 0, or if your array is allocated as a global variable then it will get initialized to 0 automatically.
The code you provided looks like it just writes the same element every time. Maybe you meant to use &list[i] instead of list.

Stack smashing detected during fread on binary file in C

UPDATE IN BOTTOM====
So a while ago I made the following function, which I successfully used to get the grey values from images (w x h dimension) that were converted to .bin-files. It just gives an array of all pixel values.
It was, however, not as a function like this but put in the main() immediately.
// read the BIN-file as grayscale image
void decodeBIN(const char* filename, short image[], int w, int h){
int i = 0;
unsigned char buffer[16]; // no specific size attributed
FILE *ptr;
ptr = fopen(filename, "rb");
if (!ptr){
printf("\nUnable to open file!\n"); // error
}
while (!feof(ptr)){
fread(buffer,2,1,ptr); // read w bytes to buffer
image[i] = buffer[1];
//printf("%u ", image[i]); // DEBUG
i++;
}
fclose(ptr);
printf("\nBinary image read (npixels: %i).\n", i-1); // DEBUG
}
I decided to expand the code, so I rewrote it to the previous function and put it in a separate file for functions and also made a header file. The extra file for functions and the header file work 100% so that's not the issue. Now, this code does not work anymore and I get a stack smashing error. Some variables called after this function have also jumped to another value, so I figured the problem was with the buffer (I didn't know about the correct size for the buffer, but it worked...). After some experimentation and testing, I came up with the following function. I replaced the buffer with a char array named image2 to simply try and test it:
void decodeBIN(const char* filename, short image[], int w, int h){
int i = 0, res;
char image2[];
FILE *ptr;
ptr = fopen(filename, "rb"); //"MySnap_20180327-2239-010.bin"
if (!ptr){
printf("\nUnable to open file!\n"); // error
}
res = fread(image2,1,w*h,ptr) // need to read w*h pixels
while (i < w*h){ // DEBUG
printf("%i ", (int)image2[i]); // DEBUG
i++;
}
printf("\nRead %u bytes\n", res); // DEBUG
fclose(ptr);
printf("Binary image read (npixels: %i).\n", i); // DEBUG
}
I'm a bit lost in how it used to work and all of a sudden when I move the code from main() to a function it stops working, so any help is welcome!
Thanks in advance.
Disclaimer: I'm aiming to write this with the help of as few libraries as possible
===== UPDATE:
After the answer of #alainmerigot I got this code, which helped with getting the correct values:
void decodeBIN(const char* filename, unsigned char image[], int w, int h){
int i = 0, res;
FILE *ptr;
res = fread(image,sizeof(char),w*h,ptr) // need to read w*h pixels
fclose(ptr);
}
The segmentation fault and jumped variables are still in place though, so here a more upper-level oversight of what I'm doing:
char filenamePathed["[path of file]/file.bin"];
short img1[npixels]; // npixels = w*h
printf("i_file: %i\n", i_file); // correct value
decodeBIN(filenamePathed, img_curr, w, h); // decode bin
printf("i_file: %i\n", i_file); // value jumped
while (i < npixels){
img1[i] = (short)img_curr[i];
i++;
}
Perhaps it is good to know that I'm doing this iteratively for multiple files (time series)? I also need it to end up in a (short) format (or integer, but eventually needs to be memory-efficient and pixels have a range of 0-255 so int is a bit abundant imo).
The problem with your second function is that you write in array image2 while no space has been reserved for it. Declaring char image2[]; only says that an array exists and that the address of this array can be found in var image2, but no space is associated with it, hence the problem.
You can associate space with this array by several means.
Using permanent storage in the heap
image2 = malloc(x*y); // but do not forget to free(image2) at the end of the function
Using temporary storage in the stack (space is automatically freed when leaving the function).
image2 = alloca(x*y); // slightly faster than malloc and do not require to free() the image
But the best is to use a array with parametrized size (since C99). Your array should be declared as
char image2[w*h]; // will use the value of w and h to define array size
If you want to do other things than printing the image values in your function, you should store the image in permanent memory and have a mean to know the address of the array in your program. This is probably what you intended and is the reason why you have short image[] in your parameter list.
The solution is just simply to use image instead of image2 in fread().
But, the declaration of image should be coherent and image should be an array of char not short.
Beware also of declarations. In your first function, the image is an array of unsigned char and in the second an array of char. While the storage size is identical and fread() will store the same values, they are not equivalent. If used in an arithmetic context, image[i] will be interpreted differently and the results will likely be different. In general, images are unsigned.
Apparently, the problem was with the allocation of image, although I'm not sure why it was wrong.
I used to allocate it with unsigned char image[npixels]; and the solution to the error appeared to be unsigned char image[npixels*7];
Somehow it works, but if anyone has an explanation, please do so :)

C unknown number of structures in file

Similar with this. But what if MAX_BOOKS would be unknown as well?
I want to get number of structures from a file.
My structure:
typedef struct material {
int mat_cislo;
char oznaceni[MAX_TEXT];
char mat_dodavatel[MAX_TEXT];
char dodavatel[MAX_TEXT];
float cena;
int mat_kusovnik;
} MATERIAL;
My code:
void nacist_material() {
FILE* pSoubor;
MATERIAL materialy_pocitadlo;
int i;
int b;
if((pSoubor = fopen(SOUBOR_MATERIAL, "rb")) == NULL ) {
printf("\nChyba při čtení souboru");
return;
}
pocet_zaznamu_materialu = 3;
printf("\n\n===>%d", pocet_zaznamu_materialu);
if(pocet_zaznamu_materialu > 0) {
printf("\nExistuje %d materialu", pocet_zaznamu_materialu);
free(pMaterialy);
pMaterialy = (MATERIAL *) malloc(pocet_zaznamu_materialu * sizeof(MATERIAL));
for(i = 0; i < pocet_zaznamu_materialu; i++) {
b = fread(&pMaterialy[i], sizeof(MATERIAL), 1, pSoubor);
}
printf("\n otrava %d", b);
}
else {
printf("\nNeexistuje předchozí záznam materialu");
}
fclose(pSoubor);
return;
}
Right now pocet_zaznamu_materialu is hard code to 3, because there are 3 structures in a file and it all works correctly. But what if number of structures in file changes?
Problem: I need to know - number of structures in file, how to a do it?
Thanks, sorry for eng
If the file is composed of nothing but a list of your desired struct stored contiguously, then the file's size, in bytes, will be a multiple of the size of your struct, and you can obtain the file size and then the number of structs in the file like so:
size_t len_file, num_structs;
fseek(fp, 0, SEEK_END);
len_file = ftell(fp);
rewind(fp);
num_structs = len_file/sizeof(MYSTRUCT);
This can be a real problem when you read from a dynamic file (another program writes at the end of file while you read it), a pipe or a network socket. In that case, you really have no way to guess the number of structs.
In that case, a common idiom is to use a dynamicaly allocated array of structs of an arbitrary size and then make it grow with realloc each time the currently allocated array is full. You could for example make the new size be twice the previous one.
That is the way C++ vectors manage their underlying array under the hood.
Have you considered adding a header to the file?
That is, place a special structure at the start of the file that tells you some information about the file. Something like ...
struct file_header {
char id[32]; /* Let this contain a special identifying string */
uint32_t version; /* version number in case the file structure changes */
uint32_t num_material; /* number of material structures in file */
};
Not only does this give you a relatively quick way to determine how many material structures you have in your file, it is also extensible. Perhaps you will want to store other structures in this file, and you want to know how many of each are in there--just add a new field and update the version.
If you want, you can even throw in some error checking.

Coredump in fread when pointing to file in array

Recently started working with pointers and have created a little script that is supposed to stich together some textfiles.
However when i try to call fputs i get a coredump/segmentation error. I suspect it is because of the way that the file pointer is saved. I find the files saves it in an array and tries to retrieve it later on.
the FILE pointer is saved in a struct. Does somebody instantly spot my fault? i would be very grateful!
The struct:
typedef struct{
int listSize;
int listCapacity;
FILE *fileStream;
}FileList;
Creating the struct
FileList fileList;
fileList.listSize=0;
fileList.listCapacity=1;
fileList.fileStream=calloc(fileList.listCapacity,sizeof(FILE));
and then i add the struct to the array by calling
void addFile(FileList* list, FILE* file)
{
list->fileStream[list->listSize]=*file;
}
However when i call
char* buffer[10];
size_t result=0;
result = fread(buffer,1,10,&fileList.fileStream[ii+currentGroupOffset]);
fputs(*buffer,outPutFile);
it crashes, i tried to watch the value ii+currentGroupOffset making sure it doesnt go out the array bounds
any help at all appriciated! :)
You can't allocate and copy around FILE structures yourself - it's an opaque data type. So, instead of creating an array of FILE structures, create an array of FILE * pointers:
typedef struct {
int listSize;
int listCapacity;
FILE **fileStream;
} FileList;
FileList fileList;
fileList.listSize = 0;
fileList.listCapacity = 1;
fileList.fileStream = calloc(fileList.listCapacity, sizeof fileList.fileStream[0]);
then add a FILE * pointer to the array by copying the pointer value:
void addFile(FileList *list, FILE *file)
{
list->fileStream[list->listSize] = file;
}
and use it like so:
char buffer[10];
size_t result = 0;
result = fread(buffer, 1, 10, fileList.fileStream[ii+currentGroupOffset]);
fwrite(buffer, 1, result, outPutFile);
It seems you want a dynamically allocated array of FILE* elements. You have:
FILE *fileStream;
That can either be treated as a FILE pointer, or an array of FILE elements. But not as an array of FILE pointers. For that, you need:
FILE **fileStream;
And allocating the array should be done with:
fileList.fileStream=calloc(fileList.listCapacity,sizeof(FILE*));
FILE is not a type you use directly. You always deal with pointers to it. You should treat it as an opaque type.
Also, I don't see where you actually open the files (using fopen()) anywhere in your code.
Why
char * buffer[10];
It should be
char buffer[10];
Where is list->listSize incremented?
I don't understand what this is
fileList.fileStream=calloc(fileList.listCapacity,sizeof(FILE));
FILE *s are initialized by calling fopen not by allocating memory

Reading file and populating struct

I have a structure with the following definition:
typedef struct myStruct{
int a;
char* c;
int f;
} OBJECT;
I am able to populate this object and write it to a file. However I am not able to read the char* c value in it...while trying to read it, it gives me a segmentation fault error. Is there anything wrong with my code:
//writensave.c
#include "mystruct.h"
#include <stdio.h>
#include <string.h>
#define p(x) printf(x)
int main()
{
p("Creating file to write...\n");
FILE* file = fopen("struct.dat", "w");
if(file == NULL)
{
printf("Error opening file\n");
return -1;
}
p("creating structure\n");
OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
myObj->a = 20;
myObj->f = 45;
myObj->c = (char*)calloc(30, sizeof(char));
strcpy(myObj->c,
"This is a test");
p("Writing object to file...\n");
fwrite(myObj, sizeof(OBJECT), 1, file);
p("Close file\n");
fclose(file);
p("End of program\n");
return 0;
}
Here is how I am trying to read it:
//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{
FILE* file = fopen("struct.dat", "r");
char* buffer;
buffer = (char*) malloc(sizeof(OBJECT));
if(file == NULL)
{
p("Error opening file");
return -1;
}
fread((void *)buffer, sizeof(OBJECT), 1, file);
OBJECT* obj = (OBJECT*)buffer;
printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
obj->a,
obj->f,
obj->c);
fclose(file);
return 0;
}
When you write your object, you're writing the pointer value to the file instead of the pointed-to information.
What you need to do is not just fwrite/fread your whole structure, but rather do it a field at a time. fwrite the a and the f as you're doing with the object, but then you need to do something special with the string. Try fwrite/fread of the length (not represented in your data structure, that's fine) and then fwrite/fread the character buffer. On read you'll need to allocate that, of course.
Your first code sample seems to assume that the strings are going to be no larger than 30 characters. If this is the case, then the easiest fix is probably to re-define your structure like this:
typedef struct myStruct{
int a;
char c[30];
int f;
} OBJECT;
Otherwise, you're just storing a pointer to dynamically-allocated memory that will be destroyed when your program exits (so when you retrieve this pointer later, the address is worthless and most likely illegal to access).
You're saving a pointer to a char, not the string itself. When you try to reload the file you're running in a new process with a different address space and that pointer is no longer valid. You need to save the string by value instead.
I would like to add a note about a potential portability issue, which may or may not exist depending upon the planned use of the data file.
If the data file is to be shared between computers of different endian-ness, you will need to configure file-to-host and host-to-file converters for non-char types (int, short, long, long long, ...). Furthermore, it could be prudent to use the types from stdint.h (int16_t, int32_t, ...) instead to guarantee the size you want.
However, if the data file will not be moving around anywhere, then ignore these two points.
The char * field of your structure is known as a variable length field. When you write this field, you will need a method for determining the length of the text. Two popular methods are:
1. Writing Size First
2. Writing terminal character
Writing Size First
In this method, the size of the text data is written first, followed immediately by the data.
Advantages: Text can load quicker by block reads.
Disadvantages: Two reads required, extra space required for the length data.
Example code fragment:
struct My_Struct
{
char * text_field;
};
void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
size_t text_length = strlen(p_struct->text_field);
fprintf(output, "%d\n", text_length);
fprintf(output, "%s", p_struct->text_field);
return;
}
void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
size_t text_length = 0;
char * p_text = NULL;
fscanf(input, "%d", &text_length);
p_text = (char *) malloc(text_length + sizeof('\0'));
if (p_text)
{
fread(p_text, 1, text_length, input);
p_text[text_length] = '\0';
}
}
Writing terminal character
In this method the text data is written followed by a "terminal" character. Very similar to a C language string.
Advantages: Requires less space than Size First.
Disadvantages: Text must be read one byte at a time so terminal character is not missed.
Fixed size field
Instead of using a char* as a member, use a char [N], where N is the maximum size of the field.
Advantages: Fixed sized records can be read as blocks.
Makes random access in files easier.
Disadvantages: Waste of space if all the field space is not used.
Problems when the field size is too small.
When writing data structures to a file, you should consider using a database. There are small ones such as SQLite and bigger ones such as MySQL. Don't waste time writing and debugging permanent storage routines for your data when they have already been written and tested.

Resources