according to my task I need to read a file passed as a command line argument using C and store its content (each character) to an 2D array to be able change array's values later and save the changed content to another file. NVM some custom functions.
Here is an example of a file I need to read:
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
Here is what I've tried:
int main(int argc, char *argv[]) {
int startX = 3;
int startY = 3;
int endX = 6;
int endY = 6;
int count = 0;
int x = 0;
int y = 0;
int fd = open(argv[1], O_RDONLY);
char ch;
if (fd == -1) {
mx_printerr("map does not exist\n");
exit(-1);
}
int targetFile =
open("path.txt", O_CREAT | O_EXCL | O_WRONLY, S_IWUSR | S_IRUSR);
while (read(fd, &ch, 1)) {
if (ch == '\n') {
x++;
}
if (ch != ',') {
count++;
}
}
fd = open(argv[1], O_RDONLY);
y = (count - x) / x;
char **arr;
arr = malloc(sizeof(char *) * x);
for (int i = 0; i < x; i++) arr[i] = malloc(y);
int tempX = 0, tempY = 0, tempCount = 0;
char tempString[count - x];
// the loop in question >>>>>
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 11; j++) {
while (read(fd, &ch, 1)) {
if (ch != ',') {
arr[i][j] = ch;
// mx_printchar(arr[i][j]);
}
}
}
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 11; j++) {
mx_printchar(arr[i][j]);
}
}
for (int i = 0; i < x; i++) free(arr[i]);
free(arr);
close(fd);
close(targetFile);
exit(0);
}
The last while loop should be saving the file's content to an array. However, when I try to print the array's content to console, I get some garbage values:
���pp
����8��
Please help me understand what is wrong here or should I use another approach to save the data to the array.
You have started off well, but then strayed into an awkward way of handling your read and allocations. There are a number of ways you can approach a flexible read of any number of characters and any number of rows into a dynamically allocated pointer-to-pointer-to char object that you can index like a 2D array. (often incorrectly referred to an a "dynamic 2D array") There is no array involved at all, you have a single-pointer to more pointers and you allocate a block of storage for your pointers (rows) and then allocate separate blocks of memory to hold each row worth of data and assign the beginning address to each such block to one of the pointers in turn.
An easy way to eliminate having to pre-read each row of characters to determine the number is to simply buffer the characters for each row and then allocate storage for and copy that number of characters to their final location. This provides the advantage of not having to allocate/reallocate each row starting from some anticipated number of characters. (as there is no guarantee that all rows won't have a stray character somewhere)
The other approach, equally efficient, but requiring the pre-read of the first row is to read the first row to determine the number of characters, allocate that number of characters for each row and then enforce that number of characters on every subsequent row (handling the error if additional characters are found). There are other options if you want to treat each row as a line and then read and create an array of strings, but your requirements appear to simply be a grid of characters) You can store your lines as strings at this point simply by adding a nul-terminating character.
Below we will use a fixed buffer to hold the characters until a '\n' is found marking the end of the row (or you run out of fixed storage) and then dynamically allocate storage for each row and copy the characters from the fixed buffer to your row-storage. This is generally a workable solution as you will know some outer bound of the max number of characters than can occur per-line (don't skimp). A 2K buffer is cheap security even if you think you are reading a max of 100 chars per-line. (if you are on an embedded system with limited memory, then I would reduce the buffer to 2X the anticipated max number of chars) If you define a constant up top for the fixed buffer size -- if you find you need more, it's a simple change in one location at the top of your file.
How does it work?
Let's start with declaring the counter variables to track the number of pointers available (avail), a row counter (row) a column counter (col) and a fixed number of columns we can use to compare against the number of columns in all subsequent rows (cols). Declare your fixed buffer (buf) and your pointer-to-pointer to dynamically allocate, and a FILE* pointer to handle the file, e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NCHARS 2048 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
size_t avail = 2, /* initial no. of available pointers to allocate */
row = 0, /* row counter */
col = 0, /* column counter */
cols = 0; /* fixed no. of columns based on 1st row */
char buf[NCHARS], /* temporary buffer to hold characters */
**arr = NULL; /* pointer-to-pointer-to char to hold grid */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
(note: if no argument is provided, the program will read from stdin by default)
Next we validate the file is open for reading and we allocate an initial avail number of pointers:
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* allocate/validate initial avail no. of pointers */
if (!(arr = malloc (avail * sizeof *arr))) {
perror ("malloc-arr");
return 1;
}
Next rather than looping while ((c = fgetc(fp)) != EOF), just continually loop - that will allow you to treat a '\n' or EOF within the loop and not have to handle the storage of the last line separately after the loop exits. Begin by reading the next character from the file and checking if you have used all your available pointers (indicating you need to realloc() more before proceeded):
while (1) { /* loop continually */
int c = fgetc(fp); /* read each char in file */
if (row == avail) { /* if all pointers used */
/* realloc 2X no. of pointers using temporary pointer */
void *tmp = realloc (arr, 2 * avail * sizeof *arr);
if (!tmp) { /* validate reallocation */
perror ("realloc-arr");
return 1; /* return failure */
}
arr = tmp; /* assign new block to arr */
avail *= 2; /* update available pointers */
}
(note: always realloc() using a temporary pointer. When realloc() fails (not if it fails) it returns NULL and if you reallocate using arr = realloc (arr, ..) you have just overwritten your pointer to your current block of memory with NULL causing the loss of the pointer and inability to free() the prior allocated block resulting in a memory-leak)
Now check if you have reached the end of line, or EOF and in the case of EOF if your col count is zero, you know you reached EOF after a previous '\n' so you can simply break the loop at that point. Otherwise, if you reach EOF with a full column-count, you know your file lacks a POSIX end-of-file and you need to store the last line of character, e.g.
if (c == '\n' || c == EOF) { /* if end of line or EOF*/
if (c == EOF && !col) /* EOF after \n - break */
break;
if (!(arr[row] = malloc (col))) { /* allocate/validate col chars */
perror ("malloc-arr[row]");
return 1;
}
memcpy (arr[row++], buf, col); /* copy buf to arr[row], increment */
if (!cols) /* if cols not set */
cols = col; /* set cols to enforce cols per-row */
if (col != cols) { /* validate cols per-row */
fprintf (stderr, "error: invalid no. of cols - row %zu\n", row);
return 1;
}
if (c == EOF) /* break after non-POSIX eof */
break;
col = 0; /* reset col counter zero */
}
If your character isn't a '\n' or EOF it's just a normal character, so add it to your buffer, check your buffer has room for the next and keep going:
else { /* reading in line */
buf[col++] = c; /* add char to buffer */
if (col == NCHARS) { /* if buffer full, handle error */
fputs ("error: line exceeds maximum.\n", stderr);
return 1;
}
}
}
At this point you have all of your characters stored in a dynamically allocated object you can index as a 2D array. (you also know it is just storage of characters that are not nul-terminated so you cannot treat each line as a string). You are free to add a nul-terminating character if you like, but then you might as well just read each line into buf with fgets() and trim the trailing newline, if present. Depends on your requirements.
The example just closes the file (if not reading from stdin), outputs the stored characters and frees all allocated memory, e.g.
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < row; i++) { /* loop over rows */
for (size_t j = 0; j < cols; j++) /* loop over cols */
putchar (arr[i][j]); /* output char */
putchar ('\n'); /* tidy up with newline */
free (arr[i]); /* free row */
}
free (arr); /* free pointers */
}
(that's the whole program, you can just cut/paste the parts together)
Example Input File
$ cat dat/gridofchars.txt
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
Example Use/Output
$ ./bin/read_dyn_grid dat/gridofchars.txt
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
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 ensure 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/read_dyn_grid dat/gridofchars.txt
==29391== Memcheck, a memory error detector
==29391== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29391== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==29391== Command: ./bin/read_dyn_grid dat/gridofchars.txt
==29391==
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
==29391==
==29391== HEAP SUMMARY:
==29391== in use at exit: 0 bytes in 0 blocks
==29391== total heap usage: 17 allocs, 17 frees, 6,132 bytes allocated
==29391==
==29391== All heap blocks were freed -- no leaks are possible
==29391==
==29391== For counts of detected and suppressed errors, rerun with: -v
==29391== 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.
I am trying to count all syllables in each word of a string that I passed into an array.
A syllable counts as two vowels adjacent to each other (a, e, i , o ,u, y). For example, the "ee" in "peel" counts as 1 syllable. But, the "u" and "e" in "juked" count as 2 syllables. The "e" at the end of a word does not count as a syllable. Also, each word has at least 1 syllable even if the previous rules don't apply.
I have a file that contained a large string (words, spaces, and newlines) that I passed into an array.
I have code that counts each word by counting the whitespace between them and newlines. See below:
for (i = 0; i < lengthOfFile; i++)
{
if (charArray[i] == ' ' || charArray[i] == '\n')
{
wordCount++;
}
}
Where charArray is the file that is passed into an array (freads) and lengthOfFile is the total bytes in the file counted by (fseek) and wordCount is total words counted.
From here, I need to somehow count the syllables in each word in the array but don't know where to start.
If you are still having trouble, it's only because you are overthinking the problem. Whenever you are counting, determining frequency, etc.., you can normally simplify things by using a "State-Loop". A state-loop is nothing more than a loop where you loop over each character (or whatever) and handle whatever state you find yourself in, like:
have I read any characters? (if not, handle that state);
is the current character a space? (if so, assuming no multiple-spaces for simplicity, you have reached the end of a word, handle that state);
is the current character a non-space and non-vowel? (if so, if my last character was a vowel, increment my syllable count); and
what do I need to do regardless of the classification of the current char? (output it, set last = current, etc..)
That's basically it and can be translated into a single loop with a number of tests to handle each state. You can also add a check to insure that words like "my" and "he" are counted as a single syllable by checking if your syllable count is zero when you reach the end of the word.
Putting it altogether, you could write a basic implementation like:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main (void) {
char c, last = 0; /* current & last char */
const char *vowels = "AEIOUYaeiouy"; /* vowels (plus Yy) */
size_t syllcnt = 0, totalcnt = 0; /* word syllable cnt & total */
while ((c = getchar()) != EOF) { /* read each character */
if (!last) { /* if 1st char (no last) */
putchar (c); /* just output it */
last = c; /* set last */
continue; /* go get next */
}
if (isspace (c)) { /* if space, end of word */
if (!syllcnt) /* if no syll, it's 1 (he, my) */
syllcnt = 1;
printf (" - %zu\n", syllcnt); /* output syll cnt and '\n' */
totalcnt += syllcnt; /* add to total */
syllcnt = 0; /* reset syllcnt to zero */
} /* otherwise */
else if (!strchr (vowels, c)) /* if not vowel */
if (strchr (vowels, last)) /* and last was vowel */
syllcnt++; /* increment syllcnt */
if (!isspace (c)) /* if not space */
putchar (c); /* output it */
last = c; /* set last = c */
}
printf ("\n total syllables: %zu\n", totalcnt);
}
(note: as mentioned above, this simple example implementation does not consider multiple spaces between words -- which you can simply add as another needed condition by checking whether !isspace (last). Can you figure out where that check should be added, hint: it's added to an existing check with && -- fine tuning is left to you)
Example Use/Output
$ echo "my dog eats banannas he peels while getting juked" | ./syllablecnt
my - 1
dog - 1
eats - 1
banannas - 3
he - 1
peels - 1
while - 1
getting - 2
juked - 2
total syllables: 13
If you need to read words from a file, simply redirect the file as input to the program on stdin, e.g.
./syllablecnt < inputfile
Edit - Reading from File into Dynamically Allocated Buffer
Following on from the comments about wanting to read from a file (or stdin) into a dynamically sized buffer and then traversing the buffer to output the syllables per-word and total syllables, you could do something like the following that simply reads all characters from a file into a buffer initially allocated holding 8-characters and is reallocated as needed (doubling the allocation size each time a realloc is needed). That is a fairly standard and reasonably efficient buffer growth strategy. You are free to grow it by any size you like, but avoid many small rabbit-pellet reallocations as memory allocation is relatively expensive from a computing standpoint.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NCHAR 8 /* initial characters to allocate */
int main (int argc, char **argv) {
char c, last = 0, *buffer; /* current, last & pointer */
const char *vowels = "AEIOUYaeiouy"; /* vowels */
size_t syllcnt = 0, totalcnt = 0, /* word syllable cnt & total */
n = 0, size = NCHAR;
/* 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 ("fopen-file");
return 1;
}
/* allocate/validate initial NCHAR buffer size */
if (!(buffer = malloc (size))) {
perror ("malloc-buffer");
return 1;
}
while ((c = fgetc(fp)) != EOF) { /* read each character */
buffer[n++] = c; /* store, increment count */
if (n == size) { /* reallocate as required */
void *tmp = realloc (buffer, 2 * size);
if (!tmp) { /* validate realloc */
perror ("realloc-tmp");
break; /* still n good chars in buffer */
}
buffer = tmp; /* assign reallocated block to buffer */
size *= 2; /* update allocated size */
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++) { /* loop over all characters */
c = buffer[i]; /* set to c to reuse code */
if (!last) { /* if 1st char (no last) */
putchar (c); /* just output it */
last = c; /* set last */
continue; /* go get next */
}
if (isspace(c) && !isspace(last)) { /* if space, end of word */
if (!syllcnt) /* if no syll, it's 1 (he, my) */
syllcnt = 1;
printf (" - %zu\n", syllcnt); /* output syll cnt and '\n' */
totalcnt += syllcnt; /* add to total */
syllcnt = 0; /* reset syllcnt to zero */
} /* otherwise */
else if (!strchr (vowels, c)) /* if not vowel */
if (strchr (vowels, last)) /* and last was vowel */
syllcnt++; /* increment syllcnt */
if (!isspace (c)) /* if not space */
putchar (c); /* output it */
last = c; /* set last = c */
}
free (buffer); /* don't forget to free what you allocate */
printf ("\n total syllables: %zu\n", totalcnt);
}
(you can do the same using fgets or using POSIX getline, or allocate all at once with your fseek/ftell or stat and then fread the entire file into a buffer in a single call -- up to you)
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/syllablecnt_array dat/syllables.txt
==19517== Memcheck, a memory error detector
==19517== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19517== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==19517== Command: ./bin/syllablecnt_array dat/syllables.txt
==19517==
my - 1
dog - 1
eats - 1
banannas - 3
he - 1
peels - 1
while - 1
getting - 2
juked - 2
total syllables: 13
==19517==
==19517== HEAP SUMMARY:
==19517== in use at exit: 0 bytes in 0 blocks
==19517== total heap usage: 5 allocs, 5 frees, 672 bytes allocated
==19517==
==19517== All heap blocks were freed -- no leaks are possible
==19517==
==19517== For counts of detected and suppressed errors, rerun with: -v
==19517== 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.
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 need to read input from a file, then split the word in capitals from it's definition. My trouble being that I need multiple lines from the file to be in one variable to pass it to another function.
The file I want to read from looks like this
ACHROMATIC. An optical term applied to those telescopes in which
aberration of the rays of light, and the colours dependent thereon, are
partially corrected. (See APLANATIC.)
ACHRONICAL. An ancient term, signifying the rising of the heavenly
bodies at sunset, or setting at sunrise.
ACROSS THE TIDE. A ship riding across tide, with the wind in the
direction of the tide, would tend to leeward of her anchor; but with a
weather tide, or that running against the wind, if the tide be strong,
would tend to windward. A ship under sail should prefer the tack that
stems the tide, with the wind across the stream, when the anchor is
let go.
Right now my code splits the word from the rest, but I'm having difficulty getting the rest of the input into one variable.
while(fgets(line, sizeof(line), mFile) != NULL){
if (strlen(line) != 2){
if (isupper(line[0]) && isupper(line[1])){
word = strtok(line, ".");
temp = strtok(NULL, "\n");
len = strlen(temp);
for (i=0; i < len; i++){
*(defn+i) = *(temp+i);
}
printf("Word: %s\n", word);
}
else{
temp = strtok(line, "\n");
for (i=len; i < strlen(temp) + len; i++);
*(defn+i) = *(temp+i-len);
len = len + strlen(temp);
//printf(" %s\n", temp);
}
}
else{
len = 0;
printf("%s\n", defn);
index = 0;
}
}
like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
//another function
void func(char *word, char *defs){
printf("<%s>\n", word);
if(defs){
printf("%s", defs);
}
}
int main(void){
char buffer[4096], *curr = buffer;
size_t len, buf_size = sizeof buffer;
FILE *fp = fopen("dic.txt", "r");
while(fgets(curr, buf_size, fp)){
//check definition line
if(*curr == '\n' || !isupper(*curr)){
continue;//printf("invalid format\n");
}
len = strlen(curr);
curr += len;
buf_size -= len;
//read rest line
while(1){
curr = fgets(curr, buf_size, fp);
if(!curr || *curr == '\n'){//upto EOF or blank line
char *word, *defs;
char *p = strchr(buffer, '.');
if(p)
*p++ = 0;
word = buffer;
defs = p;
func(word, defs);
break;
}
len = strlen(curr);
curr += len;
buf_size -= len;
assert(buf_size >= 2 || (fprintf(stderr, "small buffer\n"), 0));
}
curr = buffer;
buf_size = sizeof buffer;
}
fclose(fp);
return 0;
}
It appears you need to first pull a string of uppercase letters from the beginning of the line, up to the first period, then concatenate the remainder of that line with subsequent lines until a blank line is found. Lather, rinse, repeat as needed.
While this task would be MUCH easier in Perl, if you need to do it in C, for one thing I recommend using the built-in string functions instead of constructing your own for-loops to copy the data. Perhaps something like the following:
while(fgets(line, sizeof(line), mFile) != NULL) {
if (strlen(line) > 2) {
if (isupper(line[0]) && isupper(line[1])) {
word = strtok(line, ".");
strcpy(defn,strtok(NULL, "\n"));
printf("Word: %s\n", word);
} else {
strcat(defn,strtok(line, "\n"));
}
} else {
printf("%s\n", defn);
defn[0] = 0;
}
}
When I put this in a properly structured C program, with appropriate include files, it works fine. I personally would have approached the problem differently, but hopefully this gets you going.
There are several areas that can be addressed. Given your example input and description, it appears your goal is to develop a function that will read and separate each word (or phrase) and associated definition, return a pointer to the collection of words/definitions, while also updating a pointer to the number of words and definitions read so that number is available back in the calling function (main here).
While your data suggests that the word and definition are both contained within a single line of text with the word (or phrase written in all upper-case), it is unclear whether you will have to address the case where the definition can span multiple lines (essentially causing you to potentially read multiple lines and combine them to form the complete definition.
Whenever you need to maintain relationships between multiple variables within a single object, then a struct is a good choice for the base data object. Using an array of struct allows you access to each word and its associated definition once all have been read into memory. Now your example has 3 words and definitions. (each separated by a '\n'). Creating an array of 3 struct to hold the data is trivial, but when reading data, like a dictionary, you rarely know exactly how many words you will have to read.
To handle this situation, a dynamic array of structs is a proper data structure. You essentially allocate space for some reasonable number of words/definitions, and then if you reach that limit, you simply realloc the array containing your data, update your limit to reflect the new size allocated, and continue on.
While you can use strtok to separate the word (or phrase) by looking for the first '.', that is a bit of an overkill. You will need to traverse over each char anyway to check if they are all caps anyway, you may as well just iterate until you find the '.' and use the number for that character index to store your word and set a pointer to the next char after the '.'. You will begin looking for the start of the definition from there (you basically want to skip any character that is not an [a-zA-Z]). Once you locate the beginning of the definition, you can simply get the length of the rest of the line, and copy that as the definition (or the first part of it if the definition is contained in multiple-separate lines).
After the file is read and the pointer returned and the pointer for the number of words updated, you can then use the array of structs back in main as you like. Once you are done using the information, you should free all the memory you have allocated.
Since the size of the maximum word or phrase is generally know, the struct used provides static storage for the word. Give the definitions can vary wildly in length and are much longer, the struct simply contains a pointer-to-char*. So you will have to allocate storage for each struct, and then allocates storage for each definition within each struct.
The following code does just that. It will take the filename to read as the first argument (or it will read from stdin by default if no filename is given). The code the output the words and definitions on single lines. The code is heavily commented to help you follow along and explain the logic e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {MAXW = 64, NDEF = 128};
typedef struct { /* struct holding words/definitions */
char word[MAXW],
*def; /* you must allocate space for def */
} defn;
defn *readdict (FILE *fp, size_t *n);
int main (int argc, char **argv) {
defn *defs = NULL;
size_t n = 0;
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;
}
if (!(defs = readdict (fp, &n))) { /* read words/defs into defs */
fprintf (stderr, "readdict() error: no words read from file.\n");
return 1;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < n; i++) {
printf ("\nword: %s\n\ndefinition: %s\n", defs[i].word, defs[i].def);
free (defs[i].def); /* free allocated definitions */
}
free (defs); /* free array of structs */
return 0;
}
/** read word and associated definition from open file stream 'fp'
* into dynamic array of struct, updating pointer 'n' to contain
* the total number of defn structs filled.
*/
defn *readdict (FILE *fp, size_t *n)
{
defn *defs = NULL; /* pointer to array of structs */
char buf[BUFSIZ] = ""; /* buffer to hold each line read */
size_t max = NDEF, haveword = 0, offset = 0; /* allocated size & flags */
/* allocate, initialize & validate memory to hold 'max' structs */
if (!(defs = calloc (max, sizeof *defs))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return NULL;
}
while (fgets (buf, BUFSIZ, fp)) /* read each line of input */
{
if (*buf == '\n') { /* check for blank line */
if (haveword) (*n)++; /* if word/def already read, increment n */
haveword = 0; /* reset haveword flag */
if (*n == max) {
void *tmp = NULL; /* tmp ptr to realloc defs */
if (!(tmp = realloc (defs, sizeof *defs * (max + NDEF)))) {
fprintf (stderr, "error: memory exhaused, realloc defs.\n");
break;
}
defs = tmp; /* assign new block to defs */
memset (defs + max, 0, NDEF * sizeof *defs); /* zero new mem */
max += NDEF; /* update max with current allocation size */
}
continue; /* get next line */
}
if (haveword) { /* word already stored in defs[n].word */
void *tmp = NULL; /* tmp pointer to realloc */
size_t dlen = strlen (buf); /* get line/buf length */
if (buf[dlen - 1] == '\n') /* trim '\n' from end */
buf[--dlen] = 0; /* realloc & validate */
if (!(tmp = realloc (defs[*n].def, offset + dlen + 2))) {
fprintf (stderr,
"error: memory exhaused, realloc defs[%zu].def.\n", *n);
break;
}
defs[*n].def = tmp; /* assign new block, fill with definition */
sprintf (defs[*n].def + offset, offset ? " %s" : "%s", buf);
offset += dlen + 1; /* update offset for rest (if required) */
}
else { /* no current word being defined */
char *p = NULL;
size_t i;
for (i = 0; buf[i] && i < MAXW; i++) { /* check first MAXW chars */
if (buf[i] == '.') { /* if a '.' is found, end of word */
size_t dlen = 0;
if (i + 1 == MAXW) { /* check one char available for '\0' */
fprintf (stderr,
"error: 'word' exceeds MAXW, skipping.\n");
goto next;
}
strncpy (defs[*n].word, buf, i); /* copy i chars to .word */
haveword = 1; /* set haveword flag */
p = buf + i + 1; /* set p to next char in buf after '.' */
while (*p && (*p == ' ' || *p < 'A' || /* find def start */
('Z' < *p && *p < 'a') || 'z' < *p))
p++; /* increment p and check again */
if ((dlen = strlen (p))) { /* get definition length */
if (p[dlen - 1] == '\n') /* trim trailing '\n' */
p[--dlen] = 0;
if (!(defs[*n].def = malloc (dlen + 1))) { /* allocate */
fprintf (stderr,
"error: virtual memory exhausted.\n");
goto done; /* bail if allocation failed */
}
strcpy (defs[*n].def, p); /* copy definition to .def */
offset = dlen; /* set offset in .def buf to be */
} /* used if def continues on a */
break; /* new or separae line */
} /* check word is all upper-case or a ' ' */
else if (buf[i] != ' ' && (buf[i] < 'A' || 'Z' < buf[i]))
break;
}
}
next:;
}
done:;
if (haveword) (*n)++; /* account for last word/definition */
return defs; /* return pointer to array of struct */
}
Example Use/Output
$ ./bin/dict_read <dat/dict.txt
word: ACHROMATIC
definition: An optical term applied to those telescopes in which
aberration of the rays of light, and the colours dependent thereon,
are partially corrected. (See APLANATIC.)
word: ACHRONICAL
definition: An ancient term, signifying the rising of the heavenly
bodies at sunset, or setting at sunrise.
word: ACROSS THE TIDE
definition: A ship riding across tide, with the wind in the direction
of the tide, would tend to leeward of her anchor; but with a weather tide,
or that running against the wind, if the tide be strong, would tend to
windward. A ship under sail should prefer the tack that stems the tide,
with the wind across the stream, when the anchor is let go.
(line breaks were manually inserted to keep the results tidy here).
Memory Use/Error Check
You should also run any code that dynamically allocates memory though a memory use and error checking program like valgrind on linux. Just run the code though it and confirm you free all memory you allocate and that there are no memory errors, e.g.
$ valgrind ./bin/dict_read <dat/dict.txt
==31380== Memcheck, a memory error detector
==31380== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==31380== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==31380== Command: ./bin/dict_read
==31380==
word: ACHROMATIC
<snip output>
==31380==
==31380== HEAP SUMMARY:
==31380== in use at exit: 0 bytes in 0 blocks
==31380== total heap usage: 4 allocs, 4 frees, 9,811 bytes allocated
==31380==
==31380== All heap blocks were freed -- no leaks are possible
==31380==
==31380== For counts of detected and suppressed errors, rerun with: -v
==31380== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Look things over and let me know if you have further questions.
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)