Guys I have used this to read in data successfully from a file.
void read(FILE *fPtr, int *a, int *b, char **c, int size)
{
char line[100];
int num = 0;
while ( fgets(line, 99, fPtr) && num < size )
{
// This is a names array
c[num] = strtok (line, " ");
// here the name prints out fine
a[num] = atoi(strtok (NULL, " \n"));
b[num] = atoi(strtok (NULL, " \n"));
num++;
}
}
But I am unable to read properly from this char ** array.
main function:
int main()
{
FILE *fp;
int g[30];
int a[30];
char *names[30];
// Open file
fp = fopen("input.txt", "r");
read(
fp, g, a,
names, 30 );
printf("%s\n", player_names[0]);
printThis(
g, a,
names, 30 );
return 0;
}
print this:
void printThis(int* g, int* a, char** n, int s)
{
for (int i = 0; i < 5; ++i)
printf("%s\n", n[i]);
}
This totally does not print names! it prints just # some space chars and #. Why isn't it printing anything. Is it not the proper way for accessing char arrays?
Where, as has been addressed in the comments, you were expecting to assign c[num] = strtok (line, " ");, you found the values in names no longer pointed to anything meaningful. When you declare a variable within a function, unless you dynamically allocate storage for the variable (e.g. with malloc), the variable only survives for the lifetime of the function. Why?
When a function is called a separate area of memory is created for the execution of the function called the function stack frame. That memory holds the variables declared within the function. When the function returns, the stack frame is destroyed (actually just released for reuse as required). Any variables declared local to the function are no longer available for access.
In your case, strtok returns a pointer to the beginning of a token within line (or NULL if no token in found). Upon function return, the pointer returned by strtok points to memory that has been release for reuse and can no longer be accessed within the calling function (main here) -- thus your problem.
How do I fix this? Well, there are many ways. The first, easiest solution, is just to dynamically allocate storage for the names, and make a copy of the tokens found by strtok and store them within the memory you have allocated. That memory will survive return and can be validly accessed in the caller. (you must remember to free the memory when it is no longer needed)
You also need to have your read (my readfp below) function return a meaningful value rather than void. It would be nice to know how many records were actually filled. If you change the function type to int, then you can return num and know how many records (e.g. name[x] a[x] & g[x] combination) were filled.
Wait... num was declared local to read, why can I return it? Every function can always return a value of its own type. If you declare int read (...), read can return an int value and its survives return due to the calling convention between caller and callee.
Putting those pieces together, and using strtol in place of atoi for int conversions (and making it easy by creating a separate function to handle and validate the conversions), you could do something similar to the following. Also note that main takes arguments which you can make use of to pass a filename to open, rather than hardcoding, e.g. fopen("input.txt", "r").
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
/* define constants for use below */
enum { BASE = 10, MAXP = 30, MAXL = 100 };
int readfp (FILE *fp, int *a, int *b, char **c, int size);
int xstrtol (char *s, int base);
int main (int argc, char **argv) {
int g[MAXP] = {0},
a[MAXP] = {0},
n = 0;
char *names[MAXP] = {NULL};
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
n = readfp (fp, g, a, names, MAXP );
if (fp != stdin) fclose (fp); /* close file if not stdin */
if (n)
for (int i = 0; i < n; i++) {
printf ("%-30s %8d %8d\n", names[i], g[i], a[i]);
free (names[i]); /* free memory allocated by strdup */
}
return 0;
}
int readfp (FILE *fp, int *a, int *b, char **c, int size)
{
char line[MAXL] = "";
int num = 0;
while (num < size && fgets (line, sizeof line, fp))
{
char *delim = " \t\n",
*tmp = strtok (line, delim);
int itmp = 0;
if (tmp)
c[num] = strdup (tmp); /* allocate storage and copy tmp */
else {
fprintf (stderr, "error: strtok failed - c[%d].\n", num);
break;
}
if ((tmp = strtok (NULL, delim)) && (itmp = xstrtol (tmp, BASE)))
a[num] = itmp;
else {
fprintf (stderr, "error: strtok failed - a[%d].\n", num);
break;
}
if ((tmp = strtok (NULL, delim)) && (itmp = xstrtol (tmp, BASE)))
b[num] = itmp;
else {
fprintf (stderr, "error: strtok failed - b[%d].\n", num);
break;
}
num++;
}
return num;
}
int xstrtol (char *s, int base)
{
errno = 0;
char *endptr = NULL;
long v = strtol (s, &endptr, base);
if (errno) {
fprintf (stderr, "xstrtol() error: over/underflow detected.\n");
exit (EXIT_FAILURE);
}
if (s == endptr && v == 0) {
fprintf (stderr, "xstrtol() error: no digits found.\n");
exit (EXIT_FAILURE);
}
if (v < INT_MIN || INT_MAX < v) {
fprintf (stderr, "xstrtol() error: out of range of integer.\n");
exit (EXIT_FAILURE);
}
return (int)v;
}
Example Input File
$ cat dat/names.txt
Ryan,Elizabeth 62 325
McIntyre,Osborne 84 326
DuMond,Kristin 18 327
Larson,Lois 42 328
Thorpe,Trinity 15 329
Ruiz,Pedro 35 330
Ali,Mohammed 60 331
Vashti,Indura 20 332
Example Use/Output
$ ./bin/readnames <dat/names.txt
Ryan,Elizabeth 62 325
McIntyre,Osborne 84 326
DuMond,Kristin 18 327
Larson,Lois 42 328
Thorpe,Trinity 15 329
Ruiz,Pedro 35 330
Ali,Mohammed 60 331
Vashti,Indura 20 332
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/readnames <dat/names.txt
==11303== Memcheck, a memory error detector
==11303== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==11303== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==11303== Command: ./bin/readnames
==11303==
Ryan,Elizabeth 62 325
McIntyre,Osborne 84 326
DuMond,Kristin 18 327
Larson,Lois 42 328
Thorpe,Trinity 15 329
Ruiz,Pedro 35 330
Ali,Mohammed 60 331
Vashti,Indura 20 332
==11303==
==11303== HEAP SUMMARY:
==11303== in use at exit: 0 bytes in 0 blocks
==11303== total heap usage: 8 allocs, 8 frees, 112 bytes allocated
==11303==
==11303== All heap blocks were freed -- no leaks are possible
==11303==
==11303== For counts of detected and suppressed errors, rerun with: -v
==11303== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
You have lots of issues in your code as mentioned by the people in comments. But you should carefully consider the comment by Weather Vane. Right now your names array is char *names[30]; and you assign address of a local variable to it. You need to copy the data in some way(as line goes out of scope after function call). So i would suggest changing your names array to something like :
char names[30][WORD_LEN];
For copying data you might need something like strcpy. I changed your code a bit and was able to see the desired result:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define WORD_LEN 100
void read(FILE *fPtr, int *a, int *b, char c[][WORD_LEN], int size)
{
char line[100];
int num = 0;
while ( fgets(line, 99, fPtr) && num < size ) {
// This is a names array
//strcpy(c[num], strtok (line, " "));
// here the name prints out fine
// !avoid atoi, use sscanf instead
//a[num] = atoi(strtok (NULL, " "));
//b[num] = atoi(strtok (NULL, " "));
sscanf(line, "%s%d%d", c[num], &a[num], &b[num]);
num++;
}
}
void printThis(int* g, int* a, char n[][WORD_LEN], int s)
{
for (int i = 0; i < 2; ++i)
printf("%s %d %d\n", n[i], a[i], g[i]);
}
int main()
{
FILE *fp;
int g[30];
int a[30];
char names[30][WORD_LEN];
// Open file
fp = fopen("input.txt", "r");
read(fp, g, a, names, 30 );
printThis(g, a, names, 30 );
return 0;
}
I changed the file structure to space delimited tokens, and i was able to see the names :
~/work : $ cat input.txt
Rohan 120000 4300000
Prashant 12 43
~/work : $ g++ readCharArray.c
~/work : $ ./a.out
Rohan 4300000 120000
Prashant 43 12
~/work : $
Related
I am trying to parse a csv into a dynamically allocated array of structures, however my attempt crashes with a segmentation fault.
Here is the structure of my data:
SO02773202,5087001,0
SO02773203,5087001,0
SO02773204,5087001,0
SO02773205,5087001,0
SO02773206,5087001,14
This is the struct I am parsing the data into:
typedef struct saleslines{
char* salesid;
char* smmcampaignid;
int numberofbottles;
} saleslines_t;
Here is my attempt at parsing the file:
int read_saleslines(saleslines_t* saleslines, int number_of_lines){
char c;
FILE* fp;
fp = fopen(FILENAME, "r"); /* Open the saleslines file */
if(fp == NULL){ /* Crash if file not found */
printf("Error - file not found\n");
return 0;
}
c = getc(fp);
while (c != EOF){
if (c == '\n'){
number_of_lines += 1;
}
c = getc(fp);
}
printf("Number of lines is %d\n", number_of_lines);
saleslines = (saleslines_t*) malloc((number_of_lines * 2) * sizeof(saleslines_t));
/* allocation of the buffer for every line in the File */
char *buf = (char*) malloc(1000);
char *tmp;
if ( ( fp = fopen(FILENAME, "r" ) ) == NULL )
{
printf( "File could not be opened.\n" );
}
int i = 0;
while (fgets(buf, 255, fp) != NULL){
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
tmp = strtok(buf, ",");
saleslines[i].salesid = strdup(tmp);
tmp = strtok(NULL, ",");
saleslines[i].smmcampaignid = strdup(tmp);
tmp = strtok(NULL, ",");
saleslines[i].numberofbottles = atoi(tmp);
printf("Salesid: %s\nCampaign: %s\nBottles: %i\n\n", saleslines[i].salesid , saleslines[i].smmcampaignid, saleslines[i].numberofbottles);
i++;
}
free(buf);
fclose(fp);
printf("Number of lines is %i\n", number_of_lines);
return number_of_lines;
}
For some reason it parses the file and prints the resulting array of structs, however when I call this function immediately after, it crashes with a segfault:
void print_saleslines_struct(saleslines_t* saleslines, int number_of_lines{
int i;
printf("Number of lines is %i", number_of_lines);
for(i = 0; i < number_of_lines; i++){
printf("Salesid:\t %s\n", saleslines[i].salesid);
printf("Campaign:\t %s\n", saleslines[i].smmcampaignid);
printf("# of Bottles:\t %d\n", saleslines[i].numberofbottles);
}
}
I can't seem to find where this memory bug is.
Here is the initialization and main:
saleslines_t* saleslines;
saleslines_summary_t* saleslines_summary;
saleslines_grouped_t* saleslines_grouped;
int number_of_lines = 0;
int* number_of_linesp = &number_of_lines;
/* Main */
int main(){
int chosen_option;
while(1){
printf("What would you like to do?\n");
printf("1. Read saleslines.txt\n");
printf("2. Print saleslines\n");
printf("3. Summarise saleslines\n");
printf("4. Exit the program\n");
scanf("%d", &chosen_option);
switch(chosen_option){
/* case 1 : number_of_lines = read_saleslines_file(saleslines, number_of_lines); break; */
case 1 : number_of_lines = read_saleslines(saleslines, number_of_lines); break;
case 2 : printf("Number of lines is %i", number_of_lines); print_saleslines_struct(saleslines, number_of_lines); break;
case 3 : summarise_saleslines(saleslines, number_of_linesp, saleslines_summary, saleslines_grouped); break;
case 4 : free(saleslines); free(saleslines_summary); free(saleslines_grouped); return 0;
}
}
return 0;
}
Update
The issue seems to be with my initialization of the array of structures.
When I initialize it like this: saleslines_t* saleslines;
and then malloc like this: saleslines = malloc(number_of_lines + 1 * sizeof(saleslines_t);
I get a segfault.
But if I initialize like this: saleslines[600]; (allocating more than the number of lines in the file), everything works.
How can I get around this? I would like to be able to dynamically allocate the number of entries within the struct array.
Edit 2
Here are the changes as suggested:
int read_saleslines(saleslines_t** saleslines, int number_of_lines);
saleslines_t* saleslines;
int number_of_lines = 0;
int main(){
while(1){
printf("What would you like to do?\n");
printf("1. Read saleslines.txt\n");
printf("2. Print saleslines\n");
printf("3. Summarise saleslines\n");
printf("4. Exit the program\n");
printf("Number of saleslines = %i\n", number_of_lines);
scanf("%d", &chosen_option);
switch(chosen_option){
/* case 1 : number_of_lines = read_saleslines_file(saleslines, number_of_lines); break; */
case 1 : number_of_lines = read_saleslines(&saleslines, number_of_lines); break;
case 2 : printf("Number of lines is %i", number_of_lines); print_saleslines_struct(saleslines, number_of_lines); break;
case 3 : summarise_saleslines(saleslines, number_of_linesp, saleslines_summary, saleslines_grouped); break;
case 4 : free(saleslines); free(saleslines_summary); free(saleslines_grouped); return 0;
}
}
return 0;
}
int read_saleslines(saleslines_t** saleslines, int number_of_lines)
{
char c;
FILE* fp;
fp = fopen(FILENAME, "r"); /* Open the saleslines file */
if(fp == NULL){ /* Crash if file not found */
printf("Error - file not found\n");
return 0;
}
c = getc(fp);
while (c != EOF){
if (c == '\n'){
number_of_lines += 1;
}
c = getc(fp);
}
fclose(fp);
printf("Number of lines is %d\n", number_of_lines);
*saleslines = (saleslines_t*) malloc((number_of_lines + 1) * sizeof(saleslines_t));
/* allocation of the buffer for every line in the File */
char *buf = malloc(25);
char *tmp;
if ( ( fp = fopen(FILENAME, "r" ) ) == NULL )
{
printf( "File could not be opened.\n" );
}
int i = 0;
while (fgets(buf, 25, fp) != NULL){
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
tmp = strtok(buf, ",");
(*saleslines)[i].salesid = strdup(tmp);
tmp = strtok(NULL, ",");
(*saleslines)[i].smmcampaignid = strdup(tmp);
tmp = strtok(NULL, ",");
(*saleslines)[i].numberofbottles = atoi(tmp);
printf("Salesid: %s\nCampaign: %s\nBottles: %i\n\n", saleslines[i]->salesid , saleslines[i]->smmcampaignid, saleslines[i]->numberofbottles);
i++;
}
free(buf);
fclose(fp);
printf("Number of lines is %i\n", number_of_lines);
return number_of_lines;
}
The program now segfaults after reading the first element in the struct array.
You have a problem with the arguments of read_saleslines(). The first argument should be a pointer to an array of your structs, meaning a double pointer.
In
int read_saleslines(saleslines_t* saleslines, int number_of_lines){
you want to modify where saleslines is pointing. saleslines is a local variable of the function, and the scope is that function. Once you exit read_saleslines(), the variable is "destroyed", meaning that the value it holds it is not accessible anymore. Adding another level of indirection, a pointer, you can modify the variable that's defined outside the function, being that (ugly) global or other. So, change that argument so that the function prototype matches
int read_saleslines(saleslines_t** saleslines, int *);
and change the places where you access it inside the function (adding an * to access it, for example:
saleslines = (saleslines_t*) malloc((number_of_lines * ...
to
*saleslines = (saleslines_t*) malloc((number_of_lines * ...
and
saleslines[i].salesid = strdup(tmp);
to
(*saleslines)[i].salesid = strdup(tmp);
Then add an & where you use the variable outside the function:
number_of_lines = read_saleslines(saleslines, number_of_lines);
changes to
some_var = read_saleslines(&saleslines, &number_of_lines);
That will make you code work.
You have a large number of errors in your code, and with your approach in general. There is no need to make two-passes over the file to determine the number of lines before allocating and then re-reading the file in an attempt to parse the data. Further, there is no need to tokenize each line to separate the comma-separated-values, sscanf() to parse the two strings and one int is sufficient here after reading each line with fgets.
While you are free to pass any mix of parameters you like and return whatever you like, since you are allocating for an array of struct and reading values into the array, it makes sense to return a pointer to the allocated array from your function (or NULL on failure) and simply update a parameter passed as a pointer to make the total number of lines read available back in the caller.
Further, generally you want to open and validate the file in the caller and pass a FILE* parameter passing the open file stream to your function. With that in mind, you could refactor your function as:
/* read saleslines into array of saleslines_t, allocating for
* salesid, and smmcampaignid within each struct. Return pointer
* to allocated array on success with lines updated to hold the
* number of elements, or NULL otherwise.
*/
saleslines_t *read_saleslines (FILE *fp, size_t *lines)
{
Within your function, you simply need a buffer to hold each line read, a counter to track the number of elements allocated in your array, and a pointer to your array to return. For example, you could do something like the following to handle all three:
char buf[MAXC]; /* buffer to hold line */
size_t maxlines = MINL; /* maxlines allocated */
saleslines_t *sales = NULL; /* pointer to array of struct */
(note: since you are tracking the number of lines read through the pointer lines passed as a parameter, it would make sense to initialize the value at that address to zero)
Now the work of your function begins, you want to read each line into buf and parse the needed information from each line. Since salesid and smmcampaignid are both pointers-to-char in your struct, you will need to allocate a block of memory for each string parsed from the line, copy the string to the new block of memory, and then assign the beginning address for the bock to each of your pointers. To "dynamically" handle allocating elements for your struct, you simply check if the number of lines (*lines) filled equals against the number allocated (maxlines), (or if *lines is zero indicating a need for an initial allocation), and realloc in both cases to either realloc (or newly allocate) storage for your array of struct.
When you realloc you always realloc using a temporary pointer so if realloc fails and returns NULL, you don't overwrite your pointer to the currently allocated block with NULL thereby creating a memory leak.
Putting all that together at the beginning of your function may seem daunting, but it is actually straight forward, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line in file */
char id[MAXC], cid[MAXC]; /* temp arrays to hold strings */
int bottles; /* temp int for numberofbottles */
if (*lines == maxlines || !*lines) { /* check if realloc req'd */
/* always realloc with a temp pointer */
void *tmp = realloc (sales, 2 * maxlines * sizeof *sales);
if (!tmp) { /* if realloc fails, original pointer still valid */
perror ("realloc-sales"); /* throw error */
return sales; /* return current pointer */
} /* (don't exit or return NULL) */
sales = tmp; /* assign reallocated block to sales */
/* (optional) zero newly allocated memory */
memset (sales + *lines, 0, maxlines * sizeof *sales);
maxlines *= 2; /* update maxlines allocated */
}
Now you are ready to parse the wanted information from your line with sscanf, and then following a successful parse of information, you can allocate for each of your salesid and smmcampaignid pointers, copy the parsed information to the new blocks of memory assigning the beginning address to each pointer, respectively, e.g.
/* parse needed data from line (sscanf is fine here) */
if (sscanf (buf, "%1023[^,],%1023[^,],%d", id, cid, &bottles) == 3) {
size_t idlen = strlen (id), /* get lengths of strings */
cidlen = strlen (cid);
sales[*lines].salesid = malloc (idlen + 1); /* allocate string */
if (!sales[*lines].salesid) { /* validate! */
perror ("malloc-sales[*lines].salesid");
break;
}
sales[*lines].smmcampaignid = malloc (cidlen + 1); /* ditto */
if (!sales[*lines].smmcampaignid) {
perror ("malloc-sales[*lines].smmcampaignid");
break;
}
memcpy (sales[*lines].salesid, id, idlen + 1); /* copy strings */
memcpy (sales[*lines].smmcampaignid, cid, cidlen + 1);
sales[(*lines)++].numberofbottles = bottles; /* assign int */
} /* (note lines counter updated in last assignment) */
(note: you can use strdup to both get the length of each string parsed and allocate sufficient memory to hold the string and assign that to your pointer in one-shot, e.g. sales[*lines].salesid = strdup (id);, but... strdup is not required to be included in C99 or later, so it is just as simple to get the length, allocate length + 1 bytes and then memcpy your string manually to ensure portability. Further, since strdup allocates memory, you must validate the pointer returned -- something overlooked by 99% of those using it.)
That's it, when fgets() fails, you have reached EOF, now simply:
return sales; /* return dynamically allocated array of struct */
}
Putting it altogether in a short, working example that takes the filename to read as the first argument to your program (or reads from stdin by default if no argument is given), you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MINL 2
typedef struct saleslines{
char *salesid;
char *smmcampaignid;
int numberofbottles;
} saleslines_t;
/* read saleslines into array of saleslines_t, allocating for
* salesid, and smmcampaignid within each struct. Return pointer
* to allocated array on success with lines updated to hold the
* number of elements, or NULL otherwise.
*/
saleslines_t *read_saleslines (FILE *fp, size_t *lines)
{
char buf[MAXC]; /* buffer to hold line */
size_t maxlines = MINL; /* maxlines allocated */
saleslines_t *sales = NULL; /* pointer to array of struct */
*lines = 0; /* zero lines */
while (fgets (buf, MAXC, fp)) { /* read each line in file */
char id[MAXC], cid[MAXC]; /* temp arrays to hold strings */
int bottles; /* temp int for numberofbottles */
if (*lines == maxlines || !*lines) { /* check if realloc req'd */
/* always realloc with a temp pointer */
void *tmp = realloc (sales, 2 * maxlines * sizeof *sales);
if (!tmp) { /* if realloc fails, original pointer still valid */
perror ("realloc-sales"); /* throw error */
return sales; /* return current pointer */
} /* (don't exit or return NULL) */
sales = tmp; /* assign reallocated block to sales */
/* (optional) zero newly allocated memory */
memset (sales + *lines, 0, maxlines * sizeof *sales);
maxlines *= 2; /* update maxlines allocated */
}
/* parse needed data from line (sscanf is fine here) */
if (sscanf (buf, "%1023[^,],%1023[^,],%d", id, cid, &bottles) == 3) {
size_t idlen = strlen (id), /* get lengths of strings */
cidlen = strlen (cid);
sales[*lines].salesid = malloc (idlen + 1); /* allocate string */
if (!sales[*lines].salesid) { /* validate! */
perror ("malloc-sales[*lines].salesid");
break;
}
sales[*lines].smmcampaignid = malloc (cidlen + 1); /* ditto */
if (!sales[*lines].smmcampaignid) {
perror ("malloc-sales[*lines].smmcampaignid");
break;
}
memcpy (sales[*lines].salesid, id, idlen + 1); /* copy strings */
memcpy (sales[*lines].smmcampaignid, cid, cidlen + 1);
sales[(*lines)++].numberofbottles = bottles; /* assign int */
} /* (note lines counter updated in last assignment) */
}
return sales; /* return dynamically allocated array of struct */
}
int main (int argc, char **argv) {
saleslines_t *sales = NULL; /* pointer to saleslines_t */
size_t nlines;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
sales = read_saleslines (fp, &nlines); /* read saleslines */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < nlines; i++) { /* loop over each */
printf ("sales[%2zu]: %s %s %2d\n", i, sales[i].salesid,
sales[i].smmcampaignid, sales[i].numberofbottles);
free (sales[i].salesid); /* free salesid */
free (sales[i].smmcampaignid); /* free smmcampaignid */
}
free (sales); /* free sales */
return 0;
}
Example Use/Output
$ ./bin/saleslines dat/saleslines.txt
sales[ 0]: SO02773202 5087001 0
sales[ 1]: SO02773203 5087001 0
sales[ 2]: SO02773204 5087001 0
sales[ 3]: SO02773205 5087001 0
sales[ 4]: SO02773206 5087001 14
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/saleslines dat/saleslines.txt
==19819== Memcheck, a memory error detector
==19819== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19819== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==19819== Command: ./bin/saleslines dat/saleslines.txt
==19819==
sales[ 0]: SO02773202 5087001 0
sales[ 1]: SO02773203 5087001 0
sales[ 2]: SO02773204 5087001 0
sales[ 3]: SO02773205 5087001 0
sales[ 4]: SO02773206 5087001 14
==19819==
==19819== HEAP SUMMARY:
==19819== in use at exit: 0 bytes in 0 blocks
==19819== total heap usage: 13 allocs, 13 frees, 935 bytes allocated
==19819==
==19819== All heap blocks were freed -- no leaks are possible
==19819==
==19819== For counts of detected and suppressed errors, rerun with: -v
==19819== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
There is nothing difficult in dynamically allocating for anything. Just take it in small enough pieces that you dot all "I's" and cross all "T's" for each pointer requiring allocation. Look things over and let me know if you have further questions.
I am trying to read a text file with the following format, using fgets() and strtok().
1082018 1200 79 Meeting with President
2012018 1200 79 Meet with John at cinema
2082018 1400 30 games with Alpha
3022018 1200 79 sports
I need to separate the first value from the rest of the line, for example:
key=21122019, val = 1200 79 Meeting with President
To do so I am using strchr() for val and strtok() for key, however, the key value remains unchanged when reading from file. I can't understand why this is happening since I am allocating space for in_key inside the while loop and placing inside an array at a different index each time.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 1000 // max number of lines to be read
#define VALLEN 100
#define MAXC 1024
#define ALLOCSIZE 1000 /*size of available space*/
static char allocbuf[ALLOCSIZE]; /* storage for alloc*/
static char *allocp = allocbuf; /* next free position*/
char *alloc(int n) { /* return a pointer to n characters*/
if (allocbuf + ALLOCSIZE - allocp >= n) { /*it fits*/
allocp += n;
return allocp - n; /*old p*/
} else /*not enough room*/
return 0;
}
int main(int argc, char** argv) {
FILE *inp_cal;
inp_cal = fopen("calendar.txt", "r+");
char buf[MAXC];
char *line[1024];
char *p_line;
char *in_val_arr[100];
char *in_key_arr[100];
int count = 0;
char delimiter[] = " ";
if (inp_cal) {
printf("Processing file...\n");
while (fgets(buf, MAXC, inp_cal)) {
p_line = malloc(strlen(buf) + 1); // malloced with size of buffer.
char *in_val;
char *in_key;
strcpy(p_line, buf); //used to create a copy of input buffer
line[count] = p_line;
/* separating the line based on the first space. The words after
* the delimeter will be copied into in_val */
char *copy = strchr(p_line, ' ');
if (copy) {
if ((in_val = alloc(strlen(line[count]) + 1)) == NULL) {
return -1;
} else {
strcpy(in_val, copy + 1);
printf("arr: %s", in_val);
in_val_arr[count] = in_val;
}
} else
printf("Could not find a space\n");
/* We now need to get the first word from the input buffer*/
if ((in_key = alloc(strlen(line[count]) + 1)) == NULL) {
return -1;
}
else {
in_key = strtok(buf, delimiter);
printf("%s\n", in_key);
in_key_arr[count] = in_key; // <-- Printed out well
count++;
}
}
for (int i = 0; i < count; ++i)
printf("key=%s, val = %s", in_key_arr[i], in_val_arr[i]); //<-- in_key_arr[i] contains same values throughout, unlike above
fclose(inp_cal);
}
return 0;
}
while-loop output (correct):
Processing file...
arr: 1200 79 Meeting with President
1082018
arr: 1200 79 Meet with John at cinema
2012018
arr: 1400 30 games with Alpha
2082018
arr: 1200 79 sports
3022018
for-loop output (incorrect):
key=21122019, val = 1200 79 Meeting with President
key=21122019, val = 1200 79 Meet with John
key=21122019, val = 1400 30 games with Alpha
key=21122019, val = 1200 79 sports
Any suggestions on how this can be improved and why this is happening? Thanks
Continuing for the comment, in attempting to use strtok to separate your data into key, val, somenum and the remainder of the line as a string, you are making things harder than it need be.
If the beginning of your lines are always:
key val somenum rest
you can simply use sscanf to parse key, val and somenum into, e.g. three unsigned values and the rest of the line into a string. To help preserve the relationship between each key, val, somenum and string, storing the values from each line in a struct is greatly ease keeping track of everything. You can even allocate for the string to minimize storage to the exact amount required. For example, you could use something like the following:
typedef struct { /* struct to handle values */
unsigned key, val, n;
char *s;
} keyval_t;
Then within main() you could allocate for some initial number of struct, keep an index as a counter, loop reading each line using a temporary stuct and buffer, then allocating for the string (+1 for the nul-terminating character) and copying the values to your struct. When the number of structs filled reaches your allocated amount, simply realloc the number of structs and keep going.
For example, let's say you initially allocate for NSTRUCT struts and read each line into buf, e.g.
...
#define NSTRUCT 8 /* initial struct to allocate */
#define MAXC 1024 /* read buffer size (don't skimp) */
...
/* allocate/validate storage for max struct */
if (!(kv = malloc (max * sizeof *kv))) {
perror ("malloc-kv");
return 1;
}
...
size_t ndx = 0, /* used */
max = NSTRUCT; /* allocated */
keyval_t *kv = NULL; /* ptr to struct */
...
while (fgets (buf, MAXC, fp)) { /* read each line of input */
...
Within your while loop, you simply need to parse the values with sscanf, e.g.
char str[MAXC];
size_t len;
keyval_t tmp = {.key = 0}; /* temporary struct for parsing */
if (sscanf (buf, "%u %u %u %1023[^\n]", &tmp.key, &tmp.val, &tmp.n,
str) != 4) {
fprintf (stderr, "error: invalid format, line '%zu'.\n", ndx);
continue;
}
With the values parsed, you check whether your index has reached the number of struct you have allocated and realloc if required (note the use of a temporary pointer to realloc), e.g.
if (ndx == max) { /* check if realloc needed */
/* always realloc with temporary pointer */
void *kvtmp = realloc (kv, 2 * max * sizeof *kv);
if (!kvtmp) {
perror ("realloc-kv");
break; /* don't exit, kv memory still valid */
}
kv = kvtmp; /* assign new block to pointer */
max *= 2; /* increment max allocated */
}
Now with storage for the struct, simply get the length of the string, copy the unsigned values to your struct, and allocate length + 1 chars for kv[ndx].s and copy str to kv[ndx].s, e.g.
len = strlen(str); /* get length of str */
kv[ndx] = tmp; /* assign tmp values to kv[ndx] */
kv[ndx].s = malloc (len + 1); /* allocate block for str */
if (!kv[ndx].s) { /* validate */
perror ("malloc-kv[ndx].s");
break; /* ditto */
}
memcpy (kv[ndx++].s, str, len + 1); /* copy str to kv[ndx].s */
}
(note: you can use strdup if you have it to replace malloc through memcpy with kv[ndx].s = strdup (str);, but since strdup allocates, don't forget to check kv[ndx].s != NULL before incrementing ndx if you go that route)
That's pretty much the easy and robust way to capture your data. It is now contained in an allocated array of struct which you can use as needed, e.g.
for (size_t i = 0; i < ndx; i++) {
printf ("kv[%2zu] : %8u %4u %2u %s\n", i,
kv[i].key, kv[i].val, kv[i].n, kv[i].s);
free (kv[i].s); /* free string */
}
free (kv); /* free stucts */
(don't forget to free the memory you allocate)
Putting it altogether, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NSTRUCT 8 /* initial struct to allocate */
#define MAXC 1024 /* read buffer size (don't skimp) */
typedef struct { /* struct to handle values */
unsigned key, val, n;
char *s;
} keyval_t;
int main (int argc, char **argv) {
char buf[MAXC]; /* line buffer */
size_t ndx = 0, /* used */
max = NSTRUCT; /* allocated */
keyval_t *kv = NULL; /* ptr to struct */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("fopen-file");
return 1;
}
/* allocate/validate storage for max struct */
if (!(kv = malloc (max * sizeof *kv))) {
perror ("malloc-kv");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line of input */
char str[MAXC];
size_t len;
keyval_t tmp = {.key = 0}; /* temporary struct for parsing */
if (sscanf (buf, "%u %u %u %1023[^\n]", &tmp.key, &tmp.val, &tmp.n,
str) != 4) {
fprintf (stderr, "error: invalid format, line '%zu'.\n", ndx);
continue;
}
if (ndx == max) { /* check if realloc needed */
/* always realloc with temporary pointer */
void *kvtmp = realloc (kv, 2 * max * sizeof *kv);
if (!kvtmp) {
perror ("realloc-kv");
break; /* don't exit, kv memory still valid */
}
kv = kvtmp; /* assign new block to pointer */
max *= 2; /* increment max allocated */
}
len = strlen(str); /* get length of str */
kv[ndx] = tmp; /* assign tmp values to kv[ndx] */
kv[ndx].s = malloc (len + 1); /* allocate block for str */
if (!kv[ndx].s) { /* validate */
perror ("malloc-kv[ndx].s");
break; /* ditto */
}
memcpy (kv[ndx++].s, str, len + 1); /* copy str to kv[ndx].s */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < ndx; i++) {
printf ("kv[%2zu] : %8u %4u %2u %s\n", i,
kv[i].key, kv[i].val, kv[i].n, kv[i].s);
free (kv[i].s); /* free string */
}
free (kv); /* free stucts */
}
Example Use/Output
Using your data file as input, you would receive the following:
$ ./bin/fgets_sscanf_keyval <dat/keyval.txt
kv[ 0] : 1082018 1200 79 Meeting with President
kv[ 1] : 2012018 1200 79 Meet with John at cinema
kv[ 2] : 2082018 1400 30 games with Alpha
kv[ 3] : 3022018 1200 79 sports
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/fgets_sscanf_keyval <dat/keyval.txt
==6703== Memcheck, a memory error detector
==6703== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6703== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6703== Command: ./bin/fgets_sscanf_keyval
==6703==
kv[ 0] : 1082018 1200 79 Meeting with President
kv[ 1] : 2012018 1200 79 Meet with John at cinema
kv[ 2] : 2082018 1400 30 games with Alpha
kv[ 3] : 3022018 1200 79 sports
==6703==
==6703== HEAP SUMMARY:
==6703== in use at exit: 0 bytes in 0 blocks
==6703== total heap usage: 5 allocs, 5 frees, 264 bytes allocated
==6703==
==6703== All heap blocks were freed -- no leaks are possible
==6703==
==6703== For counts of detected and suppressed errors, rerun with: -v
==6703== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me now if you have any further questions. If you need to further split kv[i].s, then you can think about using strtok.
You are storing the same pointer in the in_key_arr over and over again.
You roughly need this:
in_key = strtok(buf, delimiter);
printf("%s\n", in_key);
char *newkey = malloc(strlen(in_key) + 1); // <<<< allocate new memory
strcpy(newkey, in_key);
in_key_arr[count] = newkey; // <<<< store newkey
count++;
Disclaimer:
no error checking is done for brevity
the malloced memory needs to be freed once you're done with it.
you are assigning an address with the call to alloc then reassigning with call to strtok? rewriting the same address? Copy return from strtok to in_key?
char *copy = strchr(p_line, ' ');
if (copy) {
if ((in_val = alloc(strlen(line[count]) + 1)) == NULL) {
return -1;
} else {
printf("arr: %ul\n", in_val);
strcpy(in_val, copy + 1);
printf("arr: %s", in_val);
in_val_arr[count] = in_val;
}
} else
printf("Could not find a space\n");
/* We now need to get the first word from the input buffer*/
if ((in_key = alloc(strlen(line[count]) + 1)) == NULL) {
return -1;
}
else {
printf("key: %ul\n", in_key);
in_key = strtok(buf, delimiter);
printf("key:\%ul %s\n",in_key, in_key);
in_key_arr[count++] = in_key; // <-- Printed out well
}
output:
allocbuf: 1433760064l
Processing file...
all: 1433760064l
arr: 1433760064l
arr: 1200 79 Meeting with President
all: 1433760104l
key: 1433760104l
key:4294956352l 1082018
this change fixed it:
strcpy(in_key, strtok(buf, delimiter));
I pass char ** input from main() to processInExp() function, then I pass it again from processInExp() function to getInput() function to dynamically allocate it over while reading through the file.
Inside getInput() function input is allocated memory properly when checked, but while using it in in processInExp() it encounters gets runtime error. What can be the issue?
Below is my code:
int getInput(char ** input, const char * fileName)
{
int numInput = 0;
int i, j;
char c;
char tempInput[100];
FILE * pFile;
if((pFile = fopen(fileName, "r")) == NULL)
{
printf("Cannot read file %s\n", fileName);
system("PAUSE");
exit(1);
}
while(!feof(pFile))
{
c = fgetc(pFile);
if(c == '\n') ++numInput;
}
/* printf("%d\n", numInput); */
input = (char**)malloc(numInput * sizeof(char*)); /* #2 MALLOC input */
rewind(pFile);
for(i = 0; !feof(pFile); ++i)
{
fscanf(pFile, "%[^\n]%*c", tempInput);
/* printf("%s\n", tempInput); */
input[i] = (char*)malloc((strlen(tempInput) + 1) * sizeof(char)); /* #3 MALLOC input[] */
strcpy(input[i], tempInput);
/* printf("%s\n", input[i]); */ /* #4 PRINT OUT PERFECTLY */
memset(tempInput, 0, sizeof(tempInput));
}
fclose(pFile);
return numInput;
}
void processInExp(char ** input, char ** output, const char * fileName)
{
int numFormula;
int i;
numFormula = getInput(input, fileName); /* #1 PASSING input */
/* printf("%s\n", input[0]); */ /* #5 RUNTIME ERROR */
output = (char**)malloc(numFormula * sizeof(char*));
system("PAUSE");
for(i = 0; i < numFormula; ++i)
{
convertIntoPost(input[i], output[i]);
printf("%d. %s -> %s", (i + 1), input[i], output[i]);
}
}
While others have pointed out the issue with pass by value, there is another issue where learning can occur. There is no need to pre-read the file to determine the number of characters or lines and then rewind the file to read each line.
Take a look at getline which returns the number of characters read. All you need to do is keep a sum variable and after reading all line, simply return (or update a pointer you provided as an argument) and you are done. Of course you can do the same with fscanf or fgets by calling strlen after reading the line.
The following is a short example of reading a text file in one pass while determining the number of characters (without the newline) and returning that information to the calling function. Just as you needed to pass a pointer to your array of pointers in getInput, we will use pointers passed as arguments to return the line and character counts to our calling function. If you declare and call the function to read the file as follows:
size_t nline = 0; /* placeholders to be filled by readtxtfile */
size_t nchar = 0; /* containing number of lines/chars in file */
...
char **file = readtxtfile (fn, &nline, &nchar);
By declaring the variables in the calling function, and then passing pointers to the variables as arguments (using the urnary &), you can update the values in the function and have those values available for use back in main (or whatever function you called readtxtfile from.)
A quick example illustrating these points could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NMAX 256
char **readtxtfile (char *fn, size_t *idx, size_t *sum);
void prn_chararray (char **ca);
void free_chararray (char **ca);
int main (int argc, char **argv) {
size_t nline = 0; /* placeholders to be filled by readtxtfile */
size_t nchar = 0; /* containing number of lines/chars in file */
char *fn = argc > 1 ? argv[1] : NULL;/* if fn not given, read stdin */
/* read each file into an array of strings,
* number of lines/chars read updated in nline, nchar
*/
char **file = readtxtfile (fn, &nline, &nchar);
/* output number of lines read & chars read and from where */
printf ("\n read '%zu' lines & '%zu' chars from file: %s\n\n",
nline, nchar, fn ? fn : "stdin");
/* simple print function to print all lines */
if (file) prn_chararray (file);
/* simple free memory function */
if (file) free_chararray (file);
return 0;
}
/* simple function using getline to read any text file and return
* the lines read in an array of pointers. user is responsible for
* freeing memory when no longer needed
*/
char **readtxtfile (char *fn, size_t *idx, size_t *sum)
{
char *ln = NULL; /* NULL forces getline to allocate */
size_t n = 0; /* line buf size (0 - use default) */
ssize_t nchr = 0; /* number of chars actually read */
size_t nmax = NMAX; /* check for reallocation */
char **array = NULL; /* array to hold lines read */
FILE *fp = NULL; /* file pointer to open file fn */
/* open / validate file or read stdin */
fp = fn ? fopen (fn, "r") : stdin;
if (!fp) {
fprintf (stderr, "%s() error: file open failed '%s'.", __func__, fn);
return NULL;
}
/* allocate NMAX pointers to char* */
if (!(array = calloc (NMAX, sizeof *array))) {
fprintf (stderr, "%s() error: memory allocation failed.", __func__);
return NULL;
}
/* read each line from stdin - dynamicallly allocated */
while ((nchr = getline (&ln, &n, fp)) != -1)
{
/* strip newline or carriage rtn */
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0;
*sum += nchr; /* add chars in line to sum */
array[*idx] = strdup (ln); /* allocate/copy ln to array */
(*idx)++; /* increment value at index */
if (*idx == nmax) { /* if lines exceed nmax, reallocate */
char **tmp = realloc (array, nmax * 2);
if (!tmp) {
fprintf (stderr, "%s() error: reallocation failed.\n", __func__);
exit (EXIT_FAILURE); /* or return NULL; */
}
array = tmp;
nmax *= 2;
}
}
if (ln) free (ln); /* free memory allocated by getline */
if (fp != stdin) fclose (fp); /* close open file descriptor */
return array;
}
/* print an array of character pointers. */
void prn_chararray (char **ca)
{
register size_t n = 0;
while (ca[n])
{
printf (" arr[%3zu] %s\n", n, ca[n]);
n++;
}
}
/* free array of char* */
void free_chararray (char **ca)
{
if (!ca) return;
register size_t n = 0;
while (ca[n])
free (ca[n++]);
free (ca);
}
Use/Output
$ ./bin/getline_ccount <dat/fc-list-fonts.txt
read '187' lines & '7476' chars from file: stdin
arr[ 0] andalemo.ttf: Andale Mono - Regular
arr[ 1] arialbd.ttf: Arial - Bold
arr[ 2] arialbi.ttf: Arial - Bold Italic
arr[ 3] ariali.ttf: Arial - Italic
arr[ 4] arialnbi.ttf: Arial
arr[ 5] arialnb.ttf: Arial
arr[ 6] arialni.ttf: Arial
arr[ 7] arialn.ttf: Arial
arr[ 8] arial.ttf: Arial - Regular
arr[ 9] ARIALUNI.TTF: Arial Unicode MS - Regular
arr[ 10] ariblk.ttf: Arial
arr[ 11] Bailey Script Regular.ttf: Bailey Script - Regular
arr[ 12] Bailey_Script_Regular.ttf: Bailey Script - Regular
arr[ 13] Belwe Gotisch.ttf: Belwe Gotisch - Regular
arr[ 14] Belwe_Gotisch.ttf: Belwe Gotisch - Regular
<snip>
Memory/Leak Check
Whenever you allocated/free memory in your code, don't forget to use a memory checker to insure there are no memory errors or leaks in your code:
$ valgrind ./bin/getline_ccount <dat/fc-list-fonts.txt
==20259== Memcheck, a memory error detector
==20259== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==20259== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==20259== Command: ./bin/getline_readfile_function
==20259==
read '187' line from file: stdin
arr[ 0] andalemo.ttf: Andale Mono - Regular
arr[ 1] arialbd.ttf: Arial - Bold
arr[ 2] arialbi.ttf: Arial - Bold Italic
arr[ 3] ariali.ttf: Arial - Italic
<snip>
==20259==
==20259== HEAP SUMMARY:
==20259== in use at exit: 0 bytes in 0 blocks
==20259== total heap usage: 189 allocs, 189 frees, 9,831 bytes allocated
==20259==
==20259== All heap blocks were freed -- no leaks are possible
==20259==
==20259== For counts of detected and suppressed errors, rerun with: -v
==20259== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Follow On From Comment
There are several issues with the code you posted in the comment:
for(i = 0; !feof(pFile); ++i) {
fscanf(pFile, "%[^\n]%*c", tempInput);
/* printf("%s\n", tempInput); */
input[i] = (char*)malloc((strlen(tempInput) + 1) * sizeof(char));
strcpy(input[i], tempInput);
printf("%s\n", input[i]);
memset(tempInput, 0, sizeof(tempInput));
}
for(i = 0; i < numInput; ++i) {
convertIntoPost(input[i], output[i]);
}
First, read the link in the first comment about why feof can cause problems when using it to indicate EOF in a loop. Second, functions have return values, the ability to use them for an advantage tells you whether you are using the correct function for the job.
The difficulty you are having trying to shoehorn reading an entire line with fscanf should be telling you something... The problem you have backed into by your choice of the format specifier "%[^\n]%*c" to read a line containing whitespace is the exact reason fscanf is NOT the proper tool for the job.
Why? The scanf family of functions were created to read discrete values. Their return is based on:
the number of input items successfully matched and assigned
Using your format specifier, the number of items read on success is 1. The *%c reads and discards the newline, but is NOT added to the item count. This causes a BIG problem when trying to read a file that can contain blank lines. What happens then? You experience an input failure and fscanf returns 0 -- but it is still very much a valid line. When that occurs, nothing is read. You cannot check the return as being >= 0 because when you encounter a blank line you loop forever...
With your format specifier, you cannot check for EOF either. Why? With the scanf family of functions:
The value EOF is returned if the end of input is reached before
either the first successful conversion or a matching failure
occurs.
That will never occur in your case because you have an input failure with fscanf (not end of input) and no matching failure has occurred. Are you starting to see why fscanf may not be the right tool for the job?
The C library provides two functions for line-oriented input. They are fgets and getline. Both read an entire line of text into the line buffer. This will include the newline at the end of each line (including blank lines). So when you use either to read text, it is a good idea to remove the newline by overwriting with a null-terminating character.
Which to use? With fgets, you can limit the number of characters read by sizing the character buffer appropriately. getline is now part of the C library, and it provides the added benefit of returning the number of characters actually read (a bonus), but it will read the line no matter how long it is because it dynamically allocates the buffer for you. I prefer it, but just know that you need to check the number of characters it has read.
Since I provided a getline example above, your read loop can be much better written with fgets as follows:
while (fgets (tempInput, MAXL, pFile) != NULL) {
nchr = strlen (tempInput);
while (nchr && (tempInput[nchr-1] == '\n' || tempInput[nchr-1] == '\r'))
tempInput[--nchr] = 0; /* strip newlines & carriage returns */
input[i++] = strdup (tempInput); /* allocates & copies tempInput */
}
numInput = i;
Next, your allocation does not need to be cast to (char *). The return of malloc and calloc is just a pointer to (i.e. the address of) the block of memory allocated. (it is the same no matter what you are allocating memory for) There is no need for sizeof (char). It is always 1. So just write:
input[i] = malloc (strlen(tempInput) + 1);
strcpy (input[i], tempInput);
A more convenient way to both allocate and copy is using strdup. With strdup, the two lines above become simply:
input[i++] = strdup (tempInput); /* allocates & copies */
Next, there is no need for memset.
memset(tempInput, 0, sizeof(tempInput));
If tempInput is declared to hold 100 chars with say: tempInput[100], you can read strings up to 99 char into the same buffer over-and-over again without ever having to zero the memory. Why? Stings are null-terminated. You don't care what is in the buffer after the null-terminator...
That's a lot to take in. Putting it all together in a short example, you could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 256
/* dummy function */
void convertIntoPost (char *in, char **out)
{
size_t i = 0, len = strlen (in);
*out = calloc (1, len + 1);
for (i = 0; i < len; i++) {
(*out)[len-i-1] = in[i];
}
}
int main (int argc, char **argv) {
char tempInput[MAXL] = {0};
char **input = NULL, **output = NULL;
size_t i = 0, numInput = 0;
size_t nchr = 0;
FILE *pFile = NULL;
pFile = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!pFile) {
fprintf (stderr, "error: file open failed '%s'.\n",
argv[1] ? argv[1] : "stdin");
return 1;
}
input = calloc (1, MAXL); /* allocate MAXL pointer for input & output */
output = calloc (1, MAXL); /* calloc allocates and sets memory to 0-NULL */
if (!input || !output) { /* validate allocation */
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
while (fgets (tempInput, MAXL, pFile) != NULL) {
nchr = strlen (tempInput);
while (nchr && (tempInput[nchr-1] == '\n' || tempInput[nchr-1] == '\r'))
tempInput[--nchr] = 0;
input[i++] = strdup (tempInput); /* allocates & copies */
}
numInput = i;
fclose (pFile);
/* call convertIntoPost with input[i] and &output[i] */
for (i = 0; i < numInput; ++i) {
convertIntoPost (input[i], &output[i]);
printf (" input[%2zu]: %-25s output[%2zu]: %s\n",
i, input[i], i, output[i]);
}
/* free all memory */
for (i = 0; i < numInput; ++i) {
free (input[i]), free (output[i]);
}
free (input), free (output);
return 0;
}
Example Output
$ ./bin/feoffix ../dat/captnjack.txt
input[ 0]: This is a tale output[ 0]: elat a si sihT
input[ 1]: Of Captain Jack Sparrow output[ 1]: worrapS kcaJ niatpaC fO
input[ 2]: A Pirate So Brave output[ 2]: evarB oS etariP A
input[ 3]: On the Seven Seas. output[ 3]: .saeS neveS eht nO
Notes On Compiling Your Code
Always compile your code with Warnings enabled. That way the compiler can help point out areas where your code may have ambiguity, etc.. To enable warnings when you compile, simply add -Wall and -Wextra to your compile string. (If you really want all warnings, add -pedantic (definition: overly concerned with trivial details)). The take the time to read and understand what the compiler is telling you with the warnings (they are really very good, and you will quickly learn what each means). Then... go fix the problems so your code compiles without any warnings.
There are only very rare and limited circumstances where it is permissible to 'understand and choose to allow' a warning to remain (like when using a library where you have no access to the source code)
So putting it all together, when you compile your code, at a minimum you should be compiling with the following for testing and development:
gcc -Wall -Wextra -o progname progname.c -g
With gcc, the -g option tell the compiler to produce additional debugging information for use with the debugger gdb (learn it).
When you have all the bugs worked out and you are ready for a final compile of your code, you will want to add optimizations like the optimization level -On (that's capital O [not zero] where 'n' is the level 1, 2, or 3 (0 is default), -Ofast is essentially -O3 with a few additional optimizations). You may also want to consider telling the compiler to inline your functions when possible with -finline-functions to eliminate function call overhead. So for final compile you will want something similar to:
gcc -Wall -Wextra -finline-functions -Ofast -o progname progname.c
The optimizations can produce a 10-fold increase in performance and decrease in your program execution time (that's a 1000% increase in performance in some cases (300-500% improvement is common)). Well worth adding a couple of switches.
C uses pass-by-value for function argument passing. So, from inside the function getInput(), you cannot change the variable input and expect that change to be reflected back in the actual argument, passed to the function. For that, you'll need a pointer-to variable to be passed, like in this case, you need to do
int getInput(char *** input, const char * fileName) { //notice the extra *
and need to call it like
char ** inp = NULL;
getInput(&inp, ..........);
Then, getInput() will be able to allocate memory to *input inside the function which will be reflected into inp.
Otherwise, after returning from the getInput(), the actual argument will still be uninitialized and using that further (in your case, in the for loop in processInExp() function) will lead to undefined behaviour.
That said, two more important things to notice,
Please see why not to cast the return value of malloc() and family in C.
Check Why is while ( !feof (file) ) always wrong?
As Sourav mentioned, C uses pass-by-value for argument passing, so the input variable within the scope of processInExp has the value of the address of the memory previously allocated in main.
This results in a segmentation fault when you print input[0]. This is because printf is trying to print the string located at the address relative to the previously allocated memory instead of memory allocated to input in the getInput function to which you copied the string.
A solution would be to pass a pointer to input, so your function signature would like like this: int getInput(char *** input, const char * fileName). You would then need to change any references to input to *input in order to dereference the pointer, and pass input's pointer to getInput like this: getInput(&input, fileName).
The C language is pass-by-value without exception.
A function is not able to change the value of actual parameters.
I have a text file which may contain one or up to 400 numbers. Each number is separated by a comma and a semicolon is used to indicate end of numbers stream.
At the moment I am reading the text file line by line using the fgets. For this reason I am using a fixed array of 1024 elements (the maximum characters per line for a text file).
This is not the ideal way how to implement this since if only one number is inputted in the text file, an array of 1024 elements will we pointless.
Is there a way to use fgets with the malloc function (or any other method) to increase memory efficiency?
If you are looking into using this in a production code then I would request you to follow the suggestions put in the comments section.
But if you requirement is more for learning or school, then here is a complex approach.
Pseudo code
1. Find the size of the file in bytes, you can use "stat" for this.
2. Since the file format is known, from the file size, calculate the number of items.
3. Use the number of items to malloc.
Voila! :p
How to find file size
You can use stat as shown below:
#include <sys/stat.h>
#include <stdio.h>
int main(void)
{
struct stat st;
if (stat("file", &st) == 0) {
printf("fileSize: %d No. of Items: %d\n", (st.st_size), (st.st_size/2));
return st.st_size;
}
printf("failed!\n");
return 0;
}
This file when run will return the file size:
$> cat file
1;
$> ./a.out
fileSize: 3 No. of Items: 1
$> cat file
1,2,3;
$> ./a.out
fileSize: 7 No. of Items: 3
Disclaimer: Is this approach to minimize the pre-allocated memory an optimal approach? No ways in heaven! :)
Dynamically allocating space for you data is a fundamental tool for working in C. You might as well pay the price to learn. The primary thing to remember is,
"if you allocate memory, you have the responsibility to track its use
and preserve a pointer to the starting address for the block of
memory so you can free it when you are done with it. Otherwise your
code with leak memory like a sieve."
Dynamic allocation is straight forward. You allocate some initial block of memory and keep track of what you add to it. You must test that each allocation succeeds. You must test how much of the block of memory you use and reallocate or stop writing data when full to prevent writing beyond the end of your block of memory. If you fail to test either, you will corrupt the memory associated with your code.
When you reallocate, always reallocate using a temporary pointer because with a reallocation failure, the original block of memory is freed. (causing loss of all previous data in that block). Using a temporary pointer allows you to handle failure in a manner to preserve that block if needed.
Taking that into consideration, below we initially allocate space for 64 long values (you can easily change to code to handle any type, e.g. int, float, double...). The code then reads each line of data (using getline to dynamically allocate the buffer for each line). strtol is used to parse the buffer assigning values to the array. idx is used as an index to keep track of how many values have been read, and when idx reaches the current nmax, array is reallocated twice as large as it previously was and nmax is updated to reflect the change. The reading, parsing, checking and reallocating continues for every line of data in the file. When done, the values are printed to stdout, showing the 400 random values read from the test file formatted as 353,394,257,...293,58,135;
To keep the read loop logic clean, I've put the error checking for the strtol conversion into a function xstrtol, but you are free to include that code in main() if you like. The same applies to the realloc_long function. To see when the reallocation takes place, you can compile the code with the -DDEBUG definition. E.g:
gcc -Wall -Wextra -DDEBUG -o progname yoursourcefile.c
The program expects your data filename as the first argument and you can provide an optional conversion base as the second argument (default is 10). E.g.:
./progname datafile.txt [base (default: 10)]
Look over it, test it, and let me know if you have any questions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#define NMAX 64
long xstrtol (char *p, char **ep, int base);
long *realloc_long (long *lp, unsigned long *n);
int main (int argc, char **argv)
{
char *ln = NULL; /* NULL forces getline to allocate */
size_t n = 0; /* max chars to read (0 - no limit) */
ssize_t nchr = 0; /* number of chars actually read */
size_t idx = 0; /* array index counter */
long *array = NULL; /* pointer to long */
unsigned long nmax = NMAX; /* initial reallocation counter */
FILE *fp = NULL; /* input file pointer */
int base = argc > 2 ? atoi (argv[2]) : 10; /* base (default: 10) */
/* open / validate file */
if (!(fp = fopen (argv[1], "r"))) {
fprintf (stderr, "error: file open failed '%s'.", argv[1]);
return 1;
}
/* allocate array of NMAX long using calloc to initialize to 0 */
if (!(array = calloc (NMAX, sizeof *array))) {
fprintf (stderr, "error: memory allocation failed.");
return 1;
}
/* read each line from file - separate into array */
while ((nchr = getline (&ln, &n, fp)) != -1)
{
char *p = ln; /* pointer to ln read by getline */
char *ep = NULL; /* endpointer for strtol */
while (errno == 0)
{ /* parse/convert each number in line into array */
array[idx++] = xstrtol (p, &ep, base);
if (idx == nmax) /* check NMAX / realloc */
array = realloc_long (array, &nmax);
/* skip delimiters/move pointer to next digit */
while (*ep && *ep != '-' && (*ep < '0' || *ep > '9')) ep++;
if (*ep)
p = ep;
else
break;
}
}
if (ln) free (ln); /* free memory allocated by getline */
if (fp) fclose (fp); /* close open file descriptor */
int i = 0;
for (i = 0; i < idx; i++)
printf (" array[%d] : %ld\n", i, array[i]);
free (array);
return 0;
}
/* reallocate long pointer memory */
long *realloc_long (long *lp, unsigned long *n)
{
long *tmp = realloc (lp, 2 * *n * sizeof *lp);
#ifdef DEBUG
printf ("\n reallocating %lu to %lu\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "%s() error: reallocation failed.\n", __func__);
// return NULL;
exit (EXIT_FAILURE);
}
lp = tmp;
memset (lp + *n, 0, *n * sizeof *lp); /* memset new ptrs 0 */
*n *= 2;
return lp;
}
long xstrtol (char *p, char **ep, int base)
{
errno = 0;
long tmp = strtol (p, ep, base);
/* Check for various possible errors */
if ((errno == ERANGE && (tmp == LONG_MIN || tmp == LONG_MAX)) ||
(errno != 0 && tmp == 0)) {
perror ("strtol");
exit (EXIT_FAILURE);
}
if (*ep == p) {
fprintf (stderr, "No digits were found\n");
exit (EXIT_FAILURE);
}
return tmp;
}
Sample Output (with -DDEBUG to show reallocation)
$ ./bin/read_long_csv dat/randlong.txt
reallocating 64 to 128
reallocating 128 to 256
reallocating 256 to 512
array[0] : 353
array[1] : 394
array[2] : 257
array[3] : 173
array[4] : 389
array[5] : 332
array[6] : 338
array[7] : 293
array[8] : 58
array[9] : 135
<snip>
array[395] : 146
array[396] : 324
array[397] : 424
array[398] : 365
array[399] : 205
Memory Error Check
$ valgrind ./bin/read_long_csv dat/randlong.txt
==26142== Memcheck, a memory error detector
==26142== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==26142== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==26142== Command: ./bin/read_long_csv dat/randlong.txt
==26142==
reallocating 64 to 128
reallocating 128 to 256
reallocating 256 to 512
array[0] : 353
array[1] : 394
array[2] : 257
array[3] : 173
array[4] : 389
array[5] : 332
array[6] : 338
array[7] : 293
array[8] : 58
array[9] : 135
<snip>
array[395] : 146
array[396] : 324
array[397] : 424
array[398] : 365
array[399] : 205
==26142==
==26142== HEAP SUMMARY:
==26142== in use at exit: 0 bytes in 0 blocks
==26142== total heap usage: 7 allocs, 7 frees, 9,886 bytes allocated
==26142==
==26142== All heap blocks were freed -- no leaks are possible
==26142==
==26142== For counts of detected and suppressed errors, rerun with: -v
==26142== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
I want to print the data from .csv file line by line which is separated by comma delimeter.
This code prints the garbage value .
enum gender{ M, F };
struct student{
int stud_no;
enum gender stud_gen;
char stud_name[100];
int stud_marks;
};
void main()
{
struct student s[60];
int i=0,j,roll_no,marks,k,select;
FILE *input;
FILE *output;
struct student temp;
input=fopen("Internal test 1 Marks MCA SEM 1 oct 2014 - CS 101.csv","r");
output=fopen("out.txt","a");
if (input == NULL) {
printf("Error opening file...!!!");
}
while(fscanf(input,"%d,%c,%100[^,],%d", &s[i].stud_no,&s[i].stud_gen,&s[i].stud_name,&s[i].stud_marks)!=EOF)
{
printf("\n%d,%c,%s,%d", s[i].stud_no,s[i].stud_gen,s[i].stud_name,s[i].stud_marks);
i++;
}
}
I also tried the code from: Read .CSV file in C But it prints only the nth field. I want to display all fields line by line.
Here is my sample input.
1401,F,FERNANDES SUZANNA ,13
1402,M,PARSEKAR VIPUL VILAS,14
1403,M,SEQUEIRA CLAYTON DIOGO,8
1404,M,FERNANDES GLENN ,17
1405,F,CHANDRAVARKAR TANUSHREE ROHIT,15
While there are a number of ways to parse any line into components, one way that can really increase understanding is to use a start and end pointer to work down each line identifying the commas, replacing them with null-terminators (i.e. '\0' or just 0), reading the field, restoring the comma and moving to the next field. This is just a manual application of strtok. The following example does that so you can see what is going on. You can, of course, replace use of the start and end pointers (sp & p, respectively) with strtok.
Read through the code and let me know if you have any questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* maximum number of student to initially allocate */
#define MAXS 256
enum gender { M, F };
typedef struct { /* create typedef to struct */
int stud_no;
enum gender stud_gen;
char *stud_name;
int stud_marks;
} student;
int main (int argc, char *argv[]) {
if (argc < 2) {
printf ("filename.csv please...\n");
return 1;
}
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* characters read by getline () */
size_t n = 0; /* number of bytes to allocate */
student **students = NULL; /* ptr to array of stuct student */
char *sp = NULL; /* start pointer for parsing line */
char *p = NULL; /* end pointer to use parsing line */
int field = 0; /* counter for field in line */
int cnt = 0; /* counter for number allocated */
int it = 0; /* simple iterator variable */
FILE *fp;
fp = fopen (argv[1], "r"); /* open file , read only */
if (!fp) {
fprintf (stderr, "failed to open file for reading\n");
return 1;
}
students = calloc (MAXS, sizeof (*students)); /* allocate 256 ptrs set to NULL */
/* read each line in input file preserving 1 pointer as sentinel NULL */
while (cnt < MAXS-1 && (read = getline (&line, &n, fp)) != -1) {
sp = p = line; /* set start ptr and ptr to beginning of line */
field = 0; /* set/reset field to 0 */
students[cnt] = malloc (sizeof (**students)); /* alloc each stuct with malloc */
while (*p) /* for each character in line */
{
if (*p == ',') /* if ',' end of field found */
{
*p = 0; /* set as null-term char (temp) */
if (field == 0) students[cnt]->stud_no = atoi (sp);
if (field == 1) {
if (*sp == 'M') {
students[cnt]->stud_gen = 0;
} else {
students[cnt]->stud_gen = 1;
}
}
if (field == 2) students[cnt]->stud_name = strdup (sp); /* strdup allocates for you */
*p = ','; /* replace with original ',' */
sp = p + 1; /* set new start ptr start pos */
field++; /* update field count */
}
p++; /* increment pointer p */
}
students[cnt]->stud_marks = atoi (sp); /* read stud_marks (sp alread set to begin) */
cnt++; /* increment students count */
}
fclose (fp); /* close file stream */
if (line) /* free memory allocated by getline */
free (line);
/* iterate over all students and print */
printf ("\nThe students in the class are:\n\n");
while (students[it])
{
printf (" %d %c %-30s %d\n",
students[it]->stud_no, (students[it]->stud_gen) ? 'F' : 'M', students[it]->stud_name, students[it]->stud_marks);
it++;
}
printf ("\n");
/* free memory allocated to struct */
it = 0;
while (students[it])
{
if (students[it]->stud_name)
free (students[it]->stud_name);
free (students[it]);
it++;
}
if (students)
free (students);
return 0;
}
(note: added condition on loop that cnt < MAXS-1 to preserve at least one pointer in students NULL as a sentinel allowing iteration.)
input:
$ cat dat/people.dat
1401,F,FERNANDES SUZANNA ,13
1402,M,PARSEKAR VIPUL VILAS,14
1403,M,SEQUEIRA CLAYTON DIOGO,8
1404,M,FERNANDES GLENN ,17
1405,F,CHANDRAVARKAR TANUSHREE ROHIT,15
output:
$./bin/stud_struct dat/people.dat
The students in the class are:
1401 F FERNANDES SUZANNA 13
1402 M PARSEKAR VIPUL VILAS 14
1403 M SEQUEIRA CLAYTON DIOGO 8
1404 M FERNANDES GLENN 17
1405 F CHANDRAVARKAR TANUSHREE ROHIT 15
valgrind memcheck:
I have updated the code slightly to insure all allocated memory was freed to prevent against any memory leaks. Simple things like the automatic allocation of memory for line by getline or failing to close a file stream can result in small memory leaks. Below is the valgrind memcheck confirmation.
valgrind ./bin/stud_struct dat/people.dat
==11780== Memcheck, a memory error detector
==11780== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==11780== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11780== Command: ./bin/stud_struct dat/people.dat
==11780==
The students in the class are:
1401 F FERNANDES SUZANNA 13
1402 M PARSEKAR VIPUL VILAS 14
1403 M SEQUEIRA CLAYTON DIOGO 8
1404 M FERNANDES GLENN 17
1405 F CHANDRAVARKAR TANUSHREE ROHIT 15
==11780==
==11780== HEAP SUMMARY:
==11780== in use at exit: 0 bytes in 0 blocks
==11780== total heap usage: 13 allocs, 13 frees, 2,966 bytes allocated
==11780==
==11780== All heap blocks were freed -- no leaks are possible
==11780==
==11780== For counts of detected and suppressed errors, rerun with: -v
==11780== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)