Fwrite/Fread a dynamically declared struct ** - c

I would like to fwrite() then fread() the following struct ** whose memory has been allocated dynamically.
This struct is declared as following :
typedef struct {
int num;
char type;
Entity entity;
}Cell;
I declare a map of cell as following :
typedef struct {
char name[MAX_STRING];
int width;
int height;
Cell** map;
}Maze;
I allocate the map's memory as following :
maze.map = (Cell **)malloc( width*sizeof( Cell* ));
for (int x = 0; x < width; x++ )
{
maze.map[x] = (Cell *)malloc(sizeof( Cell )*height);
}
The thing is is i can't fwrite()/fread() my struct like this because once I would like to fread() the struct, I would not be able to allocate the proper memory space.
So i decided to write the height and width first then allocate the memory then read the map.
But I can't find how to write or read my map properly.
I tried :
for (int x = 0; x < maze.width; ++x) {
for (int y = 0; y < maze.height; ++y) {
fwrite(&maze.map[x][y], sizeof(Cell), 1, file);
}
}
But it doesn't work, otherwise i don't know how should I write/read my map.
Everything i tried doesn't give me back the map i wrote or it just crash saying me : Process finished with exit code -1073741819 (0xC0000005)
Here are the two writing and readin fuction :
void SaveMaze(Maze maze){
FILE *file;
char file_name[MAX_STRING];
strcpy(file_name, maze.name);
strcat(file_name,".save");
file = fopen(file_name, "w");
if(file == NULL)
{
perror("Error Fopen : ");
return ;
}
fwrite(maze.name,sizeof(maze.name), 1, file);
fwrite(&maze.height,sizeof(maze.height), 1, file);
fwrite(&maze.width,sizeof(maze.width), 1, file);
for (int x = 0; x < maze.width; ++x) {
fwrite(&maze.map[x], sizeof(Cell), maze.height, file);
}
// Close the file
fclose(file);
}
Maze LoadMaze(char * name){
FILE *file;
Maze maze;
char file_name[MAX_STRING];
strcpy(file_name, name);
strcat(file_name,".save");
file = fopen(file_name, "r");
if(file == NULL) {
perror("Error Fopen : ");
return maze;
}
fread(maze.name,sizeof(maze.name), 1, file);
fread(&maze.height,sizeof(maze.height), 1, file);
fread(&maze.width,sizeof(maze.width), 1, file);
maze.map = (Cell **)malloc( maze.width*sizeof( Cell* ));
for (int x = 0; x < maze.width; x++ )
{
maze.map[x] = (Cell *)malloc(sizeof( Cell )*maze.height);
}
for (int x = 0; x < maze.width; ++x) {
fread(&maze.map[x], sizeof(Cell), maze.height, file);
}
printf("%c",maze.map[0][0].num);
fclose (file);
return maze;
}
int main() {
int choice;
int width,height;
char name[MAX_STRING],file_name[MAX_STRING];
Maze maze;
do{
printf("1. Generate a new Maze\n");
printf("2. Load an existing maze\n");
printf("3. Play\n");
printf("4. Exit\n");
scanf("%d",&choice);
fflush(stdin);
if(choice == 1){
do {
printf("What is the width of your maze (Odd number only)\n");
scanf("%d", &width);
fflush(stdin);
} while (width%2 == 0);
do {
printf("What is the height of your maze (Odd number only)\n");
scanf("%d", &height);
fflush(stdin);
} while (height%2 == 0);
printf("What is the name of the maze\n");
fgets(name,sizeof(name),stdin);
// Remove the \n from the name
name[strcspn(name, "\n")] = 0;
fflush(stdin);
maze = CreateMaze(width,height,name);
InitialyzeMaze(&maze);
BuildMaze(&maze);
fflush(stdin);
SaveMaze(maze);
}else if(choice == 2){
//system("clear");
//system("ls *.{save}");
printf("What is the name of the maze you want to load ?\n");
fgets(file_name,sizeof(file_name),stdin);
// Remove the \n from the filename
file_name[strcspn(file_name, "\n")] = 0;
fflush(stdin);
maze = LoadMaze(file_name);
}else if(choice == 3){
Play(&maze);
}
}while(choice != 4);
}

You're mixing text data and binary data in your data file.
When you write the name, height, and width:
fprintf(file,"%s",maze.name);
fwrite(&maze.height,sizeof(maze.height), 1, file);
fwrite(&maze.width,sizeof(maze.width), 1, file);
This outputs a series of characters for the name (let's say "my_maze") followed immediately by sizeof(int) bytes for the height and sizeof(int) bytes for the width.
So that's 7 bytes for the name, 4 bytes for height (assuming an int is 4 bytes), and 4 bytes for width.
Now when you read back:
fscanf(file,"%s",maze.name);
fread(&maze.height,sizeof(maze.height), 1, file);
fread(&maze.width,sizeof(maze.width), 1, file);
The %s format specifier to fscanf reads characters until it encounters whitespace. The first 7 characters get read in correctly but right after that is binary data for height so where does it stop reading? The result is that you most likely read more bytes than you indented to and now the rest of your reads are not in the correct place.
You can fix this by doing away with fprintf and fscanf by writing the entire name field with fwrite:
fwrite(maze.name,sizeof(maze.name), 1, file);
And reading it with fread:
fread(maze.name,sizeof(maze.name), 1, file);
You also have a problem here:
fwrite(&maze.map[x], sizeof(Cell), maze.height, file);
And here:
fread(&maze.map[x], sizeof(Cell), maze.height, file);
&maze.map[x] is not the address of the memory you allocated but the address of the pointer it points to. So rather than reading/writing the memory set aside for each row of cells you're reading/writing the memory used for the row of pointers for each cell. You end up reading/writing past the end of allocated memory when you do this.
Get rid of the address-of operator here to pass in the pointer to the actual memory you're reading/writing:
fwrite(maze.map[x], sizeof(Cell), maze.height, file);
...
fread(maze.map[x], sizeof(Cell), maze.height, file);

Since the elements in each maze item (maze.map[X]) are continuous, and are pre-allocated, you can write each 'maze' item with a single fwrite call:
for (int x = 0; x < maze.width; ++x) {
fwrite(&maze.map[x], sizeof(Cell), maze.height, file);
}
Taking this route, you can use fread instead of fwrite to read the elements.
Side Note: Usually better to use calloc(count, sizeof(...)), instead of malloc(count*sizeof) - it will initialize the allocated memory to zeros.

Related

Why I'm getting two lines in one when I have an specific size

I have a file with 3 lines like this:
1234567890
abcdefghij
ABCDEFGHIJ
And I want to reverse the lines and send it to the same file like this:
ABCDEFGHIJ
abcdefghij
1234567890
But i'm getting a blank line and the last two lines in the same line:
This is my code
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
int main()
{
char filename[512];
memset(filename, 0, 512);
puts("Input file name:");
scanf("%s", filename);
FILE * file = fopen(filename, "rb");
if (file == NULL){
printf("Cannot open file %s \n", filename);
exit(0);
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char * buffer = (char *)malloc(size);
fread(buffer, 1, size, file);
fclose(file);
char *token;
token = strtok(filename, ".");
strcat(token, ".txt.OUT");
file = fopen(filename, "wb");
for (long i = size; i > 0; i--)
{
long j = 0;
while (buffer[i - j] != '\n' && i - j > 0)
j++;
fwrite(&buffer[i - j], 1, j, file);
i -= j;
}
fclose(file);
free(buffer);
return 0;
}
Update:
When I write in the file if I do fwrite(&buffer[i - (j-2)], 1, j, file); I get the three lines correctly but without both A and without the numbers 12
#include<stdio.h>
void main() {
FILE *fi=fopen("input.txt","r");
if(fi==NULL) {
printf("File does not exist...");
exit(0);
}
static char ch[255][255];
for(int i=0;1;i++) {
fscanf(fi,"%s",ch[i]);
if(getc(fi)==EOF) {
break;
}
}
fclose(fi);
FILE *fo=fopen("output.txt","w");
for(int i=254;i>=0;i--) {
if(ch[i][0]!='\0') {
fputs(ch[i],fo);
fputs("\n",fo);
}
}
fclose(fo);
printf("Done...");
}
The program having read the size bytes of a file into buffer, this is the bit responsible for (incorrectly) writing the output:
for (long i = size; i > 0; i--)
{
long j = 0;
while (buffer[i - j] != '\n' && i - j > 0)
j++;
fwrite(&buffer[i - j], 1, j, file);
i -= j;
}
Note first that as was expressed in comments, that code overruns the end of buffer. It having been allocated as length size, buffer's valid indices are from 0 to size - 1, inclusive, yet you attempt to access buffer[size] when i == size and j == 0. Do fix that.
Second, it's pretty easy to see why you get an initial blank line. On the first pass through the outer loop, you scan backwards until you find a j such that buffer[i-j] == '\n'. You then write a segment of the buffer starting at that position, so the first character written is a newline. You probably want instead to output the data starting after the newline.
Continuing from there, the rest becomes clear. On the second pass, you do the same thing, so again the first character written is a newline. That is the newline that appears at the end of "ABCDEFGHIJ". On the third pass, however, you reach the beginning of the buffer without encountering another newline. You correctly avoid overrunning the beginning of the buffer, but this time, the first character written is not a newline, because there isn't one at the beginning of the buffer.
Your best bet for proper line splitting is to avoid copying newlines from the buffer altogether. Copy only the data between the newlines, and add your own newlines manually where they are needed.

how do i use pointers for storing multiple strings using malloc

I tried to make a program to dynamically allocate memory for storing multiple strings using pointers but it cant seem to work.
#include <stdio.h>
#include<conio.h>
void main()
{
FILE *fp;
int num;
printf("enter no of students");
scanf("%d",&num);
char *names = (char*) malloc(num * 100 * sizeof(char));
printf("enter the names");
fp = fopen("file.txt","w");
for (int i = 0; i < num; i++)
{
fgets(*(names + i * 100), 100, stdin);
fprintf(fp, "%s\n", (names + i * 100));
}
fclose(fp);
fp = fopen("file.txt", "r");
printf("names stored in *ptr");
}
You've got several problems. Firstly scanf doesn't play nicely with fgets - it'll leave a the newline in the input stream which means when you call fgets it'll just read that. Easiest way to fix that is to use fgets to read in the whole line when asking for the number of students and sscanf to get the actual number like this:
char temp[100];
fgets(temp,100,stdin);
sscanf(temp,"%d",&num);
Secondly, you're only creating one really big long string, rather than multiple strings. So to create num strings you first want to allocate the "array" of strings like this
names=malloc(sizeof(char *)*num);
and then use a loop to allocate the space for the strings
for(int i=0; i<num; i++)
{
names[i]=malloc(100);
}
You'll also see don't need to cast the return value from malloc, but you do need to include the right file, namely "stdlib.h". And technically you don't need sizeof(char) as that'll always be 1.
You don't check the return values from fopen so you don't handle the situation where it cannot open the file for writing.
Not sure why you're also opening the file for reading at the end either as you don't do anything with it and it'll just get closed automatically when the program ends.
And you should always get into the habit of writing the code to free your allocated memory even if you don't need to cos the code is this simple.
Also technically main should return int.
Final version of the code could look like this:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
int num;
char temp[100];
printf("enter no of students");
fgets(temp,100,stdin);
sscanf(temp,"%d",&num);
char **names=malloc(num*sizeof(char*));
printf("enter the names");
fp=fopen("file.txt","w");
if(fp==NULL)
{
printf("Failed to open\n");
return 1;
}
for(int i=0;i<num;i++)
{
names[i]=malloc(100);
fgets(names[i],100,stdin);
fprintf(fp,"%s\n",names[i]);
}
fclose(fp);
for(int i=0;i<num;i++)
{
free(names[i]);
}
free(names);
printf("names stored in *ptr");
}
You need to have a table of the pointers to the char.
char **strtable = NULL
size_t nstrings = 0;
int addstring(const char *str)
{
int result = -1;
char **tmp = realloc(strtable, (nstrings + 1)* sizeof(*strtable));
if(tmp)
{
tmp[nstrings] = malloc(strlen(str) + 1);
if(tmp[nstrings])
{
result = 0;
strtable = tmp;
strcpy(tmp[nstrings++], str);
}
}
return result;
}
Using it you can add the entered (read) strings and easy iterate.
The simplest approach is to declare names as "a pointer to an array of 100 char" and use just a single malloc. After that you can simply access the individual name using names[i].
Like this:
char (*names)[100] = malloc(num * sizeof *names);
printf("enter the names\n");
fp = fopen("file.txt","w");
for (int i = 0; i < num; i++)
{
fgets(names[i], 100, stdin);
fprintf(fp, "%s", names[i]);
}
fclose(fp);
A few extra comments:
1) sizeof(char) is always 1
2) Notice that scanf("%d",&num); will leave a newline in the input buffer. That causes your first name to be empty. You need to remove that newline before scanning names
3) Also notice that fgets will include a newline (at least when the input is less than 99 chars).
4) It seems that you don't really need to store all names in array as you write them to the file immediately. So you could simply do:
printf("enter the names\n");
fp = fopen("file.txt","w");
for (int i = 0; i < num; i++)
{
char name[100];
fgets(name, 100, stdin);
fprintf(fp, "%s", name);
}
fclose(fp);
I agree with the answer above but if you want to keep the same structure there you go
#include<stdio.h>
#include<conio.h>
void main()
{
FILE *fp;
int num;
printf("enter no of students ");
scanf("%d",&num);
char *names=malloc(num*100);
printf("enter the names\n");
fp=fopen("file.txt","w");
fseek(stdin,0,SEEK_END);
for(int i=0;i<num;i++)
{
fgets((names + i * 100), 100, stdin);
fprintf(fp, "%s", (names + i * 100));
fseek(stdin,0,SEEK_END);
}
fclose(fp);
fp=fopen("file.txt","r");
printf("names stored in *ptr");
fclose(fp);
}

Random values assigned when using fwrite to modify one int on a binary file

Function that it's not working
I'm writing a simple function that opens a file containing 20 ints.
The user can select a number from 1 to 20 and then a score. Then the new score should be written onto the file, and when accessing the file the new value should be there.
void EditScore(void)
{
printf("\nThis is the function to edit a score.\n");
FILE *fPtr = NULL;
int student = 0;
int score = 0;
if((fPtr = fopen("score.dat", "rb+")) == NULL)
{
printf("File could not be opened.\n");
}
else
{
printf("Enter the number of student: ");
scanf("%d", &student);
printf("Enter the new score for the student: ");
scanf("%d", &score);
fseek(fPtr, (student * sizeof(int) - 1), 0);
fwrite(&score, sizeof(int), 1, fPtr);
}
fclose(fPtr);
}
For example, choosing Student 1 and giving it a new score of 10 should give a score of 10 when used with another function to display the numbers of the file.
If I give the score of 10, the value when reading the file is: 167772160.
I've been trying to see if there's an error on my use of the fwrite function, but I haven't found anything.
Reading function (apparently working fine)
void DisplayScore(void)
{
printf("\nThis is the function to display the scores.\n");
FILE *fPtr = NULL;
int grades[20] = {0};
if((fPtr = fopen("score.dat", "rb")) == NULL)
{
printf("File could not be opened.\n");
}
else
{
fread(&grades, sizeof(int), 20, fPtr);
for(int i = 0; i < 20; i++)
{
printf("The score of student %d is %d\n", i + 1, grades[i]);
}
}
fclose(fPtr);
}
Maybe my error is on the process of reading the values of the file again, so I'm going to include the function that displays those values as well if it's useful.
I don't get any compiler error nor warning and I've been looking at other working examples, so I really don't know what I'm doing wrong.
This line
fseek(fPtr, (student * sizeof(int) - 1), 0);
should be
fseek(fPtr, (student-1) * sizeof(int), 0);
otherwise the writing is shifted one byte.

Can't read a single double value from a file (C)

I am having trouble reading in just a single data point from a file. It is supposed to be able to read two columns of data (such as x and y values), but I found out my code cannot even read a single value of double precision. Any help would be appreciated.
The file is at D:\test.txt
and there is a single value of 1.11111.
Enter the location of file (text file) of the airfoil coordinates: D:\test.txt
There are 1 lines
The amount of data in x and y is 1 points and 1 points.
failed to read.
* Process returned 1 *
Press any key to continue...
That was my input.
/*
Purpose:
Create a program that can take in a list of data points that represents an airfoil from some file.
Then through the use of spline function, spline the data points for interpolation then go on to plotting them.
With these data points, use the Vortex Panel Method to obtain coefficients of lift, pressure, and tangential velocity.
Then after these are calculated, plot each with respect to the splined x data points.
*/
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <stdlib.h>
#define LEN 12
int countlines(FILE *fp);
int main (void)
{
char airfoil[500];
double *x_data = NULL;
double *y_data = NULL;
FILE *pfile = NULL;
int line_count = 0;
double test = 0.0;
printf("Enter the location of file (text file) of the airfoil coordinates: ");
scanf("%s", airfoil);
if(fopen_s(&pfile, airfoil, "r"))
{
printf("Error opening the file for reading the data. Program terminated.\n");
exit(1);
}
line_count = countlines(pfile);
printf("There are %d lines\n", line_count);
x_data = realloc(x_data, line_count*(sizeof(double)));
y_data = realloc(y_data, line_count*(sizeof(double)));
if((!x_data) || (!y_data))
{
printf("Memory allocation has failed. Exiting...\n");
exit(1);
}
printf("The amount of data in x and y is %zu points and %zu points.\n", (sizeof(x_data)/sizeof(double)), (sizeof(y_data)/sizeof(double)));
if(EOF == fscanf_s(pfile, "%lf", &test))
{
printf("failed to read.\n");
exit(1);
}
//for(int i = 0; i < line_count; i++)
//{
//fscanf(pfile, " %lf", &x_data[i]);
//}
printf("The x-data are %lf!\n", test);
//for(int i = 0; i < line_count; i++)
//{
//printf("%.2lf", x_data[i]);
//printf("\n");
//}
return 0;
}
int countlines(FILE *fp)
{
int lines = 0;
char str[LEN];
while(!feof(fp))
{
if (fgets(str, LEN, fp) != NULL);
{
lines++;
}
}
return lines;
}
countlines just brought the file pointer to the end of file. Before you can read anything, you must first rewind the file to the beginning:
fseek(pfile,0,SEEK_SET);
You can do ths in countlines().
See also the comments, that spot some more errors.

Trying to read text file into array without repeats in C

This is for a beginner's C programming unit. I'm trying to read a text file containing MAC addresses and the data they received, separate out the relevant data (address and number of packets), copy the addresses to an array without repeating any of them and sum the associated number of packets if an identical address is encountered.
I can read the file in just fine, and get the bits of each line I want without issue, but when I try to check each address read against those already in the array I hit a problem. Depending on the location of the integer counting the number of full lines, the program either fails to recognise identical strings and prints them all as they are in the file, or prints them over one another in addresses[0], leaving me with only the last address. I'm stumped and need some fresh eyes on this - any suggestions would be greatly appreciated.
My code follows:
static void readadds(char filename[])
{
FILE* packetfile = fopen(filename, "r");
FILE* datafile = fopen("packdata.txt", "w+");
// Open file from input; create temporary file to store sorted data.
char line[100];
char addresses[500][18];
int datasize[500];
int addressno = 0;
// Create storage for lines read from text file, addresses and related data.
if(packetfile != NULL)
{
while(fgets(line, sizeof line, packetfile) != NULL)
{
int linenum = 0;
char thisadd[18];
int thisdata;
//Create arrays to temp store data from each line
sscanf(line, "%*s %*s %s %i", thisadd, &thisdata);
for(int i = 0; i < 500; i++)
{
if(strcmp(thisadd, addresses[i]) == 0)
{ //check if the address is already in the array
int x = datasize[i];
datasize[i] = x + thisdata; //sum packet data if address already exists
printf("Match!\n");
break;
}
else
{
strcpy(addresses[linenum], thisadd); //initialize new address
datasize[linenum] = thisdata; //initialize assoc. data
linenum++;
addressno++;
printf("Started!\n");
break;
}
}
}
for(int i = 0; i <= addressno; i++)
{
printf("%s %i\n", addresses[i], datasize[i]);
fprintf(datafile,"%s %i\n", addresses[i], datasize[i]);
}
}
fclose(packetfile);
fclose(datafile);
}
This version prints over addresses[0]. If linenum is replaced by addressno in the for() loop, identical strings are not recognised. My dataset is arranged like this:
1378251369.691375 84:1b:5e:a8:bf:7f 68:94:23:4b:e8:35 100
1378251374.195670 00:8e:f2:c0:13:cc 00:11:d9:20:aa:4e 397
1378251374.205047 00:8e:f2:c0:13:cc 00:11:d9:20:aa:4e 397
1378251374.551604 00:8e:f2:c0:13:cc 00:11:d9:20:aa:4e 157
1378251375.551618 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
1378251375.552697 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
1378251375.553957 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
1378251375.555332 84:1b:5e:a8:bf:7c cc:3a:61:df:4b:61 37
I'm almost certain this is what you're trying to do. The logic to add a new entry was incorrect. You only add one if you have exhausted searching all the current ones, which means you need to finish the current for-search before the add.
Note: Not tested for compilation, but hopefully you get the idea.
static void readadds(char filename[])
{
// Open file from input; create temporary file to store sorted data.
FILE* packetfile = fopen(filename, "r");
FILE* datafile = fopen("packdata.txt", "w+");
// Create storage for lines read from text file, addresses and related data.
char addresses[500][18];
int datasize[500];
int addressno = 0;
if (packetfile != NULL)
{
char line[100];
while(fgets(line, sizeof line, packetfile) != NULL)
{
char thisadd[18];
int thisdata = 0;
//Create arrays to temp store data from each line
if (sscanf(line, "%*s %*s %s %i", thisadd, &thisdata) == 2)
{
// try to find matching address
for(int i = 0; i < addressno; i++)
{
if(strcmp(thisadd, addresses[i]) == 0)
{
//check if the address is already in the array
datasize[i] += thisdata;;
printf("Match!\n");
break;
}
}
// reaching addressno means no match. so add it.
if (i == addressno)
{
printf("Started!\n");
strcpy(addresses[addressno], thisadd); //initialize new address
datasize[addressno++] = thisdata; //initialize assoc. data
}
}
else
{ // failed to parse input parameters.
break;
}
}
for(int i = 0; i <= addressno; i++)
{
printf("%s %i\n", addresses[i], datasize[i]);
fprintf(datafile,"%s %i\n", addresses[i], datasize[i]);
}
}
fclose(packetfile);
fclose(datafile);
}

Resources