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.
Related
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.
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.
I made a simple dictionary code, and surfed for an hour, and I found a file I/O code. But my compiler(I use Microsoft visual C++) says my code(unfortunately, the core part of the code) is wrong. but I can't get it. What is actually wrong and why???
/*
DosDic ver 1.0.0 2015-07-03
*/
#include<stdio.h>
#include<string.h>
char key = 0;
FILE *fp; //set a file pointer var
fp = fopen("dicdata.dat","r"); //open a file
int b = 0;
int trial = 0;
char result[];
char searchfor[] = fp; //save a whole list of dictionary in a var
int i;
char sb[]; //var for search in a list
int getsearchtrial(char sb[]){
for(i=0;i=strlen(sb);i++){ //how much I tried to reach to the word
switch((int)searchfor[b]-(int)sb[i]){ //are two alphabets same?
case 0 :
default : i=0;
}
b++; //keep finding in a list
trial++; //try again
}
return trial;
}
int adress;
int mainpage(){
printf("Type what you want to search : ");
scanf("%c",sb[ ]);
getsearchtrial(sb[ ]) - strlen(sb[ ]) = adress; //where the word is located in the list
for(i = adress;i = adress + 30; i++){ //print
printf("%c",searchfor[i]);
}
printf("Thank you for using DosDic ver.1.0!"); //thank you!
}
void main(){ //call all those functions
mainpage();
fclose(fp); //close list
}
//and whats wrong with this? i cant even get it, but it's not working
Multiple issues.
First of all, you can't assign the result of fopen to fp outside the body of a function; you must move fp = fopen("dicdata.dat", "r"); to within the body of one of your functions (getsearchtrial most likely).
Secondly, you don't read from a file by simply assigning a file pointer to an object; you must use a library function like fscanf or fread or fgets. Assuming your file contains a single string of length 80, you'd need to write something like
char searchfor[81] = {0}; // make sure string is initially empty,
// extra space for string terminator
if ( !fgets( searchfor, sizeof searchfor, fp) )
{
// error reading from file
}
Of course, this depends on how your input file is structured. If it contains a list of strings, then you'll need to use a multidimensional array (or some other structure).
Third, when you declare an array, must specify its size before you can use it. result and searchfor are incomplete array definitions.
Finally, this line
getsearchtrial(sb[ ]) - strlen(sb[ ]) = adress;
needs to be reversed; the target of an assignment must be on the left of the assignment operator.
You need to step back and learn how to write C code from the ground up.
There is so much wrong I'm not even going to itemise it all here - most of it seems to stem from your lack of understanding of arrays in C.
Most notably...
You can't declare an array and not initialise it or specify a size.
You can't assign a FILE * to a char array (and expect decent
results).
You can't execute a statement like fp = fopen at the
global scope like you are.
Try this tutorial and you may fix 95% of your problems, then go from there.
Today I was working on a problem of moving an array of smaller structs directly into an array of larger structs (arrayNew) (essentially upgrading the smaller structs to store more information). The smaller structs needed to be read from a HDD in one single read operation into the array of new 'upgraded' larger structs, a function would be called to do the 'upgrading'. Also all the new fields in the structs that were read from the hard drive would be set to '0'.
Other more simple solutions that I tried were:
Creating a local array of the old structures (arrayOld), loading the structures from the HDD into it then simply looping through the empty array of the new structures (arrayNew) and manually moving each structs contents from arrayOld into arrayNew. (e.g. arrayNew[i].x = arrayOld[i].x; )
The problem with this is that in my case the arrays I was working with were very large and too large for the stack ( about 1mb for each array) causing a segmentation fault the instant the upgrading function was called.
Another viable solution was to create a dynamic array of the old structures (arrayDy) and load the old structures into arrayDy and then again manually moving each structs contents from arrayDy into arrayNew. (e.g. arrayNew[i].y = arrayDy[i].y; ) This addressed the issue of running out of stack memory.
After implementing the second solution. I decided to experiment and develop a solution that uses no dynamically allocated memory and loads the array of old structures from the HHD directly into the larger array of larger structs arrayNew in one read operation and manipulate the contents of arrayNew in memory to pad out the missing values that are there due to the array being bigger.
I will post my solution below in a scaled down version of what I implemented, using the following structs for my example:
typedef struct INNER_STRUCT_ {
int i_item1;
int i_item2;
char i_item3;
} INNER_STRUCT;
typedef struct SMALL_STRUCT_ {
int item1;
char item2;
INNER_STRUCT item3;
} SMALL_STRUCT;
typedef struct BIG_STRUCT_ {
int item1;
char item2;
INNER_STRUCT item3;
INNER_STRUCT item4;
} BIG_STRUCT;
Yes, this is possible - you can use union for that. C99 standard makes a special guarantee that can be used to implement your requirement:
6.5.2.3-5: One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the complete type of the union is visible.
Your structA_ and structB_ do share a common initial sequence, so creating a union and accessing the structs through it would do the trick:
union {
structA a;
structB b;
} u;
memset(&u.b, 0, sizeof(structB)); // Zero out the bigger structB
loadFromHdd(&u.a); // Load structA part into the union
// At this point, u.b is valid, with its structA portion filled in
// and structB part zeroed out.
Note that you cannot do it to an array (unless, of course, you make an array of unions). Each structA needs to be loaded individually into the union, from which it could then be read as structB.
The method I propose and used as a solution basically loads the smaller structs for the HDD ( a file in this case) into the array of new larger structs and then rearranges the block of memory so that each field can be accessed properly. The code to illustrate this is below, and is an mcve.
#include <stdio.h>
#include <string.h>
typedef struct INNER_STRUCT_ {
int i_item1;
int i_item2;
char i_item3;
} INNER_STRUCT;
typedef struct SMALL_STRUCT_ {
int item1;
char item2;
INNER_STRUCT item3;
} SMALL_STRUCT;
typedef struct BIG_STRUCT_ {
int item1;
char item2;
INNER_STRUCT item3;
INNER_STRUCT item4;
/*
Note that the big struct is exactly the same as the small
struct with one extra field - Key to this method working
is the fact that the extension to the struct is appended
at the end, in an array of the structs will be placed one
after the other in memory with no gaps*/
} BIG_STRUCT;
void printSmallStruct (SMALL_STRUCT *inStruct, int count) {
// Print everything inside given small struct
printf("\n\n Small struct %d, item1: %d \n",count,inStruct->item1);
printf(" Small struct %d, item2: %c \n",count,inStruct->item2);
printf(" Small struct %d, item3.i_item1: %d \n",count,inStruct->item3.i_item1);
printf(" Small struct %d, item3.i_item2: %d \n",count,inStruct->item3.i_item2);
printf(" Small struct %d, item3.i_item3: %c \n",count,inStruct->item3.i_item3);
}
void printBigStruct (BIG_STRUCT *inStruct, int count) {
// Print everything inside given big struct
printf("\n\n Big struct %d, item1: %d \n",count,inStruct->item1);
printf(" Big struct %d, item2: %c \n",count,inStruct->item2);
printf(" Big struct %d, item3.i_item1: %d \n",count,inStruct->item3.i_item1);
printf(" Big struct %d, item3.i_item2: %d \n",count,inStruct->item3.i_item2);
printf(" Big struct %d, item3.i_item3: %c \n",count,inStruct->item3.i_item3);
printf(" Big struct %d, item4.i_item1: %d \n",count,inStruct->item4.i_item1);
printf(" Big struct %d, item4.i_item1: %d \n",count,inStruct->item4.i_item2);
printf(" Big struct %d, item4.i_item1: %c \n",count,inStruct->item4.i_item3);
}
int main() {
SMALL_STRUCT smallStructArray[5]; // The array of small structs that we will write to a file then read
BIG_STRUCT loadedBigStructArray[5]; // The large array of structs that we will read the data from the file into
int i; // Counter that we will use
FILE *pfile; // pointer to our file stream
void *secondary_ptr; // void pointer that we will use to 'chop' memory into the size we want
/* Fill the array of structs (smallStructArray) */
for (i = 0; i < 5; i++) {
/* We fill each field with different data do we can ID that the right data is in the right fields */
smallStructArray[i].item1 = 111;
smallStructArray[i].item2 = 'S';
INNER_STRUCT* temp = &smallStructArray[i].item3;
temp->i_item1 = 777;
temp->i_item2 = 999;
temp->i_item3 = 'I';
}
/* Write the contents of smallStructArray to binary file then display it */
pfile = fopen("test.dat","wb");
if (pfile!=NULL){
for (i = 0; i < 5; i++) {
fwrite(&smallStructArray[i],sizeof(SMALL_STRUCT),1,pfile);
}
fclose(pfile);
}
else{
printf("Unable to open file!");
return 1;
}
for (i = 0; i < 5; i++) {
printSmallStruct(&smallStructArray[i],i);
}
/* Clear array of big structs using memset */
memset(&loadedBigStructArray[0],0,sizeof(loadedBigStructArray));
/* Here we read from the smallStructArray that was aved to file into the loadedBigStructArray */
pfile = fopen("test.dat","rb");
if (pfile !=NULL){
/*
He we pass fread the following: size_t fread(void *args1, size_t args2, size_t args3, FILE *args4)
args1 - a pointer to the beginning of a block of memory, in our case the beginning of the
array loadedBigStructArray.
args2 - the size of the ammout of bytes we wish to read, in our case the size of a SMALL_STRUCT,
the size one of the elments in the array saved to the file.
args3 - the ammount of elements to read, in our case five (which is the number of elements the
array saved to the file has.
args4 - a pointer to a FILE that specifies our input stream.
Essentially what fread will do here is read a block of bytes the size of the array we saved to
the file (smallStructArray) into the array in memory loadedBigStructArray from the
beggining of loadedBigStructArray. Fig 1 illustrates what this will look like in memory.
*/
fread(&loadedBigStructArray,sizeof(SMALL_STRUCT),5,pfile);
fclose(pfile);
}
else{
printf("Unable to open file!");
return 1;
}
/*
Due to the way the array on the file has been read into the array in memory, if we try
to access the data in loadedBigStructArray only the first 5 values will be valid, due to
the memory not being in the order we want. We need to re-arrange the data in loadedBigStructArray
*/
/*
Here we use a void pointer to point to the beggining of the loadedBigStructArray.
we will use this pointer to 'chop' the data loadedBigStructArray into SMALL_STRUCT
sized 'chunks' we can read from.
Due to the way pointers and arrays work in C we can cast the void pointer to any type we want
and get a chunk of memory that size begginnig from the pointer and its off set.
E.g. : int temp = ((int *)void_ptr)[i];
This example above will give us an integer 'temp' that was taken from memory beggining from position
void_ptr in memory and its offset i. ((int *)void_ptr) casts the pointer to type int and [i] dereferances
the pointer to location i.
*/
secondary_ptr = &loadedBigStructArray;
/*
Not we are going through the array backwards so that we can rearange the data with out overwriting
data in a location that has data which we havent moved yet. As the bottom end of the loadedBigStructArray
is essentially empty we can shift data down that way.
*/
for (i = 5; i > -1; i=i-1) {
SMALL_STRUCT temp = ((SMALL_STRUCT *)secondary_ptr)[i]; // dereference pointer to SMALL_STRUCT [i] inside loadedBigStructArray call it 'temp'
/*
Now that we have dereferenced a pointer a given SMALL_STRUCT inside loadedBigStructArray called 'temp'
we can use temp to move the data inside temp to its corresponding position in loadedBigStructArray
which rearragnes the data.
*/
loadedBigStructArray[i].item1 = temp.item1;
loadedBigStructArray[i].item2 = temp.item2;
loadedBigStructArray[i].item3.i_item1 = temp.item3.i_item1;
loadedBigStructArray[i].item3.i_item2 = temp.item3.i_item2;
loadedBigStructArray[i].item3.i_item3 = temp.item3.i_item3;
/* We then fill the new field to be blank */
loadedBigStructArray[i].item4.i_item1 = 0;
loadedBigStructArray[i].item4.i_item2 = 0;
loadedBigStructArray[i].item4.i_item3 = '0';
}
/* Print our new structures */
for (i = 0; i < 5; i++) {
printBigStruct(&loadedBigStructArray[i],i);
}
return 0;
}
Visualization of technique:
When fread does the single read operation of the array saved on disk into the array in memory due to it being smaller it will take up the first potion of the array in memory but the 'bottom' section could be anything, if we try to access the data in the new array with the current handles we have on the data we will either get inaccurate information or a bad piece of memory. We have to rearrange this data before we can use any of our handles on the structs in the array.
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.