This question already has answers here:
Writing and reading (fwrite - fread) structures with pointers
(3 answers)
Closed 8 years ago.
I tried to write and read from a file with pointers in structures. But when I read from file I see some garbage value. I am using GCC 4.7.2 on Linux. Need some help.
Read:
//read from a file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[14];
FILE * fp = NULL;
fp = fopen("/media/Study/PhoneDirectory.dat","rb");
if(fp == NULL)
printf("Error opening file!!!");
fseek(fp,0,SEEK_SET);
if(fread(&phoneList[1],sizeof(LISTING),1,fp)==1)
printf("%s %s",phoneList[1].name,phoneList[1].phone);
fclose(fp);
return 0;
}
And write:
//Write to file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[2];
FILE * fp = NULL;
fp = fopen("/media/Study/PhoneDirectory.dat","wb");
phoneList[1].name = "Santosh";
phoneList[1].phone = "9657681798";
if(fwrite(&phoneList[1],sizeof(LISTING),1,fp)==1)
printf("inserted");
fclose(fp);
return 0;
}
Pointers are only meaningful in the application process that they originate from. If you write them to a file, as you're doing here, the values you read back will be meaningless — they will most likely point to uninitialized memory, or to memory which is being used for something else entirely.
You will need to come up with another way of writing this data to a file.
The problem you have is equivocating between char* and char[]. You can certainly assign a string literal to a char*, but you need to understand what the contents of a LISTING structure contain, and how you want to serialize and deserialize data to a file.
It does not make sense to save pointers from one process and read them into another process, so you probably want to save the contents (what a pointer points at). You want to store two values, (name, phone) to the file. Since you likely want to store the literal name and literal phone, let us consider what the file might look like:
roast duck|212-333-4444
peking duck|411-511-61111
duck soup|314-222-3333
free duck|800-111-2222
...
You need functions to serialize and deserialize your data. Since your LISTING type is pointers, you will need to allocate appropriate space for those values, as you read them, and you need functions (methods) to read serialized data from a file and write serialized data to a file.
Reading (you will need to allocate enough space),
int
listing_read(FILE*fp, LISTING* listing)
{
char name_buffer[100];
char phone_buffer[100];
if(!fp) return(-1);
if(!listing) return(-2);
int res = fscanf(fp,"%s|%s\n",name_buffer,phone_buffer);
if( !res ) {
//handle error here
}
//careful here, you cannot free if you didn't malloc/strdup
if(listing->name) free(listing->name);
if(listing->phone) free(listing->phone);
listing->name = strdup(name_buffer);
listing->phone = strdup(phone_buffer);
return(0);
}
Writing (you will need to provide proper formatting),
int
listing_write(FILE*fp, LISTING* listing)
{
if(!fp) return(-1);
if(!listing) return(-2);
fprintf(fp,"%s|%s\n",listing->name,listing->phone);
return(0);
}
Here is how you need to modify your code,
//read from a file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[14];
FILE* fp = NULL;
if( !(fp = fopen("/media/Study/PhoneDirectory.dat","rb")) ) {
printf("Error opening file!!!");
exit(1);
}
fseek(fp,0,SEEK_SET);
if( listing_read(fp,&phoneList[0]) >= 0 ) {
printf("%s %s",phoneList[0].name,phoneList[0].phone);
}
fclose(fp);
return 0;
}
And here is how writing the file would change,
//Write to file
#include<stdio.h>
typedef struct
{
char* name;
char* phone;
}LISTING;
int main(void)
{
LISTING phoneList[14];
FILE* fp = NULL;
if( !(fp = fopen("/media/Study/PhoneDirectory.dat","wb")) ) {
printf("error, cannot write file\n");
exit(1);
}
phoneList[0].name = "Santosh";
phoneList[0].phone = "9657681798";
if( listing_write(fp,&phoneList[0])>=0) {
printf("inserted");
}
fclose(fp);
return 0;
}
Note that in you writing program you assign the string literals "Santosh" and "9657681798" to the LISTING members name and phone. Though legal to do, you need a better understanding of what C does here. C takes the address of these C-string constants and assigns those addresses to the phonelist[1].name and phonelist[1].phone member pointers.
Consider that if you did this assignment,
phoneList[0].name = "Santosh";
phoneList[0].phone = "9657681798";
You have assigned the pointers to constant strings to your structure members.
But if you were to allocate space (for example, using strdup()),
phoneList[0].name = strdup("Santosh");
phoneList[0].phone = strdup("9657681798");
You have allocated space for the strings, assigning independent locations for these member elements. Which is is more likely what you want to do.
Note that I used phonelist[0] since C has zero-based arrays.
printf("%s %s",phoneList[1].name,phoneList[1].phone);
The above statement invokes undefined behaviour.
Since the pointers name & phone of struct object phoneList[1] are not initialized dereferencing them invokes UB. In your case they are throwing out garbage values but it could have lead to a crash also.
To fit your case of reading the contents of file and storing it in the struct objects use getline function to read them row-wise(assuming that all the details are stored line-wise) and then dynamically allocate the memory for char pointers then assign them to the read value. But, this approach leads to lot of memory management which is error prone.
Related
This is my first post at stack overflow, and hope someone is able to point me in the correct direction. I am writing a C funtion where my goal is to read a csv file. The data in the file is then passed to an structure array, which I then would like to return to a function call in main(), accessing the data for further use. How do I properly read and then return the complete structure array?
This function is an addon to an existing PLC program, where at the moment all system parameters is stored in retain memory. The goal is to read/write parameters to a CSV file for backup.I suspect that I am doing something wrong in the while loop, but are at this point not able to figure out what. It could also be I am not using pointers correctly. The CSV file looks like this:
2;motor nominal current;1700
3;motor nominal speed;2500.0
4;motor nominal power;1200.0
5;motor nominal voltage;690.0
6;Enable motor heating;TRUE
7;Motor heating time on;40.0
I am by the way aware that I don`t free the memory allocated in the function. This will be handled further on.
Here is the program containing the function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 80
struct parameter{
int id;
char *name;
char *value;
};
struct parameter* readCSV(const char *file)
{
char buffer[BSIZE];
FILE *f;
char *field;
// open the CSV file
f = fopen(file,"r");
if( f == NULL)
{
printf("Unable to open file '%s'\n",file);
exit(1);
}
static struct parameter *parameters[BSIZE];
int i = 0;
// read the data
while(fgets(buffer,BSIZE,f) != NULL)
{
parameters[i] =(struct parameter*)malloc(sizeof(struct parameter));
// get id
field = strtok(buffer,";");
parameters[i]->id = atoi(field);
// get name
field = strtok(NULL,";");
parameters[i]->name = field;
// get value
field = strtok(NULL,";");
parameters[i]->value = field;
// display the result
printf("ID%d:\t%s\t%s\n",parameters[i].id, parameters[i].name, parameters[i].value);
i++;
}
//close file
fclose(f);
return *parameters;
}
int main()
{
struct parameter *parameters;
parameters = readCSV("QD_Config.csv");
printf("ID%d:\t%s\t%s\n",parameters[0]->id, parameters[0]->name, parameters[0]->value);
return(0);
}
I am able to print the contents of the file, but am not able to properly store the structure array before passing it it seems. In main(), when calling the function, I only get the last name and value in the file, but with the ID number of the first.
The problem you (probably) have is that the strtok function returns a pointer to the string you're tokenizing. It does not create a new string for you.
That means e.g.
field = strtok(NULL,";");
parameters[i]->name = field;
will make parameters[i]->name point to some character in buffer. And once the function readCSV returns the variable buffer ends its life-time and ceases to exist, leaving you with invalid pointer.
You need to allocate memory for the strings yourself and copy the data to them. This is either done by making the structure members arrays and use strcpy to copy the string to these arrays, or by using the non-standard but commonly available strdup function (which allocates memory dynamically of the heap and copies the string to it).
There's another problem related to returning your structures:
return *parameters;
is equal to
return parameters[0];
That is, you return a pointer to a single parameter structure.
If you want to return the whole array, you should do
return parameters; // Return the whole array
But note that it will decay to a pointer to its first element (i.e. ¶meters[0]) which have the type struct parameter **, so you need to adjust the return type appropriately.
You also need to initialize parameters to null-pointers, or otherwise it's going to be hard to find the end of the array:
static struct parameter *parameters[BSIZE] = { NULL };
However a better solution which I rather recommend, is that you pass in the array as an argument, and return the number of elements you fill in. Then you can use an array of structure objects (instead of an array of structure pointers) and don't have to do any dynamic allocation and risk memory leaks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 80
struct parameter{
int id;
char *name;
char *value;
struct parameter *next;//If you dont sure how many lins in csv you need this
};
typedef struct parameter parameter;
//I'm lazy too type struct
parameter* CreateNewQ_Q(){
parameter *Q_Q=(parameter*)malloc(sizeof(parameter));
Q_Q->name=NULL;//Nothing at first
Q_Q->value=NULL;//Nothing at first
Q_Q->next=NULL;//Nothing at first
return Q_Q;
}
void readCSV(const char *file,parameter *Q_Q)
{
char buffer[BSIZE];
FILE *f;
char *field;
parameter* A_A=Q_Q;
// open the CSV file
f = fopen(file,"r");
if( f == NULL)
{
printf("Unable to open file '%s'\n",file);
exit(1);
}
// read the data
while(fgets(buffer,BSIZE,f) != NULL)
{
if(A_A->next==NULL){//Next Nothing So Create after it
A_A->next=CreateNewQ_Q();
}
A_A=A_A->next;//A_A is New A_A now
// get id
field = strtok(buffer,";");
A_A->id = atoi(field);
// get name
field = strtok(NULL,";");
//Q_Q
//<--------Here alloc memory for your name because strtok not alloc new memory it just return a pointer in buffer[?]-------------->
A_A->name=(char *)malloc((sizeof(strlen(field)+1)*sizeof(char)));//+1 Becuz '\0' at end of string is necessary
//<--------Here Copy Result-------------->
strcpy(A_A->name, field);
//Q_Q
// get value
field = strtok(NULL,";");
//Q_Q
//<--------Here alloc memory for your value because strtok not alloc new memory it just return a pointer in buffer[?]-------------->
A_A->value=(char *)malloc((sizeof(strlen(field)+1)*sizeof(char)));//+1 Becuz '\0' at end of string is necessary
//<--------Here Copy Result-------------->
strcpy(A_A->value, field);
//Q_Q
// display the result
printf("ID%d:\t%s\t%s\n",A_A->id, A_A->name, A_A->value);
}
//close file
fclose(f);
}
void DeleteAllQ_Q(parameter *Q_Q){
if(Q_Q->next){
DeleteAllQ_Q(Q_Q->next);
Q_Q->next=NULL;
}else{
free(Q_Q->name);//I dont have next so i'm free
free(Q_Q->value);
free(Q_Q);
}
}
int main()
{
//memory control is important!!!!!!!!!!!!!!
parameter *parameters=CreateNewQ_Q();
readCSV("QD_Config.csv",parameters);
printf("Ok Load Done A_A\n");
for(parameter *loopQ_Q=parameters->next;loopQ_Q!=NULL;loopQ_Q=loopQ_Q->next){
printf("ID%d:\t%s\t%s\n",loopQ_Q->id, loopQ_Q->name, loopQ_Q->value);
}
DeleteAllQ_Q(parameters);//free parameters's next and next's next and....
free(parameters);//free self
return(0);
}
I spend sometime, i think this method control memory is more safe!
I have this struct.
Struct Wizard{
char name[50];
int maxHealth;
int attackMin, attackRange;
int curHealth, winCount;
};
And I need to make a function save the stats to a txt file.
I was given this bit of code to start it.
int saveWiz(struct Wizard * wiz)
{
File * fp = fopen("champion.txt","w");
char * buff = malloc(100);
sprintf(buff,... );
sprintf(buff,...);
//Todo: replace ... with appropriate string, using tags like %d for variables
fputs(buff,fp);
fclose(fp);
}
Any help would be much appreciated. I think I am supposed to use the wiz pointer to access the struct but I'm not exactly sure.
I don't know if I understand exactly what you are asking but I'm guessing it's this:
You have a wiz variable of type struct Wizard * . You access the elements like this : wiz->name , wiz->maxHealth and so on,
wiz->name is equivalent to (*wiz).name . So you'll have :
int saveWiz(struct Wizard * wiz) //assuming you return 1 for success 0 for failure
{
File * fp = fopen("champion.txt","w");
if(!fp) {
//file opening failed
return 0;
}
char * buff = malloc(200); // 200 to be sure
if(buff) {
sprintf(buff,"%s",wiz->name);
sprintf(buff,"%d %d %d %d %d" , wiz->maxHealth, wiz->attackMin, wiz->attackRange, wiz->curHealth, wiz->winCount);
fputs(buff,fp);
fclose(fp);
free(buff);
return 1;
}
else {
//memory allocation failed
return 0;
}
}
Also as Martin James pointed out it is best to free dynamically allocated memory after you're done using it ( otherwise causes ugly memory leaks on long-running programs) . On that note , it's also good to check if the file opening was successful as well as the memory allocation. Also another thing I noticed is the number of elements in the buff array , 100 might not be enough ( considering an int is 32 bit (10 digits) and the name has a full 50 characters, along with the spaces it might cause an overflow).
I want to ultimately insert strings from a file into elements in structs and can't get it right. Can you see what is wrong here?
int main()
{
FILE *fp;
char file_name[10] = "map7.map";
fp = fopen(file_name,"r");
int a = 1;
int *pnumberOfRows = &a;
fscanf(fp,"%d",pnumberOfRows);
typedef struct {
bool visited;
char *cityName;
} map;
map *ver = malloc(sizeof(map)*2*(*pnumberOfRows));
fscanf(fp,"%s",ver[1].cityName);
printf("%s",ver[1].cityName);
return 0;
}
It seems like you're simply missing to allocate space for the char *cityName fields, which makes you fscanf onto an unallocated pointer. You could either provide a fixed-with field, e.g.
typedef struct {
bool visited;
char cityName[81];
} map;
for a maximum length of 80 characters (i.e. excluding \0) or determine the length of the city names in the file beforehand and then allocating memory to the field using
ver[0]->cityName = (char*)malloc(sizeof(char)*(stringLength+1));
Note that sizeof(char) == 1, so feel free to leave it away, but see the answers here for more information. I left it here for the sake of being expressive about what you want to achieve.
Also, don't forget to free the memory you malloc'd at the end and also close the file descriptor after you're done (i.e. fclose(fp);).
student programmer here. I am having trouble reading input from a binary file in C. The data stored in the file are structs that look like this:
typedef struct reserve{
char *name;
char *ic;
int room_number;
} Reserve;
I get no problem when I write the struct to a binary file. Here is the code I did for writing to the file:
void reg_new() //Make a new reservation
{
Reserve newReserve = {"", "", 0};
char name[NAMEFIELD], ic[ICFIELD];
int room_number;
FILE *resvptr;
scanf_s("%s", name, NAMEFIELD);
scanf_s("%s", ic, ICFIELD);
scanf_s("%d", &room_number);
newReserve.name = name;
newReserve.ic = ic;
newReserve.room_number=room_number;
resvptr = fopen("reservations.dat", "wb");
fwrite(&newReserve, sizeof(Reserve), 1, resvptr);
fclose(resvptr);
}
And the code for reading from the file:
void reg_view() //view a reservation.
{
FILE *seekptr;
Reserve viewReserve = {"", "", 0};
int read;
if ( (seekptr = fopen("reservations.dat", "rb")) == NULL)
{
puts("Error: file could not be found.");
}
else
{
while ( !feof(seekptr))
{
read = fread(&viewReserve, sizeof(Reserve), 1, seekptr);
if(read != 0)
{
printf("Name: %s\nIC number: %s\nRoom Number: %d\n", viewReserve.name, viewReserve.ic, viewReserve.room_number);
}
}
fclose(seekptr);
}
Immediately after writing to the binary file and accessing the function for reading, the room number (of int value) reads fine, but the two strings (name and IC number) read out as garbage characters. Subsequently, closing and restarting the program, then attempting to read from the file will give me an access violation and bad pointer error.
The trouble is with reading the strings from the file. I'm suspecting a memory leak but I can't say for sure...can anyone help? Thanks.
You are writing pointers to the file, not the actual data. When you read the pointer from the file, it's just a memory address that you probably don't have access to, even if you do it in the same process that wrote the file (because it was actually a pointer to a function-scoped stack variable).
If you want to store the records in that way, make your function store the actual string data rather than the pointer:
typedef struct reserve{
char name[NAMEFIELD];
char ic[ICFIELD];
int room_number;
} Reserve;
And just do this:
scanf_s("%s", newReserve.name, NAMEFIELD);
scanf_s("%s", newReserve.ic, ICFIELD);
scanf_s("%d", &newReserve.room_number);
The structure consists of the integer and the addresses of two arrays (pointers). When you write it out and read it back, you are reading and writing the addresses of the character arrays - not their contents. You need to modify your routine to write and read the contents (including lengths) and also to allocate the storage to hold the contents when you read them back in.
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.