how I correctly read struct from a file? - c

I have this kind of struct:
typedef struct {
char a[MAX];
char b[MAX];
int c;
} mys;
and this functions:
mys* search_m(FILE *fp, int m)
{
mys* s;
s=(mys *)malloc(sizeof(mys));
if(s){
for(int i=0;i<m;i++)
fread(s,sizeof(mys),1,fp);
}else{
printf("Error during allocation\n");
}
return s;
}
void search_s(FILE *fp, char *c)
{
mys s;
int count=0;
while(fread(&s,sizeof(mys),1,fp)==1){
if(strcmp(s.b,c)==0){
show_s(s,count);
}
count++;
}
return;
}
that's a part of my program. During the input fase (when I write data on my file) I don't have any problem ( using fread) but I have some problem reading it.
The my function search_s execute and find the value every time that I call it, but the search_m function seems that can't read anby data in my file and since it's practically the same things that I did on search_s I don't know where is my mistake.
In my main when I call seach_m I do this:
s=*search_ID(f_ptr,m);
show_s(s,m);
I don't think that the problem is in the main since the fread in the search_m function doesn't load any data (it stops on the first try).
Where is my mistake? I thought that it was all right
PS before calling this function I open the file and I check for errors and after I close the file.
EDIT:
I'm opening the file with this:
f_ptr=fopen(argv[1],"rb");
(It doesn't work (also) if I only use "r")
argv[1] is the name of my file
PS m is the number of the element in the file that I want to read.
(I read every time one block untile I don't get the m-esimo element)
My file contains only struct of that type.

The following line does not allocate enough memory.
s=(mys *)malloc(sizeof(mys));
You are allocating memory for just object. As a result, you are accessing more memory than you allocated, which causes undefined behavior.
You need to allocate memory for m objects.
s = malloc(sizeof(mys)*m);
See Do I cast the result of malloc? to understand why you should not cast the return value of malloc.
Also, make sure that you read into the appropriate slot of the allocated memory.
mys* search_m(FILE *fp, int m)
{
mys* s = malloc(sizeof(mys)*m);
if(s){
for(int i=0;i<m;i++)
fread(s+i, sizeof(mys), 1, fp); // Use s+i
}else{
printf("Error during allocation\n");
}
return s;
}
You can compress that to just one call to fread.
mys* search_m(FILE *fp, int m)
{
mys* s = malloc(sizeof(mys)*m);
if(s){
fread(s, sizeof(mys), m, fp);
}else{
printf("Error during allocation\n");
}
return s;
}

This is how you would read and write a struct to a file. You should then be able to open your file with fopen and call read_data and write_data for each of your data structures. Once read_data returns 0, it has reached EOF. This can be error prone though, as this depends on the size of the struct. If its size change between versions of your application, it might not be able to deserialize the struct correctly.
struct st_data
{
char str[25];
int i;
int j;
};
typedef struct st_data data_t;
size_t write_data(data_t *data, FILE *stream)
{
return fwrite(data, sizeof(*data), 1, stream);
}
size_t read_data(data_t *data, FILE *stream)
{
return fread(data, sizeof(*data), 1, stream);
}
void search(char *str, FILE *stream)
{
data_t data;
size_t n_read = read_data(&data, stream);
while (n_read > 0)
{
if (strcmp(data.str, str) == 0)
{
// handle data
}
n_read = read_data(&data, stream);
}
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
fprintf(stderr, "ERROR: requires a single argument");
exit(-1);
}
FILE *stream = fopen("data.txt", "rb+");
search(argv[1], stream);
fclose(stream);
exit(0);
}

Related

How to read from pipe into a file?

I would like to read from the pipe straight into a file with the code below. base_fd is a pipe.
FILE* fp = fopen("dec_data", "wb+");
int r_result;
int len = msg_length-part-3; //set to 75933
while ((r_result = read(base_fd[0], fp, len))) {
printf("r_result: %d \n", r_result);
len -= r_result;
}
The read seems to happen fine, with r_result showing 65536 and then 10397 as required. However, when I inspect the file I created, it has a size of 0 bytes...
You have a semantic error in your code.
Take a look at the read(2) system call signature:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
The second parameter to the function is a void pointer (void *buf), which is where read will store the count bytes it reads from fd descriptor.
However, a FILE * is an abstraction of the C library. In this answer you can see more of it. The struct FILE in MinGW32 5.1.4 is:
typedef struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
What read will do is similar to how we copy strings. Consider this function:
void strcpy(char *dst, char *src)
{
while(*src) *dst++ = *src++;
}
This function will copy the contents from src into dst until it finds a NULL terminating byte. This is obviously a very flawed function and should never be used, but illustrates why your example doesn't work.
Under the hood, what read is doing is very similar to this strcpy function: it is overwriting a lot of bytes in memory starting at the address pointed to by the fp pointer. You are effectively losing your reference to the FILE * pointer and the resources associated to it.
I'll bet that if you try to close(fp) after that loop you'll get a segmentation fault (it's Undefined Behavior, but I'll bet anyway).
The right way to do what you want is:
FILE* fp = fopen("dec_data", "wb+");
char *buf;
int r_result;
int len = msg_length - part - 3; //set to 75933
buf = malloc(len);
if(!buf) {
perror("malloc");
exit(EXIT_FAILURE);
}
while ((r_result = read(base_fd[0], buf, len))) {
fprintf(fp, buf);
len -= r_result;
}
free(buf);
close(fp); // now it closes the file pointer

How to access struct in an array of structs

In a school assignment, I have to sort elements of struct that are located in binary file. I think I have managed to sort it, but I have a problem with printing the result. I don't know how to access elements of struct because data must be read from file, so I only have address of the first struct in a array. (I think it should stay in array so that I can use qsort.)
This is the main code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "readfile.h"
typedef struct {
char name[32];
double value;
} record;
int nuvu(record* a, record* b){
if(a->name < b->name) return -1;
if(a->name > b->name) return 1;
if(a->value < b->value) return -1;
if(a->value > b->value) return 1;
}
int main()
{
long N;
unsigned char* p = readfile( "d.bin", &N );
char* s;
scanf("%s",&s);
int k= N/sizeof(record);
qsort(p,k,sizeof(record),(int(*)(const void*, const void *))nuvu);
printf???
free(p);
return 0;
}
Additional:
readfile.c
#include "readfile.h"
unsigned char* readfile( char* filename, long* pN )
{
FILE* f= fopen(filename,"rb");
if(f==0){
*pN=-1;
return 0;
}
fseek(f,0,SEEK_END);
*pN=ftell(f);
fseek(f,0,SEEK_SET);
char*p=malloc(*pN);
if(p==0){
*pN=-2;
fclose(f);
return 0;
}
size_t r = fread(p,1,*pN,f);
if(r!=*pN){
*pN=-3;
fclose(f);
free(p);
return 0;
}
fclose(f);
return p;
}
readfile.h
#ifndef __READFILE_H
#define __READFILE_H
#include <stdio.h>
#include <stdlib.h>
unsigned char* readfile(char* filename, long* pN);
#endif /* __READFILE_H */
The biggest confusion you seem to have is "How do I read my array of structs back in?"
unsigned char* p = readfile( "d.bin", &N );
Is no way to begin. The concept of reading records from a binary file into an array of struct, is to read sizeof (struct record) bytes from the file into storage for type struct record. (this will ignore for now the lack of serialization of the data, padding and portability issues, and the fact we are using a typedef).
Knowing the file size, and knowing the sizeof (struct record) allows you to (1) validate the number of records you will read from the file, e.g. (nbytes / sizeof (struct record)) and (2) determine whether there are any stray bytes leftover that will not be part of the read (e.g. if (nbytes / sizeof (struct record) != 0)) which if present you should at least warn about.
Depending on the number of records you have to read and whether there is an upper bound for that number will determine whether you can use a fixed size array (or VLA) or whether you need to dynamically allocate (and reallocate) to address an unknown number of records or to prevent StackOverflow.. Regardless how you handle creating storage for your records -- It is up to you to make sure you do not write beyond the bounds of the storage you create.
Below, for example purposes we will simply work with an array of 100 records. The dividing line between what will fit on the stack and when you need to dynamically allocate will be compiler dependent, but any time you start thinking about tens of thousands of records, you need to consult your compiler documentation and start thinking about dynamic allocation.
fread provides a simple method to read binary records from a file and validate that you actually read the number of records you intended to read. For example, given a declaration of an array of 100 records in rec, you could do something like the following:
enum { MAXC = 32, MAXS = 100 }; /* if you need constants, define them */
...
record rec[MAXS] = {{ .name = "" }}; /* array of 100 records */
...
nrec = nbytes / sizeof *rec; /* number of records based on file size */
/* read / validate nrec records from file */
if (fread (rec, sizeof *rec, nrec, fp) != nrec) {
perror (fn);
return 1;
}
With your records successfully read from your file, using qsort to sort the records (either by name or by value) requires you to understand that the const void * pointers to be compared in your comparison function will be pointer to rec, so you must provide an appropriate cast within your comparison function to access and compare the values. For example to perform a string comparison on name, you can do something similar to the following:
/** record string comparison on name */
int reccmpname (const void *a, const void *b)
{
const record *ra = a,
*rb = b;
return strcmp (ra->name, rb->name);
}
Other than that, the remainder of what your code lack is validation of each step in the process. Always, always, validate the return of any function you use and handle any errors you encounter. A minimal example, without splitting the code up between separate source files could be something like the following. Splitting up into separate source files is left to you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 32, MAXS = 100 }; /* if you need constants, define them */
typedef struct {
char name[MAXC];
double value;
} record;
/** record string comparison on name */
int reccmpname (const void *a, const void *b)
{
const record *ra = a,
*rb = b;
return strcmp (ra->name, rb->name);
}
int main (int argc, char **argv) {
record rec[MAXS] = {{ .name = "" }}; /* array of 100 records */
size_t nrec = 0; /* number of records from file */
long nbytes = 0; /* number of bytes in file */
char *fn = argc > 1 ? argv[1] : "dat/records.bin";
FILE *fp = fopen (fn, "rb");
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (fseek (fp, 0, SEEK_END) == -1) { /* validate seek to end */
perror ("fseek");
return 1;
}
nbytes = ftell (fp); /* number of bytes in file */
if (nbytes == -1) {
perror ("ftell");
return 1;
}
if (fseek (fp, 0, SEEK_SET) == -1) { /* validate seek to start */
perror ("fseek");
return 1;
}
if (nbytes % sizeof *rec != 0) /* does file contain even no. or records? */
fprintf (stderr, "warning: file size not multiple of record size.\n");
nrec = nbytes / sizeof *rec; /* number of records based on file size */
/* read / validate nrec records from file */
if (fread (rec, sizeof *rec, nrec, fp) != nrec) {
perror (fn);
return 1;
}
fclose (fp); /* close file */
printf ("\n=== unsorted records ===\n\n"); /* output unsorted */
for (size_t i = 0; i < nrec; i++)
printf ("%-32s %g\n", rec[i].name, rec[i].value);
qsort (rec, nrec, sizeof *rec, reccmpname); /* qsort records */
printf ("\n=== sorted records ===\n\n"); /* output sorted */
for (size_t i = 0; i < nrec; i++)
printf ("%-32s %g\n", rec[i].name, rec[i].value);
return 0;
}
note: The data file used simply contained 100 struct records with dictionary words as name and random values as value shuffled before being written to the file.
Example Use/Output
$ ./bin/struct_rd_name_val_recs
=== unsorted records ===
Abscess 4.15871e+08
Abject 3.5743e+08
Abo 6.87659e+08
Aboard 2.02028e+09
Abase 3.34319e+08
...
=== sorted records ===
A 3.66907e+08
Aaa 5.59224e+07
Aaas 1.45617e+09
Aardvark 1.72828e+09
Aarhus 1.95723e+09
Let me know if you have any questions.
Use the standard procedure for qsort, don't change its signature. Use strcmp as noted in comments. You have to figure out the logic of how the structure is to be sorted. The example below sorts by record::name, if name is the same, it tests value, in that order:
int nuvu(const void * a_, const void * b_)
{
const record* a = a_;
const record* b = b_;
if(strcmp(a->name, b->name) == 0)
return a->value > b->value;
return strcmp(a->name, b->name);
}
Data is read as bytes in to p, it must be converted to "array of records" record* arr = (record*)p;. The number of items in the array should be filesize/sizeof(record) if everything went according to plan
int main(void)
{
long filesize = 0;
unsigned char* p = readfile("d.bin", &filesize);
if(!p)
return 0;
int count = filesize / sizeof(record);
record* arr = (record*)p;
qsort(arr, count, sizeof(record), nuvu);
for(int i = 0; i < count; i++)
printf("%s %f\n", arr[i].name, arr[i].value);
free(p);
return 0;
}
You can use the [] operator with a pointer:
struct my_struct {
int i, j;
};
struct my_struct * ptr = malloc(sizeof(struct my_struct) * 10);
for(int n = 0; 10 > n; ++ n)
{
ptr[n].i = n;
ptr[n].j = n*2;
}
free(ptr);

realloc() works only a few times [duplicate]

My C program is crashing and I am too new to figure it out. It's very simple so far and I imagine the code is enough to figure out what is going wrong.
I am simply trying to read a file line by line. I will double the memory of a structure once I am out of memory. If this is not enough information, I will give whatever else you need.
Thank you very much for any help, as I have been stuck for hours now.
/*
John Maynard
1000916794
7/15/2013
HW-06
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
struct course
{
char subject[11];
int catalogNum;
int sectionNum;
int enrollmentTotal;
int enrollmentCap;
};
void readFile(struct course *d, char* filename);
void double_array_size(struct course *d, int new_size);
int main(void)
{
char *filename = "hw06-data.csv";
struct course *d;
d = malloc( N * sizeof(struct course));
readFile(d, filename);
}
void readFile(struct course *d, char* filename)
{
FILE* fp;
char buffer[100];
int i = 0, array_size = 100;
struct course *temp;
if( ( fp = fopen(filename, "r") ) == NULL)
{
printf("Unabale to open %s.\n", filename);
exit(1);
}
fgets(buffer, sizeof(buffer), fp);
while( fgets(buffer, sizeof(buffer), fp) != NULL)
{
if (i == array_size)
{
array_size *= 2;
double_array_size(d, array_size);
printf("reached limit...increasing array to %d structures\n", array_size);
}
i++;
}
fclose( fp );
}
void double_array_size(struct course *d, int new_size)
{
struct course *temp;
temp = realloc(d, new_size * sizeof(struct course));
if(temp == NULL)
{
printf("unable to reallocate\n");
exit(1);
}
else
d = temp;
}
realloc() may return a different pointer than the original one but you assign that to temp only so the calling function still works with the original pointer afterwards. Change double_array_size() to return the new pointer returned by realloc() and call
d = double_array_size(d, array_size);
Furthermore you should always check the result fo malloc(), realloc() etc. They may return NULL if there is no more memory available
Combining Ingo's and codroipo's answers, you have to either return the new pointer from double_array_size, or you have to pass in a pointer to d so you can update the pointer from double_array_size
Realloc reallocates memory, so probably memory pointed by d will be released, so double_array_size has to edit d, you could try:
void double_array_size(struct course** d, int new_size){
*d = realloc(*d, new_size * sizeof(struct course));
.
.
.
}

Passing pointer of pointers to function, returning both an int and an address (by parameter)

I've got a function which, as is, works correctly. However the rest of the program has a limitation in that I've preset the size of the array (the space to be allocated). Obviously, this is problematic should an event arise in which I need extra space for that array. So I want to add dynamic allocation of memory into my program.
But I'm having an issue with the whole pointer to a pointer concept, and I've utterly failed to find an online explanation that makes sense to me...
I think I'll want to use malloc(iRead + 1) to get an array of the right size, but I'm not sure what that should be assigned to... *array? **array? I'm not at all sure.
And I'm also not clear on my while loops. &array[iRead] will no longer work, and I'm not sure how to get a hold of the elements in the array when there's a pointer to a pointer involved.
Can anyone point (heh pointer pun) me in the right direction?
I can think of the following approaches.
First approach
Make two passes through the file.
In the first pass, read the numbers and discard them but keep counting the number of items.
Allocate memory once for all the items.
Rewind the file and make a second pass through it. In the second pass, read and store the numbers.
int getNumberOfItems(FILE* fp, int hexi)
{
int numItems = 0;
int number;
char const* format = (hexi == 0) ? "%X" : "%d";
while (fscanf(fp, format, &number) > 0) {
++numItems;
return numItems;
}
void read(int *array, FILE* fp, int numItems, int hexi)
{
int i = 0;
char const* format = (hexi == 0) ? "%X" : "%d";
for ( i = 0; i < numItems; ++i )
fscanf(fp, format, &array[i]);
}
int main(int argc, char** argv)
{
int hexi = 0;
FILE* fp = fopen(argv[1], "r");
// if ( fp == NULL )
// Add error checking code
// Get the number of items in the file.
int numItems = getNumberOfItems(fp, hexi);
// Allocate memory for the items.
int* array = malloc(sizeof(int)*numItems);
// Rewind the file before reading the data
frewind(fp);
// Read the data.
read(array, fp, numItems, hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
Second approach.
Keep reading numbers from the file.
Every time you read a number, use realloc to allocate space the additional item.
Store the in the reallocated memory.
int read(int **array, char* fpin, int hexi)
{
int number;
int iRead = 0;
// Local variable for ease of use.
int* arr = NULL;
char const* format = (hexi == 0) ? "%X" : "%d";
FILE *fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
while (fscanf(fp, format, &number) > 0) {
arr = realloc(arr, sizeof(int)*(iRead+1));
arr[iRead] = number;
iRead += 1;
}
fclose(fp);
// Return the array in the output argument.
*array = arr;
return iRead;
}
int main(int argc, char** argv)
{
int hexi = 0;
int* array = NULL;
// Read the data.
int numItems = read(&array, argv[1], hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
int read(int **array, char* fpin, int hexi) {
int iRead = 0;
int i, *ary;
char *para;
FILE *fp;
fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
para = (hexi == 0) ? "%*X" : "%*d";
while (fscanf(fp, para)!= EOF)
++iRead;
ary = *array = malloc(iRead*sizeof(int));
if(ary == NULL){
printf("malloc error!\n");
exit(-2);
}
rewind(fp);
para = (hexi == 0) ? "%X" : "%d";
for(i = 0; i < iRead; ++i)
fscanf(fp, para, &ary[i]);
fclose(fp);
return iRead;
}
I'd suggest something like this:
int read(int **array_pp, char* fpin, int hexi) {
...
int *array = malloc (sizeof (int) * n);
for (int i=0; i < n; i++)
fscanf(fp, "%X",&array[i]);
...
*array_pp = array;
return n;
}
Notes:
1) You must use "**" if you want to return a pointer in a function argument
2) If you prefer, however, you can declare two pointer variables (array_pp and array) to simplify your code.
I think you wouldn't call it an array. Arrays are of fixed size and lie on the stack. What you need (as you already said), is dynamically allocated memory on the heap.
maybe that's why you didn't find much :)
here are some tutorials:
http://en.wikibooks.org/wiki/C_Programming/Arrays (and following pages)
http://www.eskimo.com/~scs/cclass/int/sx8.html
you got the function declaration correctly:
int read(int **array, char* fpin, int hexi)
What you need to do:
find out how much memory you need, eg. how many elements
allocate it with *array = malloc(numElements * sizeof(int)) (read "at the address pointed by array allocate memory for numElements ints")
now you can (*array)[idx] = some int (read "at the address pointed by array, take the element with index idx and assign some int to it")
call it with int* destination; int size = read(&destination, "asdf", hexi)
hope it helps..

realloc structure with function in C

My C program is crashing and I am too new to figure it out. It's very simple so far and I imagine the code is enough to figure out what is going wrong.
I am simply trying to read a file line by line. I will double the memory of a structure once I am out of memory. If this is not enough information, I will give whatever else you need.
Thank you very much for any help, as I have been stuck for hours now.
/*
John Maynard
1000916794
7/15/2013
HW-06
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
struct course
{
char subject[11];
int catalogNum;
int sectionNum;
int enrollmentTotal;
int enrollmentCap;
};
void readFile(struct course *d, char* filename);
void double_array_size(struct course *d, int new_size);
int main(void)
{
char *filename = "hw06-data.csv";
struct course *d;
d = malloc( N * sizeof(struct course));
readFile(d, filename);
}
void readFile(struct course *d, char* filename)
{
FILE* fp;
char buffer[100];
int i = 0, array_size = 100;
struct course *temp;
if( ( fp = fopen(filename, "r") ) == NULL)
{
printf("Unabale to open %s.\n", filename);
exit(1);
}
fgets(buffer, sizeof(buffer), fp);
while( fgets(buffer, sizeof(buffer), fp) != NULL)
{
if (i == array_size)
{
array_size *= 2;
double_array_size(d, array_size);
printf("reached limit...increasing array to %d structures\n", array_size);
}
i++;
}
fclose( fp );
}
void double_array_size(struct course *d, int new_size)
{
struct course *temp;
temp = realloc(d, new_size * sizeof(struct course));
if(temp == NULL)
{
printf("unable to reallocate\n");
exit(1);
}
else
d = temp;
}
realloc() may return a different pointer than the original one but you assign that to temp only so the calling function still works with the original pointer afterwards. Change double_array_size() to return the new pointer returned by realloc() and call
d = double_array_size(d, array_size);
Furthermore you should always check the result fo malloc(), realloc() etc. They may return NULL if there is no more memory available
Combining Ingo's and codroipo's answers, you have to either return the new pointer from double_array_size, or you have to pass in a pointer to d so you can update the pointer from double_array_size
Realloc reallocates memory, so probably memory pointed by d will be released, so double_array_size has to edit d, you could try:
void double_array_size(struct course** d, int new_size){
*d = realloc(*d, new_size * sizeof(struct course));
.
.
.
}

Resources