I have written a struct array to a binary file using this function:
int write_binary(const char* filename, const Product* shop)
{
FILE* OUT;
int jees = 0;
int i = 0;
OUT = fopen(filename, "wb");
if (!OUT) {
return 1;
}
while (jees == 0)
{
//the last element of the struct array has '\0' as the first char of its name
if (shop[i].name[0] == '\0')
{
jees = 1;
}
fwrite(&shop[i], sizeof (Product), 1, OUT) ;
i++;
}
fclose(OUT);
return 0;
}
Now I want to read it back into a struct array pointer. I have tried:
Product* read_binary(const char* filename)
{
FILE* IN = fopen(filename,"rb");
Product *shop;
for (int i = 0; i < 10; i++) {
fread(&shop[i], sizeof(Product), 1, IN);
}
fclose(IN);
return shop;
}
But this way doesn't seem to work. Is there a way to find out the how many structs are in the binary data?
Product *shop;
Here you are declaring a pointer, but you are not allocating memory for it. You should allocate with malloc() or do some static allocation.
To know the number of structs in the file, I'd seek to the end of it, count the bytes and divide by the size of the struct.
A side note: you don't need the jees variable. Just test the breaking condition after writing and break the loop explicitly:
for (i = 0; ; i++)
{
fwrite(&shop[i], sizeof (Product), 1, OUT);
if (shop[i].name[0] == '\0')
break;
}
Related
I am reading a file that contains several lines of strings(max length 50 characters). To store those strings I created a char double-pointer using calloc. The way my code works is as it finds a line in the file it adds one new row (char *) and 50 columns (char) and then stores the value.
My understanding is that I can call this method and get this pointer with values in return. However, I was not getting the values so I check where I am losing it and I found that the memory is not persisting after while loop. I am able to print strings using print 1 statement but print 2 gives me null.
Please let me know what I am doing wrong here.
char **read_file(char *file)
{
FILE *fp = fopen(file, "r");
char line[50] = {0};
char **values = NULL;
int index = 0;
if (fp == NULL)
{
perror("Unable to open file!");
exit(1);
}
// read both sequence
while (fgets(line, 50, fp))
{
values = (char **)calloc(index + 1, sizeof(char *));
values[index] = (char *)calloc(50, sizeof(char));
values[index] = line;
printf("%s",values[index]); // print 1
index++;
}
fclose(fp);
printf("%s", values[0]); // print 2
return values;
}
line content is overwritten on each loop iteration (by fgets()).
values is overwritten (data loss) and leaks memory on each iteration index > 1.
value[index] is allocated memory on each iteration which leaks as you overwrite it with the address of line on the following line.
line is a local variable so you cannot return it to caller where it will be out of scope.
caller has no way to tell how many entries values contain.
Here is a working implementation with a few changes. On error it closes the file and frees up memory allocated and return NULL instead of exiting. Moved printf() to caller:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_LEN 50
char **read_file(char *file) {
FILE *fp = fopen(file, "r");
if(!fp) {
perror("Unable to open file!");
return NULL;
}
char **values = NULL;
char line[BUF_LEN];
unsigned index;
for(index = 0;; index++) {
char **values2 = realloc(values, (index + 1) * sizeof(char *));
if(!values2) {
perror("realloc failed");
goto err;
}
values = values2;
if(!fgets(line, BUF_LEN, fp)) break;
values[index] = strdup(line);
}
fclose(fp);
values[index] = NULL;
return values;
err:
fclose(fp);
for(unsigned i = 0; i < index; i++) {
free(values[i]);
}
free(values);
return NULL;
}
int main() {
char **values = read_file("test.txt");
for(unsigned i = 0; values[i]; i++) {
printf("%s", values[i]);
free(values[i]);
}
free(values);
return 0;
}
fgets() returns line ending in '\n' or at most BUF_LEN - 1 of data. This means a given value[i] may or may not be ending with a \n. You may want this behavior, or you want value[i] to be consistent and not contain any trailing \n irregardless of the input.
strdup() is _POSIX_C_SOURCE >= 200809L and not standard c,
so if you build with --std=c11 the symbol would not be defined.
I am trying to write a structure to a binary file, the punctuation will be received as a parameter in the insertInFile function, (working with raspberryPi, and store the values of the time reaction with switches). Then I want to read from the file and and store the values on the heap and finally read those values I stored dynamically. I'm having problems with this because either it cannot read properly or it cannot write to the binary file.
typedef enum dificulty {
EASY, MEDIUM, HARD
} Dificulty;
typedef struct player {
char nickname[MAXSIZE];
enum dificulty Dificulty;
float pontuacion;
} Player;
#define LED_CYCLES 5
#define MAX_PLAYERS 2
int insertInFile(float const *const timeReceived, unsigned short *level, char *playerName) {
Player players[MAX_PLAYERS];
int count = 0, i;
FILE *fplayers;
//check if file can be opened
if ((fplayers = fopen("game.bin", "wb")) == NULL) {
fputs("Erro ao abrir ficheiro\n", stderror);
return (-1);
}
//go to the beginning of the file
rewind(fplayers);
//cycle that allows to save int the bin file the struct "Player"
for (i = 0; i < count; i++) {
Player[i].pontuacion = &timeReceived[count];
Player[i].Dificulty = &level;
Player[i].nickname = &playerName;
fwrite(&players, sizeof (Player), count, fplayers);
}
//close the bin file
fclose(fplayers);
return 0;
}
void obtainFromFile() {
Player players;
int count = 0;
FILE *fplayers;
size_t size;
unsigned char *buffer;
int i;
//open file
fp = fopen("game.bin", "rb");
fseek(fp, 0, SEEK_END);
size = ftell(fp); // calculate the size needed
fseek(fp, 0, SEEK_SET);
buffer = (unsigned char *)malloc(size);
if (fplayers == NULL) { // check some error if file == empty
printf("Error\n", stderr);
return (-1);
} else
if (fread(&buffer, sizeof (*buffer), size, fp) != size) // if count of read bytes != calculated size of .bin file -> ERROR
printf("Error\n", stderr);
return (-1);
} else {
for (i = 0; i < size; i++) {
printf("%02x", buffer[i]);
}
}
fclose(fp);
free(buffer);
}
In insertInFile() there is a misunderstanding of the structure use. Writing Player.pontuacion is not right.
To assign a value in an array of struct Player
players[MAX_PLAYERS];, use players[i].Dificulty = (Dificulty)level.
//cicle that allows to save int the bin file the struct "Player"
for (i = 0; i < count; i++) {
players[i].pontuacion = timeReceived[count];
players[i].Dificulty = (Dificulty)(level[count]);
strncpy(players[i].nickname,playerName,MAXSIZE-1);
players[i].nickname[MAXSIZE]='\0';
}
// write count players
fwrite(&(players[0]), sizeof (Player), count, fplayers);
Instead of:
for (i = 0; i < count; i++) {
Player.pontuacion = &timeReceived[count];
Player.Dificulty = &level;
Player.nickname = &playerName;
fwrite(&players, sizeof (Player), count, fplayers);
}
So I am coming from C++ for the first time with this exercise using pointers. My professor would like us to do this exercise without using index to have better mastery of pointers. I'm trying to read from a file into a 2d array embedded in a dynamically allocated struct. I hope I've worded my question well enough and provided the proper information. Thank you very much.
my struct
typedef struct //struct that holds ASCII art
{
char artistName[80]; //name of artist
char asciiArt[20][80]; //actual ascii art line by line
int rating; //rating of art
}TextArt;
Array of struct declaration and allocation
TextArt **artPtr;
artPtr = malloc(1000 * sizeof(TextArt) + 1);
Line of code giving me problems. I read from the file fine up to this point, I'm not sure if this while is valid, I'm looking for a # in the first character. The syntax is very confusing for me here not using index, so this is where I need the most help. It is a pointer to an array of struct (*asciiArt + i) then in the struct -> a 2d c string array asciiArt[][].
while(*tempLine != '#') //while there is no # in position [0] -- read into temp string
{
printf("while templine\n");
fgets((*asciiArt+i)->asciiArt, 100, pFile);
}
Full function from above code if you need more info, otherwise ignore this block of code.
void readFromFile(TextArt **asciiArt, int size, char *fileName)
{
int index = 0; //index for array of struct
int i = 0;
int j = 0;
char tempLine[500]; //temp placeholder
FILE *pFile;
pFile = fopen (fileName ,"r");
if (pFile == NULL)
printf("Error opening file");
else
{printf("file opened.");
for(i = 0; pFile != NULL; i++) //*(*(data + i) + j)
{ j=0;
fgets((*asciiArt+i)->artistName, 100, pFile); //get artist name from first line
printf("got artist name");
while(*tempLine != '#')
{printf("while templine\n");
fgets((*asciiArt+i)->asciiArt, 100, pFile); //while there is no # -- read into temp string
}
strcpy(tempLine, ""); //clear temp string
printf("for loop done");
}
return;
}
}
This is a mistake:
TextArt **artPtr;
artPtr = malloc(1000 * sizeof(TextArt) + 1);
sizeof(TextArt) has nothing to do with sizeof(TextArt *). artPtr points to an array of pointers, not to an array of TextArt objects. Each of those 1000 pointers you just allocated (or tried to allocate) currently points nowhere, and you have to point each one somewhere before using them.
Update: OP clarified the intent to allocate an array of 1000 TextArts:
TextArt *artPtr;
artPtr = malloc(1000 * sizeof(TextArt));
I'm not sure what the + 1 was meant to be.
If the readFromFile function needs to possibly resize the array then you pass it like:
void readFromFile( &artPtr, ......
and inside the function you access it like:
fgets((*asciiArt)[i].artistName, 100, pFile);
Actually I'd write
TextArt *artPtr = *asciiArt;
fgets( artPtr[i].artistName, 100, pFile );
as that is easier to read instead of having brackets everywhere.
If the function does not need to reallocate then just make it take TextArt *artPtr;.
Probably
TextArt *artPtr;
artPtr = malloc(1000 * sizeof(TextArt));
...
void readFromFile(TextArt *asciiArt, int size, char *fileName){
int index, i, ch; //index for array of struct
char tempLine[500];
FILE *pFile;
pFile = fopen (fileName ,"r");
if (pFile == NULL)
printf("Error opening file");
else{
printf("file opened.");
for(index = 0; index < size; ++index){
TextArt *pta = asciiArt + index;
fgets(pta->artistName, sizeof(pta->artistName), pFile);
printf("got artist name");
for(i=0;i<20;++i){//20 : sizeof(pta->asciiArt)/sizeof(pta->asciiArt[0])
if(EOF==(ch=fgetc(pFile))){
index = size;//outer loop break
break;
} else if(ch == '#'){
//ungetc(ch, pFile);
fgets(tempLine, sizeof(tempLine), pFile);
break;
} else {
ungetc(ch, pFile);
fgets(pta->asciiArt + i, sizeof(pta->asciiArt[0]), pFile);
}
}
}
fclose(pFile);
}
}
this is an assignment for my CS course,
im trying to write a code that reads a file line by line and put the input into a struct element.the struct looks like this:
typedef char* Name;
struct Room
{
int fStatus;
Name fGuest;
};
the status is 0 for available and 1 for booked. the name will be empty if the room is available.
there are 2 function, one to read and put the values to a struct element, and the other one to print it out.
int openRoomFile()
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
while (getline(&buffer, &length, roomFile) != -1) {
if (count % 2 == 0) {
sscanf(buffer, "%d", &AllRooms[count].fStatus);
} else {
AllRooms[count].fGuest = buffer;
}
count++;
}
fclose(roomFile);
free(buffer);
return 0;
}
print function
void printLayout(const struct Room rooms[])
{
for (int i=0; i<3; i++) {
printf("%3d \t", rooms[i].fStatus);
puts(rooms[i].fGuest);
}
}
the output is not what i expected, given the input file is :
1
Johnson
0
1
Emilda
i will get the output :
1 (null)
0
0 (null)
i dont know what went wrong, am i using the right way to read the file? every code is adapted from different sources on the internet.
Here is a fixed version of the openRoomFile()
int openRoomFile(void)
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
while (1) {
buffer = NULL;
if (getline(&buffer, &length, roomFile) == -1) {
break;
}
sscanf(buffer, "%d", &AllRooms[count].fStatus);
free(buffer);
buffer = NULL;
if (getline(&buffer, &length, roomFile) == -1) {
fprintf(stderr, "syntax error\n");
return 1;
}
AllRooms[count].fGuest = buffer;
count++;
}
fclose(roomFile);
return 0;
}
When you no longer need those fGuest anymore, you should call free on them.
If your input is guaranteed to be valid (as were many of my inputs in my CS classes), I'd use something like this for reading in the file.
while(!feof(ifp)){
fscanf(ifp,"%d%s",&AllRooms[i].fStatus, AllRooms[i].fGuest); //syntax might not be right here
//might need to play with the '&'s
//and maybe make the dots into
//arrows
//do work here
i++;
}
You are not allocating memory for Name. Check this. In the below example i'm not included free() calls to allocated memory. you need to call free from each pointer in AllRooms array, once you feel you are done with those and no more required.
#include<stdio.h>
#include<stdlib.h>
typedef char* Name;
struct Room
{
int fStatus;
Name fGuest;
}Room_t;
struct Room AllRooms[10];
int openRoomFile()
{
FILE *roomFile;
char *buffer = NULL;
size_t length = 0;
size_t count = 0;
size_t itemCount = 0;
roomFile = fopen("roomstatus.txt", "r+");
if (roomFile == NULL)
return 1;
buffer = (char *) malloc(16); // considering name size as 16 bytes
while (getline(&buffer, &length, roomFile) != -1) {
if (count % 2 == 0) {
sscanf(buffer, "%d", &AllRooms[itemCount].fStatus);
} else {
AllRooms[itemCount].fGuest = buffer;
itemCount++;
}
count++;
buffer = (char *) malloc(16); // considering name size as 16 bytes
}
fclose(roomFile);
free(buffer);
return 0;
}
void printLayout(const struct Room rooms[])
{
int i;
for (i=0; i<3; i++) {
printf("%3d \t", rooms[i].fStatus);
puts(rooms[i].fGuest);
}
}
int main(void)
{
openRoomFile();
printLayout(AllRooms);
// free all memory allocated using malloc()
return 0;
}
so sorry if I am a little confused.
I am trying to fill out an array of structs with values I read in from an input file. I am having no trouble reading in the values from the file. But when the file is very small and does not fill the array completely, the remaining structs have random values in them, and I would like to completely set these structs to NULL. I am attempting to do this because I would like to run through this filled out array of structs and print its values, and I need to see which array values are legitimately from the file.
Here is my code so far
struct function {
char name[20];
int parameterNumer;
};
int main(int argc, const char * argv[])
{
struct function functionList[10];
int i =0, j;
int portNumber;
char *configFile = argv[1];
FILE *fp;
fp = fopen(configFile, "r");
if(fp == NULL) {
perror("File not found");
exit(1);
}
fscanf(fp, "%d", &portNumber);
while(fscanf(fp, "%s %d", functionList[i].name, &functionList[i].parameterNumer) == 2) {
i++;
}
functionList[i] = NULL; //getting an error here
for(j = 0; functionList[j] != NULL; j++) { //and here
printf("%s %d", functionList[j].name, &functionList[j].parameterNumer);
}
return 0;
}
Initialize the array:
/* Remaining elements zero-d. */
struct function functionList[10] = { { "", 0 } };
if an empty string or a zero indicates an unpopulated entry in the array and then use either an empty string or zero int to terminate the for:
for(j = 0; strlen(functionList[j].name); j++) {
for(j = 0; functionList[j].parameterNumber; j++) {
Additionally, prevent out of bounds access on functionList in the while:
while(i < 10 && fscanf(fp,
"%s %d",
functionList[i].name,
&functionList[i].parameterNumer) == 2)
{
i++;
}
Note that the value of i after this while would also provide a terminating condition for the subsequent for loop:
for (j = 0; j < i; j++) {
You could create the array with a calloc()
struct function* functionList = calloc(sizeof(struct function), 10);
and change to this pointer where the array is referenced, in this way the struct is created with all zero in it.
You can also use memset:
memset(functionList, 0, sizeof(functionList));