Error using malloc - c

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.

Related

Creating a function in C that reads a file but it infinitely loops

I basically want to print out all of the lines in a file i made
but then it just loops over and over back to the start
basically cuz in the function i set fseek(fp, 0, SEEK_SET); this part
but idk how otherwise i would place it to get through all the other lines
im basically going back to the start every time.
#include<stdio.h>
#include <stdlib.h>
char *freadline(FILE *fp);
int main(){
FILE *fp = fopen("story.txt", "r");
if(fp == NULL){
printf("Error!");
}else{
char *pInput;
while(!feof(fp)){
pInput = freadline(fp);
printf("%s\n", pInput); // outpu
}
}
return 0;
}
char *freadline(FILE *fp){
int i;
for(i = 0; !feof(fp); i++){
getc(fp);
}
fseek(fp, 0, SEEK_SET); // resets to origin (?)
char *pBuffer = (char*)malloc(sizeof(char)*i);
pBuffer = fgets(pBuffer, i, fp);
return pBuffer;
}
this is my work so far
Continuing from my comments, you are thinking along the correct lines, you just haven't put the pieces together in the right order. In main() you are looping calling your function, allocating for a single line, and then outputting a line of text and doing it over and over again. (and without freeing any of the memory you allocate -- creating a memory leak of every line read)
If you are allocating storage to hold the line, you will generally want to read the entire file in your function in a single pass though the file allocating for, and storing all lines, and returning a pointer to your collection of lines for printing in main() (or whatever the calling function is).
You do that by adding one additional level of indirection and having your function return char **. This is a simple two-step process where you:
allocate a block of memory containing pointers (one pointer for each line). Since you will not know how many lines beforehand, you simply allocate some number of pointers and then realloc() more pointers when you run out;
for each line you read, you allocate length + 1 characters of storage and copy the current line to that block of memory assigning the address for the line to your next available pointer.
(you either keep track of the number of pointers and lines allocated, or provide an additional pointer set to NULL as a Sentinel after the last pointer assigned a line -- up to you, simply keeping track with a counter is likely conceptually easier)
After reading your last line, you simply return the pointer to the collection of pointers which is assigned for use back in the caller. (you can also pass the address of a char ** as a parameter to your function, resulting in the type being char ***, but being a Three-Star Programmer isn't always a compliment). However, there is nothing wrong with doing it that way, and in some cases, it will be required, but if you have an alternative, that is generally the preferred route.
So how would this work in practice?
Simply change your function return type to char ** and pass an additional pointer to a counting variable so you can update the value at that address with the number of lines read before you return from your function. E.g., you could do:
char **readfile (FILE *fp, size_t *n);
Which will take your file pointer to an open file stream and then read each line from it, allocating storage for the line and assigning the address for that allocation to one of your pointers. Within the function, you would use a sufficiently sized character array to hold each line you read with fgets(). Trim the '\n' from the end and get the length and then allocate length + 1 bytes to hold the line. Assign the address for the newly allocated block to a pointer and copy from your array to the newly allocated block.
(strdup() can both allocate and copy, but it is not part of the standard library, it's POSIX -- though most compilers support it as an extension if you provide the proper options)
Below the readfile() function puts it altogether, starting with a single pointer and reallocation twice the current number when you run out (that provides a reasonable trade-off between the number of allocations needed and the number of pointers. (after just 20 calls to realloc(), you would have 1M pointers). You can choose any reallocation and growth scheme you like, but you want to avoid calling realloc() for every line -- realloc() is still a relatively expensive call.
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTR 1 /* initial no. of pointers to allocate */
/* readfile reads all lines from fp, updating the value at the address
* provided by 'n'. On success returns pointer to allocated block of pointers
* with each of *n pointers holding the address of an allocated block of
* memory containing a line from the file. On allocation failure, the number
* of lines successfully read prior to failure is returned. Caller is
* responsible for freeing all memory when done with it.
*/
char **readfile (FILE *fp, size_t *n)
{
char buffer[MAXC], **lines; /* buffer to hold each line, pointer */
size_t allocated = NPTR, used = 0; /* allocated and used pointers */
lines = malloc (allocated * sizeof *lines); /* allocate initial pointer(s) */
if (lines == NULL) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
while (fgets (buffer, MAXC, fp)) { /* read each line from file */
size_t len; /* variable to hold line-length */
if (used == allocated) { /* is pointer reallocation needed */
/* always realloc to a temporary pointer to avoid memory leak if
* realloc fails returning NULL.
*/
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* lines before failure still good */
}
lines = tmp; /* assign reallocted block to lines */
allocated *= 2; /* update no. of allocated pointers */
}
buffer[(len = strcspn (buffer, "\n"))] = 0; /* trim \n, save length */
lines[used] = malloc (len + 1); /* allocate storage for line */
if (!lines[used]) { /* validate EVERY allocation */
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buffer, len + 1); /* copy buffer to lines[used] */
used++; /* increment used no. of pointers */
}
*n = used; /* update value at address provided by n */
/* can do final realloc() here to resize exactly to used no. of pointers */
return lines; /* return pointer to allocated block of pointers */
}
In main(), you simply pass your file pointer and the address of a size_t variable and check the return before iterating through the pointers making whatever use of the line you need (they are simply printed below), e.g.
int main (int argc, char **argv) {
char **lines; /* pointer to allocated block of pointers and lines */
size_t n; /* number of lines read */
/* 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;
}
lines = readfile (fp, &n);
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!lines) { /* validate readfile() return */
fputs ("error: no lines read from file.\n", stderr);
return 1;
}
for (size_t i = 0; i < n; i++) { /* loop outputting all lines read */
puts (lines[i]);
free (lines[i]); /* don't forget to free lines */
}
free (lines); /* and free pointers */
return 0;
}
(note: don't forget to free the memory you allocated when you are done. That become critical when you are calling functions that allocate within other functions. In main(), the memory will be automatically released on exit, but build good habits.)
Example Use/Output
$ ./bin/readfile_allocate dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
The program will read any file, no matter if it has 4-lines or 400,000 lines up to the physical limit of your system memory (adjust MAXC if your lines are longer than 1023 characters).
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/readfile_allocate dat/captnjack.txt
==4801== Memcheck, a memory error detector
==4801== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4801== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==4801== Command: ./bin/readfile_allocate dat/captnjack.txt
==4801==
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
==4801==
==4801== HEAP SUMMARY:
==4801== in use at exit: 0 bytes in 0 blocks
==4801== total heap usage: 10 allocs, 10 frees, 5,804 bytes allocated
==4801==
==4801== All heap blocks were freed -- no leaks are possible
==4801==
==4801== For counts of detected and suppressed errors, rerun with: -v
==4801== 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.
The full code used for the example simply includes the headers, but is included below for completeness:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTR 1 /* initial no. of pointers to allocate */
/* readfile reads all lines from fp, updating the value at the address
* provided by 'n'. On success returns pointer to allocated block of pointers
* with each of *n pointers holding the address of an allocated block of
* memory containing a line from the file. On allocation failure, the number
* of lines successfully read prior to failure is returned. Caller is
* responsible for freeing all memory when done with it.
*/
char **readfile (FILE *fp, size_t *n)
{
char buffer[MAXC], **lines; /* buffer to hold each line, pointer */
size_t allocated = NPTR, used = 0; /* allocated and used pointers */
lines = malloc (allocated * sizeof *lines); /* allocate initial pointer(s) */
if (lines == NULL) { /* validate EVERY allocation */
perror ("malloc-lines");
return NULL;
}
while (fgets (buffer, MAXC, fp)) { /* read each line from file */
size_t len; /* variable to hold line-length */
if (used == allocated) { /* is pointer reallocation needed */
/* always realloc to a temporary pointer to avoid memory leak if
* realloc fails returning NULL.
*/
void *tmp = realloc (lines, 2 * allocated * sizeof *lines);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-lines");
break; /* lines before failure still good */
}
lines = tmp; /* assign reallocted block to lines */
allocated *= 2; /* update no. of allocated pointers */
}
buffer[(len = strcspn (buffer, "\n"))] = 0; /* trim \n, save length */
lines[used] = malloc (len + 1); /* allocate storage for line */
if (!lines[used]) { /* validate EVERY allocation */
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buffer, len + 1); /* copy buffer to lines[used] */
used++; /* increment used no. of pointers */
}
*n = used; /* update value at address provided by n */
/* can do final realloc() here to resize exactly to used no. of pointers */
return lines; /* return pointer to allocated block of pointers */
}
int main (int argc, char **argv) {
char **lines; /* pointer to allocated block of pointers and lines */
size_t n; /* number of lines read */
/* 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;
}
lines = readfile (fp, &n);
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!lines) { /* validate readfile() return */
fputs ("error: no lines read from file.\n", stderr);
return 1;
}
for (size_t i = 0; i < n; i++) { /* loop outputting all lines read */
puts (lines[i]);
free (lines[i]); /* don't forget to free lines */
}
free (lines); /* and free pointers */
return 0;
}
Let me know if you have further questions.
If you want to read a fine till the last line, you can simply use getc(). It returns EOF when end of file is reached or it fails to read. So, if using getc(), to make sure the end of file is reached, it's better to use feof(), which returns a non-zero value if end of file is reached, else 0.
Example :-
int main()
{
FILE *fp = fopen("story.txt", "r");
int ch = getc(fp);
while (ch != EOF)
{
/* display contents of file on screen */
putchar(ch);
ch = getc(fp);
}
if (feof(fp))
printf("\n End of file reached.");
else
printf("\nError while reading!");
fclose(fp);
getchar();
return 0;
}
you can also fgets(), below is the example :-
#define MAX_LEN 256
int main(void)
{
FILE *fp = fopen("story.txt", "r");
if (fp == NULL) {
perror("Failed: "); //prints a descriptive error message to stderr.
return 1;
}
char buffer[MAX_LEN];
// -1 to allow room for NULL terminator for really long string
while (fgets(buffer, MAX_LEN - 1, fp))
{
// Remove trailing newline
buffer[strcspn(buffer, "\n")] = 0;
printf("%s\n", buffer);
}
fclose(fp);
return 0;
}
alternatively,
int main() {
int MAX_LEN = 255;
char buffer[MAX_LEN];
FILE *fp = fopen("story.txt", "r");
while(fgets(buffer, MAX_LEN, fp)) {
printf("%s\n", buffer);
}
fclose(fp);
return 0;
}
For more details, refer C read file line by line
Your approach is wrong and it's very likely that it will generate an endless loop. I'll explain why using the original code and inline comments:
char *freadline(FILE *fp){
int i;
// This part attempts to count the number of characters
// in the whole file by reading char-by-char until EOF is set
for(i = 0; !feof(fp); i++){
getc(fp);
}
// Here EOF is set
// This returns to the start and clears EOF
fseek(fp, 0, SEEK_SET);
// Here EOF is cleared
char *pBuffer = (char*)malloc(sizeof(char)*i);
// Here you read a line, i.e. you read characters until (and including) the
// first newline in the file.
pBuffer = fgets(pBuffer, i, fp);
// Here EOF is still cleared as you only read the first line of the file
return pBuffer;
}
So in main when you do
while(!feof(fp)){
...
}
you have an endless loop as feof is false. Your program will print the same line again and again and you have memory leaks as you never call free(pInput).
So you need to redesign your code. Read what fgets do, e.g. here https://man7.org/linux/man-pages/man3/fgets.3p.html
A number of issues to address:
Using fgets does not guarantee that you read a line after the function returns. So if you really want to check whether you've read a complete line, check the number of characters in the returned string, and also check for the presence of a new-line character at the end of the string.
Your use of fseek is interesting here because what it does is to tell the stream pointer to go back to the start of the file, and start reading from there. This means that after the first time the freadline function is called, you will continue reading the first byte from the file each time.
Lastly, your program is hoarding memory like a greedy baby! You never free any of those allocations you did!
With that being said, here is an improved freadline implementation:
char *freadline(FILE *fp) {
/* initializations */
char buf[BUFSIZ + 1];
char *pBuffer;
size_t size = 0, tmp_size;
/* fgets returns NULL when it reaches EOF, so our loop is conditional
* on that
*/
while (fgets (buf, BUFSIZ + 1, fp) != NULL) {
tmp_size = strlen (buf);
size += tmp_size;
if (tmp_size != BUFSIZ || buf[BUFSIZ] == '\n')
break;
}
/* after breaking from loop, check that size is not zero.
* this should only happen if we reach EOF, so return NULL
*/
if (size == 0)
return NULL;
/* Allocate memory for the line plus one extra for the null byte */
pBuffer = malloc (size + 1);
/* reads the contents of the file into pBuffer */
if (size <= BUFSIZ) {
/* Optimization: use memcpy rather than reading
* from disk if the line is small enough
*/
memcpy (pBuffer, buf, size);
} else {
fseek (fp, ftell(fp) - size, SEEK_SET);
fread (pBuffer, 1, size, fp);
}
pBuffer[size] = '\0'; /* set the null terminator byte */
return pBuffer; /* remember to free () this when you are done! */
}
This way will not need the additional call to feof (which is often a hit and miss), and instead relies on what fgets returns to determine if we have reached the end of file.
With these changes, it should be enough to change main to:
int main() {
FILE *fp = fopen("story.txt", "r");
if(fp == NULL){
printf("Error!");
}else{
char *pInput;
/* here we just keep reading until a NULL string is returned */
for (pInput = freadline(fp); pInput != NULL; pInput = freadline(fp))) {
printf("%s", pInput); // output
free (pInput);
}
fclose(fp);
}
return 0;
}

How to read in words from an input file that ignores punctuation using fscanf?

I am trying to use fscanf to read in from an input file while only reading in the letter and ignoring the special characters like commas, periods, etc.
I tried the code below but it does not print anything when I try to print each input word.
I have also tried "%20[a-zA-Z]" and "%20[a-zA-Z] " in the fscanf.
char** input;
input = (char **)malloc(numWordsInput*sizeof(char*));
for (i = 0; i < numWordsInput; i++)
{
fscanf(in_file, "%s", buffer);
sLength = strlen(buffer)+1;
input[i] = (char *)malloc(sLength*sizeof(char));
}
rewind(in_file);
for (i = 0; i < numWordsInput; i++)
{
fscanf(in_file, "%20[a-zA-Z]%*[a-zA-Z]", input[i]);
}
It is unclear why you are attempting to create a pointer-to-pointer to char for each word, and then allocating for each word, but to simply classify the characters that are [a-zA-Z] the C-library provides a number of macros in ctype.h like isalpha() that do exactly that.
(OK, your comment about storing words came as I was done with this part of the answer, so I'll add the word handling in a minute)
To handle file input and to check whether each character is [a-zA-Z], all you need to do is open the file and use a character oriented input function like fgetc and test each character with isalpha(). A short example that does just that is:
#include <stdio.h>
#include <ctype.h>
int main (int argc, char **argv) {
int c;
/* 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;
}
while ((c = fgetc (fp)) != EOF) /* read each char in file */
if (isalpha (c) || c == '\n') /* is it a-zA-Z or \n */
putchar (c); /* output it */
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(the basic stream I/O is buffered anyway (8192 bytes on Linux), so you don't incur a penalty from not reading into a larger buffer)
Example Input File
So if you had a messy input file:
$ cat ../dat/10intmess.txt
8572,;a -2213,;--a 6434,;
a- 16330,;a
- The Quick
Brown%3034 Fox
12346Jumps Over
A
4855,;*;Lazy 16985/,;a
Dog.
11250
1495
Example Use/Output
... and simply wanted to pick the [a-zA-Z] characters from it (and the '\n' characters to preserve line spacing for the example), you would get:
$ ./bin/readalpha ../dat/10intmess.txt
aa
aa
TheQuick
BrownFox
JumpsOver
A
Lazya
Dog
If you wanted to also include [0-9], you would simply use isalnum (c) instead of isalpha (c).
You are also free to read a line at a time (or a word at a time) for that matter and simply walk-a-pointer down the buffer doing the same thing. For instance you could do:
#include <stdio.h>
#include <ctype.h>
#define MAXC 4096
int main (int argc, char **argv) {
char buf[MAXC];
/* 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;
}
while (fgets (buf, MAXC, fp)) { /* read each line in file */
char *p = buf; /* pointer to bufffer */
while (*p) { /* loop over each char */
if (isalpha (*p) || *p == '\n') /* is it a-zA-Z or \n */
putchar (*p); /* output it */
p++;
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(output is the same)
Or if you prefer using indexes rather than a pointer, you could use:
while (fgets (buf, MAXC, fp)) /* read each line in file */
for (int i = 0; buf[i]; i++) /* loop over each char */
if (isalpha (buf[i]) || buf[i] == '\n') /* is it a-zA-Z or \n */
putchar (buf[i]); /* output it */
(output is the same)
Look things over and let me know if you have questions. If you do need to do it a word at a time, you will have to significantly add to your code to protect your number of pointers and realloc as required. Give me a sec and I'll help there, in the mean time, digest the basic character classification above.
Allocating And Storing Individual Words of Only Alpha-Characters
As you can imaging, dynamically allocating pointers and then allocating for, and storing each word made up of only alpha-characters is a bit more involved. It's not any more difficult, your simply have to keep track of the number of pointers allocated, and if you have used all allocated pointers, reallocate and keep going.
The place where new C programmers usually get into trouble is failing to validate each required step to ensure each allocation succeeds to avoid writing to memory you don't own invoking Undefined Behavior.
Reading individual words with fscanf is fine. Then to ensure you have alpha characters to store, it makes sense to extract the alpha characters into a separate temporary buffer and checking whether there were actually any stored, before allocating storage for that word. The longest word in the non-medical unabridged dictionary is 29-characters, so a fixed buffer larger than that will suffice (1024 chars is used below -- Don't Skimp on Buffer Size!)
So what you need for storing each word and tracking the number of pointers allocated and number of pointers used, as well as your fixed buffer to read into would be similar to:
#define NPTR 8 /* initial number of pointers */
#define MAXC 1024
...
char **input, /* pointers to words */
buf[MAXC]; /* read buffer */
size_t nptr = NPTR, /* number of allcoated pointers */
used = 0; /* number of used pointers */
After allocating your initial number of pointers you can read each word and then parse the alpha-characters from it similar to the following:
while (fscanf (fp, "%s", buf) == 1) { /* read each word in file */
size_t ndx = 0; /* alpha char index */
char tmp[MAXC]; /* temp buffer for alpha */
if (used == nptr) /* check if realloc needed */
input = xrealloc2 (input, sizeof *input, &nptr); /* realloc */
for (int i = 0; buf[i]; i++) /* loop over each char */
if (isalpha (buf[i])) /* is it a-zA-Z or \n */
tmp[ndx++] = buf[i]; /* store alpha chars */
if (!ndx) /* if no alpha-chars */
continue; /* get next word */
tmp[ndx] = 0; /* nul-terminate chars */
input[used] = dupstr (tmp); /* allocate/copy tmp */
if (!input[used]) { /* validate word storage */
if (used) /* if words already stored */
break; /* break, earlier words still good */
else { /* otherwise bail */
fputs ("error: allocating 1st word.\n", stderr);
return 1;
}
}
used++; /* increment used count */
}
(note: when the number of used pointers equals the number allocated, then the input is reallocated to twice the current number of pointers)
The xrealloc2 and dupstr functions are simply helper functions. xrealloc2 simply calls realloc and doubles the size of the current allocation, validating the allocation and returning the reallocated pointer on success, or currently exiting on failure -- you can change it to return NULL to handle the error if you like.
/** realloc 'ptr' of 'nelem' of 'psz' to 'nelem * 2' of 'psz'.
* returns pointer to reallocated block of memory with new
* memory initialized to 0/NULL. return must be assigned to
* original pointer in caller.
*/
void *xrealloc2 (void *ptr, size_t psz, size_t *nelem)
{ void *memptr = realloc ((char *)ptr, *nelem * 2 * psz);
if (!memptr) {
perror ("realloc(): virtual memory exhausted.");
exit (EXIT_FAILURE);
/* return NULL; */
} /* zero new memory (optional) */
memset ((char *)memptr + *nelem * psz, 0, *nelem * psz);
*nelem *= 2;
return memptr;
}
The dupstr function is just a normal strdup, but since not all compilers provide strdup, it is used to ensure portability.
/** allocate storage for s + 1 chars and copy contents of s
* to allocated block returning new sting on success,
* NULL otherwise.
*/
char *dupstr (const char *s)
{
size_t len = strlen (s);
char *str = malloc (len + 1);
if (!str)
return NULL;
return memcpy (str, s, len + 1);
}
Using the helpers just keeps the main body of your code a tad cleaner rather than cramming it all into your loop.
Putting it altogether you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NPTR 8 /* initial number of pointers */
#define MAXC 1024
void *xrealloc2 (void *ptr, size_t psz, size_t *nelem);
char *dupstr (const char *s);
int main (int argc, char **argv) {
char **input, /* pointers to words */
buf[MAXC]; /* read buffer */
size_t nptr = NPTR, /* number of allcoated pointers */
used = 0; /* number of used pointers */
/* 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;
}
input = malloc (nptr * sizeof *input); /* allocate nptr pointers */
if (!input) { /* validate every allocation */
perror ("malloc-input");
return 1;
}
while (fscanf (fp, "%s", buf) == 1) { /* read each word in file */
size_t ndx = 0; /* alpha char index */
char tmp[MAXC]; /* temp buffer for alpha */
if (used == nptr) /* check if realloc needed */
input = xrealloc2 (input, sizeof *input, &nptr); /* realloc */
for (int i = 0; buf[i]; i++) /* loop over each char */
if (isalpha (buf[i])) /* is it a-zA-Z or \n */
tmp[ndx++] = buf[i]; /* store alpha chars */
if (!ndx) /* if no alpha-chars */
continue; /* get next word */
tmp[ndx] = 0; /* nul-terminate chars */
input[used] = dupstr (tmp); /* allocate/copy tmp */
if (!input[used]) { /* validate word storage */
if (used) /* if words already stored */
break; /* break, earlier words still good */
else { /* otherwise bail */
fputs ("error: allocating 1st word.\n", stderr);
return 1;
}
}
used++; /* increment used count */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < used; i++) {
printf ("word[%3zu]: %s\n", i, input[i]);
free (input[i]); /* free storage when done with word */
}
free (input); /* free pointers */
return 0;
}
/** realloc 'ptr' of 'nelem' of 'psz' to 'nelem * 2' of 'psz'.
* returns pointer to reallocated block of memory with new
* memory initialized to 0/NULL. return must be assigned to
* original pointer in caller.
*/
void *xrealloc2 (void *ptr, size_t psz, size_t *nelem)
{ void *memptr = realloc ((char *)ptr, *nelem * 2 * psz);
if (!memptr) {
perror ("realloc(): virtual memory exhausted.");
exit (EXIT_FAILURE);
/* return NULL; */
} /* zero new memory (optional) */
memset ((char *)memptr + *nelem * psz, 0, *nelem * psz);
*nelem *= 2;
return memptr;
}
/** allocate storage for s + 1 chars and copy contents of s
* to allocated block returning new sting on success,
* NULL otherwise.
*/
char *dupstr (const char *s)
{
size_t len = strlen (s);
char *str = malloc (len + 1);
if (!str)
return NULL;
return memcpy (str, s, len + 1);
}
(same input file is used)
Example Use/Output
$ ./bin/readalphadyn ../dat/10intmess.txt
word[ 0]: a
word[ 1]: a
word[ 2]: a
word[ 3]: a
word[ 4]: The
word[ 5]: Quick
word[ 6]: Brown
word[ 7]: Fox
word[ 8]: Jumps
word[ 9]: Over
word[ 10]: A
word[ 11]: Lazy
word[ 12]: a
word[ 13]: Dog
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/readalphadyn ../dat/10intmess.txt
==8765== Memcheck, a memory error detector
==8765== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8765== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==8765== Command: ./bin/readalphadyn ../dat/10intmess.txt
==8765==
word[ 0]: a
word[ 1]: a
word[ 2]: a
word[ 3]: a
word[ 4]: The
word[ 5]: Quick
word[ 6]: Brown
word[ 7]: Fox
word[ 8]: Jumps
word[ 9]: Over
word[ 10]: A
word[ 11]: Lazy
word[ 12]: a
word[ 13]: Dog
==8765==
==8765== HEAP SUMMARY:
==8765== in use at exit: 0 bytes in 0 blocks
==8765== total heap usage: 17 allocs, 17 frees, 796 bytes allocated
==8765==
==8765== All heap blocks were freed -- no leaks are possible
==8765==
==8765== For counts of detected and suppressed errors, rerun with: -v
==8765== 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.
(note: There is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?)
To skip single-character words (or pick the limit you want), you can simply change:
if (ndx < 2) /* if 0/1 alpha-chars */
continue; /* get next word */
Doing that would changed your stored words to:
$ ./bin/readalphadyn ../dat/10intmess.txt
word[ 0]: The
word[ 1]: Quick
word[ 2]: Brown
word[ 3]: Fox
word[ 4]: Jumps
word[ 5]: Over
word[ 6]: Lazy
word[ 7]: Dog

Split a string on the occurance of a particular character [duplicate]

This question already has answers here:
C - split string into an array of strings
(2 answers)
Closed 5 years ago.
I am trying to split a string on every occurrence of a closing bracket and send it to a character array as a line by line in a while loop.
This is the input I am reading in a char * input
(000,P,ray ),(100,D,ray ),(009,L,art ),(0000,C,max ),(0000,S,ben ),(020,P,kay ),(040,L,photography ),(001,C,max ),(0001,S,ben ),(0001,P,kay )
This is the output I am trying to produce in a char each[30] = {}
(000,P,ray ),
(100,D,ray ),
(009,L,art ),
(000,C,max ),
(000,S,ben ),
(020,P,kay ),
(040,L,photography ),
(001,C,max ),
(201,S,ben ),
(301,P,kay )
I copied the input to a char * temp so that strtok() does not change the input. But I am not understanding how to use strtok() inside the while loop condition. Does anyone know how to do it ?
Thanks,
UPDATE:
Sorry if I have violated the rules.
Here's my code -
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]){
size_t len = 0;
ssize_t read;
char * line = NULL;
char *eacharray;
FILE *fp = fopen(argv[1], "r");
char * each = NULL;
while ((read = getline(&line, &len, fp)) != -1) {
printf("%s\n", line);
eacharray = strtok(line, ")");
// printf("%s +\n", eacharray);
while(eacharray != NULL){
printf("%s\n", eacharray);
eacharray = strtok(NULL, ")");
}
}
return 0;
}
It produces an output like this -
(000,P,ray
,(100,D,ray
,(009,L,art
,(0000,C,max
,(0000,S,ben
,(020,P,kay
,(040,L,photography
,(001,C,max
,(0001,S,ben
,(0001,P,kay
I would not use strtok, because your simple parser should first detect an opening brace and then search for a closing one. With strtok, you could just split at a closing brace; then you could not check if there was an opening one, and you'd have to skip the characters until the next opening brace "manually".
BTW: you probably meant each[10][30], not each[30].
See the following code looking for opening and closing braces and copying the content in between (including the braces):
int main(int argc, char *argv[]) {
const char* source ="(000,P,ray ),"
"(100,D,ray ),"
"(009,L,art ),"
"(0000,C,max ),"
"(0000,S,ben ),"
"(020,P,kay ),"
"(040,L,photography ),"
"(001,C,max ),"
"(0001,S,ben ),"
"(0001,P,kay )";
char each[10][30] = {{ 0 }};
const char *str = source;
int i;
for (i=0; i<10; i++) {
const char* begin = strchr(str, '(');
if (!begin)
break;
const char* end = strchr(begin,')');
if (!end)
break;
end++;
ptrdiff_t length = end - begin;
if (length >= 30)
break;
memcpy(each[i], begin, length);
str = end;
}
for (int l=0; l<i; l++) {
printf("%s", each[l]);
if (l!=i-1)
printf(",\n");
}
putchar ('\n');
}
Hope it helps.
There are many ways to approach this problem. Stephan has a good approach using the functions available in string.h (and kindly contributed the example source string). Another basic way to approach this problem (or any string parsing problem) is to simply walk-a-pointer down the string, comparing characters as you go and taking the appropriate action.
When doing so with multiple-delimiters (e.g. ',' and (...), it is often helpful to indicate the "state" of your position within the original string. Here a simple flag in (for inside or outside (...)) well let you control whether you copy characters to your array or skip them.
The rest is just keeping track of your indexes and protecting your array bounds as you loop over each character (more of an accounting problem from a memory standpoint -- which you should do regardless)
Putting the pieces together, and providing additional details in comments in-line below, you could do something like the following:
#include <stdio.h>
#define MAXS 10 /* if you need constants -- declare them */
#define MAXL 30 /* (don't use 'magic numbers' in code) */
int main (void) {
const char* source ="(000,P,ray ),"
"(100,D,ray ),"
"(009,L,art ),"
"(0000,C,max ),"
"(0000,S,ben ),"
"(020,P,kay ),"
"(040,L,photography ),"
"(001,C,max ),"
"(0001,S,ben ),"
"(0001,P,kay )";
char each[MAXS][MAXL] = {{0}},
*p = (char *)source;
int i = 0, in = 0, ndx = 0; /* in - state flag, ndx - row index */
while (ndx < MAXS && *p) { /* loop over all chars filling 'each' */
if (*p == '(') { /* (while protecting your row bounds) */
each[ndx][i++] = *p; /* copy opening '(' */
in = 1; /* set flag 'in'side record */
}
else if (*p == ')') {
each[ndx][i++] = *p; /* copy closing ')' */
each[ndx++][i] = 0; /* nul-terminate */
i = in = 0; /* reset 'i' and 'in' */
}
else if (in) { /* if we are 'in', copy char */
each[ndx][i++] = *p;
}
if (i + 1 == MAXL) { /* protect column bounds */
fprintf (stderr, "record exceeds %d chars.\n", MAXL);
return 1;
}
p++; /* increment pointer */
}
for (i = 0; i < ndx; i++) /* display results */
printf ("each[%2d] : %s\n", i, each[i]);
return 0;
}
(note: above, each row in each will be nul-terminated by default as a result of initializing all characters in each to zero at declaration, but it is still good practice to affirmatively nul-terminate all strings)
Example Use/Output
$ ./bin/testparse
each[ 0] : (000,P,ray )
each[ 1] : (100,D,ray )
each[ 2] : (009,L,art )
each[ 3] : (0000,C,max )
each[ 4] : (0000,S,ben )
each[ 5] : (020,P,kay )
each[ 6] : (040,L,photography )
each[ 7] : (001,C,max )
each[ 8] : (0001,S,ben )
each[ 9] : (0001,P,kay )
Get comfortable using either method. You can experiment whether using if.. else if.. or a switch best fits any parsing problem. The functions in string.h can be the better choice. It all depends on your input. Being comfortable with both approaches helps you better tailor your code to the problem at hand.
Example with getline and realloc of Rows
Since you are using getline to read each line, it will potentially read and allocate storage for an unlimited number of records (e.g. (...)). The way to handle this is to allocate storage for your records (pointers) dynamically, keep track of the number of pointers used, and realloc to allocate more pointers when you reach your record limit. You will need to validate each allocation, and understand you allocate each as a pointer-to-pointer-to-char (e.g. char **each) instead of each being a 2D array (e.g. char each[rows][cols]). (though you will still access and use the string held with each the same way (e.g. each[0], each[1], ...))
The code below will read from the filename given as the first argument (or from stdin if no argument is given). The approach is a standard approach for handling this type problem. each is declared as char **each = NULL; (a pointer-to-pointer-to-char). You then allocate an initial number of pointers (rows) for each with:
each = malloc (rows * sizeof *each); /* allocate rows no. of pointers */
if (!each) { /* validate allocation */
perror ("each - memory exhausted"); /* throw error */
return 1;
}
You then use getline to read each line into a buffer (buf) and pass a pointer to buf to the logic we used above. (NOTE, you must preserve a pointer to buf as buf points to storage dynamically allocated by getline that you must free later.)
The only addition to the normal parsing logic is we now need to allocate storage for each of the records we parse, and assign the address of the block of memory holding each record to each[x]. (we use strcpy for that purpose after allocating the storage for each record).
To simplify parsing, we originally parse each record into a fixed size buffer (rec) since we do not know the length of each record ahead of time. You can dynamically allocate/reallocate for rec as well, but that adds an additional level of complexity -- and I suspect you will struggle with the additions as they stand now. Just understand we parse each record from buf into rec (which we set at 256 chars #define MAXR 256 -- more than ample for the expected 30-31 char record size) Even though we use a fixed length rec, we still check i against MAXR to protect the fixed array bounds.
The storage for each record and copy of parsed records from rec to each[ndx] is handled when a closing ) is encountered as follows:
(note - storage for the nul-character is included in 'i' where you would normally see 'i + 1')
each[ndx] = malloc (i); /* allocate storage for rec */
if (!each[ndx]) { /* validate allocation */
perror ("each[ndx] - memory exhausted");
return 1;
}
strcpy (each[ndx], rec);/* copy rec to each[ndx] */
(note: by approaching allocation in this manner, you allocate the exact amount of storage you need for each record. There is no wasted space. You can handle 1 record or 10,000,000 records (to the extent of the memory on your computer))
Here is your example. Take time to understand what every line does and why. Ask questions if you do not understand. This is the meat-and-potatoes of dynamic allocation and once you get it -- you will have a firm understanding of the basics for handling any of your storage needs.
#include <stdio.h>
#include <stdlib.h> /* for malloc, realloc */
#include <string.h> /* for strcpy */
#define ROWS 10 /* initial number of rows to allocate */
#define MAXR 256 /* maximum record length between (...) */
int main (int argc, char **argv) {
int in = 0; /* in - state flag */
char **each = NULL, /* pointer to pointer to char */
*buf = NULL; /* buffer for getline */
size_t rows = ROWS, /* currently allocated row pointers */
ndx = 0, /* ndx - row index */
n = 0, /* buf size (0 - getline decides) */
i = 0; /* loop counter */
ssize_t nchr = 0; /* num chars read by getline (return) */
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;
}
each = malloc (rows * sizeof *each); /* allocate rows no. of pointers */
if (!each) { /* validate allocation */
perror ("each - memory exhausted"); /* throw error */
return 1;
}
while ((nchr = getline (&buf, &n, fp) != -1)) { /* read line into buf */
char *p = buf, /* pointer to buf */
rec[MAXR] = ""; /* temp buffer to hold record */
while (*p) { /* loop over all chars filling 'each' */
if (*p == '(') { /* (while protecting your row bounds) */
rec[i++] = *p; /* copy opening '(' */
in = 1; /* set flag 'in'side record */
}
else if (*p == ')') {
rec[i++] = *p; /* copy closing ')' */
rec[i++] = 0; /* nul-terminate */
each[ndx] = malloc (i); /* allocate storage for rec */
if (!each[ndx]) { /* validate allocation */
perror ("each[ndx] - memory exhausted");
return 1;
}
strcpy (each[ndx], rec);/* copy rec to each[ndx] */
i = in = 0; /* reset 'i' and 'in' */
ndx++; /* increment row index */
if (ndx == rows) { /* check if rows limit reached */
/* reallocate 2X number of pointers using tmp pointer */
void *tmp = realloc (each, rows * sizeof *each * 2);
if (!tmp) { /* validate realloc succeeded */
perror ("realloc each - memory exhausted");
goto memfull; /* each still contains original recs */
}
each = tmp; /* assign reallocated block to each */
rows *= 2; /* update rows with current pointers */
}
}
else if (in) { /* if we are 'in', copy char */
rec[i++] = *p;
}
if (i + 1 == MAXR) { /* protect column bounds */
fprintf (stderr, "record exceeds %d chars.\n", MAXR);
return 1;
}
p++; /* increment pointer */
}
}
memfull:; /* labet for goto */
free (buf); /* free memory allocated by getline */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (i = 0; i < ndx; i++) { /* display results */
printf ("each[%2zu] : %s\n", i, each[i]);
free (each[i]); /* free memory for each record */
}
free (each); /* free pointers */
return 0;
}
(note: since nchr isn't used to trim the '\n' from the end of the buffer read by getline, you can eliminate that variable. Just note that there is no need to call strlen on the buffer returned by getline as the number of characters read is the return value)
Example Use/Output
Note: for the input test, I just put your line of records in the file dat/delimrecs.txt and copied it 4 times giving a total of 40 records in 4 lines.
$ ./bin/parse_str_state_getline <dat/delimrecs.txt
each[ 0] : (000,P,ray )
each[ 1] : (100,D,ray )
each[ 2] : (009,L,art )
each[ 3] : (0000,C,max )
each[ 4] : (0000,S,ben )
<snip 5 - 34>
each[35] : (020,P,kay )
each[36] : (040,L,photography )
each[37] : (001,C,max )
each[38] : (0001,S,ben )
each[39] : (0001,P,kay )
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/parse_str_state_getline <dat/delimrecs.txt
==13035== Memcheck, a memory error detector
==13035== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13035== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==13035== Command: ./bin/parse_str_state_getline
==13035==
each[ 0] : (000,P,ray )
each[ 1] : (100,D,ray )
each[ 2] : (009,L,art )
each[ 3] : (0000,C,max )
each[ 4] : (0000,S,ben )
<snip 5 - 34>
each[35] : (020,P,kay )
each[36] : (040,L,photography )
each[37] : (001,C,max )
each[38] : (0001,S,ben )
each[39] : (0001,P,kay )
==13035==
==13035== HEAP SUMMARY:
==13035== in use at exit: 0 bytes in 0 blocks
==13035== total heap usage: 46 allocs, 46 frees, 2,541 bytes allocated
==13035==
==13035== All heap blocks were freed -- no leaks are possible
==13035==
==13035== For counts of detected and suppressed errors, rerun with: -v
==13035== 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.
This is a lot to take in, but this is a basic minimal example of the framework for handling an unknown number of objects.

C issue in which a while(fgets) loop inside another while loop is only executed once

This is for a college introductory CS course I am taking. I have been stuck on this problem for a few days, and our department resources have been swamped with other students on this assignment. Searching has not been much help, but I'm not sure I'm wording it correctly.
I am trying to write a program which reads words from a text file, compares them against another text file containing a list of correctly spelled words, and prints the incorrect words from the first text file.
I have written one while(fgets) loop to read each line of the input text file, a while loop nested inside it to tokenize each line into individual words, and finally another while(fgets) loop nested inside that to compare each token against each line in the dictionary file. I have seen some questions in which the inside loop has to be "reset", but I am using the strtok function to do that.
Here is a link to a gist of the code with samples of the input files.
Here is the output from this input:
Misspelled words in words.txt :
hello, A do not match
hello, AA do not match
hello, AAA do not match
hello, thirtytwomo do not match
hello, this do not match
hello, thisaway do not match
hello, contains do not match
hello, few do not match
hello, words do not match
hello, hello match
this
contanes
a
few
words
they
are
seperated
by
multaple
And this is the relevant loop in question:
while (fgets(tempBuffer, sizen, instream) != NULL) {
tempBuffer[strlen(tempBuffer) - 1] = '\0';
//remove the endline character from the line
char *tempToken = strtok(tempBuffer, " ");
//tokenize the line on space:
while (tempToken != NULL)
//will be null at the end of each line
{
char *tempCheck = malloc(sizeof(char) * (strlen(tempToken) + 1));
//build dynamic array to hold string to check
strcpy(tempCheck, tempToken);
while (fgets(tempDictBuffer, sizen, dictInstream) != NULL) {
//compares against each line in dictionary
tempDictBuffer[strlen(tempDictBuffer) - 1] = '\0';
//remove the endline character from the line
char *tempDict = malloc(
sizeof(char) * (strlen(tempDictBuffer) + 1));
//build dynamic array to hold string from dictionary
strcpy(tempDict, tempDictBuffer);
if (strcmp(tempCheck, tempDict) == 0) {
printf("%s, %s match\n", tempCheck, tempDict);
//if the string matches a dictionary line, this prints
result = 1;
//sets flag
} else {
printf("%s, %s do not match\n", tempCheck, tempDict);
//if the string does not match, this prints
}
free(tempDict);
tempDict = NULL;
}
if (result != 1) {
printf("%s\n", tempCheck);
//checks flag
}
result = 0;
//resets flag
free(tempCheck);
tempCheck = NULL;
tempToken = strtok(NULL, " ");
//gets next token in line and reruns second while loop
}
Thanks for any help you can provide!
This is somewhat coincidental, but I happened to have a few functions that essentially do what it is you are trying to do. It may not be a perfect fit, but the following with read 2 files, load the lines of each into an array of pointers to char, then split each line into tokens and compare each of the respective tokens to determine if spelling differs and output the words on each line that are not spelled the same.
It may provide you with a few additional ideas about how to approach your problem. Note, this is by way of example, and not represented to be fully tested for all corner-cases, etc. Since you were allocating storage dynamically, it helps to continue that approach in tokenizing each line. A function that will fully tokenize each line and return the words in an array of pointers to char, cuts down substantially on the number and type of nested loops required. For what it's worth, take a look. Also note that the prn_chararray function is not used in the code below, but is left as a convenience:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NMAX 256
#define BUFL 64
#define MAXS 32
char **readtxtfile (char *fn, size_t *idx);
char **splitstr (char *s, size_t *n);
char **realloc_char (char **p, size_t *n);
void prn_chararray (char **ca);
void free_chararray (char **ca);
int main (int argc, char **argv) {
if (argc < 3 ) {
fprintf (stderr, "error: insufficient input, usage: %s <filename1> <filename2>\n", argv[0]);
return 1;
}
size_t file1_size = 0; /* placeholders to be filled by readtxtfile */
size_t file2_size = 0; /* for general use, not needed to iterate */
size_t i = 0; /* general counter/iterator */
size_t linemin = 0; /* minimum of comparison lines in file1/2 */
/* read each file into an array of strings,
number of lines read, returned in file_size */
char **file1 = readtxtfile (argv[1], &file1_size);
char **file2 = readtxtfile (argv[2], &file2_size);
linemin = file1_size < file2_size ? file1_size : file2_size;
for (i = 0; i < linemin; i++)
{
size_t nwords1 = 0; /* number of words read file1 line */
size_t nwords2 = 0; /* number of words read file2 line */
size_t wordmin = 0; /* minimum number of words in file1/2 lines */
size_t j = 0; /* general counter/iterator */
printf ("\n file1[%2zu] : %s\n file2[%2zu] : %s\n\n", i, file1[i], i, file2[i]);
char **f1words = splitstr (file1[i], &nwords1);
char **f2words = splitstr (file2[i], &nwords2);
if (!f1words || !f2words) {
fprintf (stderr, "error: word splitting falure.\n");
continue;
}
wordmin = nwords1 < nwords2 ? nwords1 : nwords2;
for (j = 0; j < wordmin; j++)
{
if (strcmp (f1words[j], f2words[j]))
printf (" %16s != %s\n", f1words[j], f2words[j]);
}
free_chararray (f1words);
free_chararray (f2words);
f1words = NULL;
f2words = NULL;
}
/* simple free memory function */
if (file1) free_chararray (file1);
if (file2) free_chararray (file2);
return 0;
}
char** readtxtfile (char *fn, size_t *idx)
{
if (!fn) return NULL; /* validate filename provided */
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 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 */
if (!(fp = fopen (fn, "r"))) {
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;
array[*idx] = strdup (ln); /* allocate/copy ln to array */
(*idx)++; /* increment value at index */
if (*idx == nmax) /* if lines exceed nmax, reallocate */
array = realloc_char (array, &nmax);
}
if (ln) free (ln); /* free memory allocated by getline */
if (fp) fclose (fp); /* close open file descriptor */
return array;
}
/* split string 's' into separate words including break on
space as well as non-printing and format characters
return pointer to array of pointers to strings 'a' and
number of words in 'n' */
char **splitstr (char *s, size_t *n)
{
if (!s || !*s ) return NULL;
char *p = s; /* pointer to char */
char buf[BUFL] = {0}; /* temporary buffer */
char *bp = buf; /* pointer to buf */
size_t maxs = MAXS; /* check for reallocation */
*n = 0; /* index number of tokens */
/* allocate and validate array of pointer to char */
char **a = calloc (MAXS, sizeof *a);
if (!a) {
fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
return NULL;
}
while (*p) /* for each char in string1 */
{
/* skip each non-print/format char */
while (*p && (*p <= ' ' || *p > '~'))
p++;
if (!*p) break; /* break if end reached */
while (*p > ' ' && *p <= '~') /* for each printable char */
{
*bp = *p++; /* copy to strings buffer */
bp++; /* advance to nex position */
}
*bp = 0; /* null-terminate strings */
a[*n] = strdup (buf); /* alloc/copy buf to a[*n] */
(*n)++; /* next index in strings */
if (*n == maxs) /* check if *n exceeds maxs */
a = realloc_char (a, &maxs); /* realloc if a if reqd */
bp = buf; /* reset bp to start of buf */
}
return a;
}
/* 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);
}
/* realloc an array of pointers to strings setting memory to 0.
* reallocate an array of character arrays setting
* newly allocated memory to 0 to allow iteration
*/
char **realloc_char (char **p, size_t *n)
{
#ifdef DEBUG
printf ("\n reallocating %zu to %zu (size: %lu)\n", *n, *n * 2, 2 * *n * sizeof *p);
#endif
char **tmp = realloc (p, 2 * *n * sizeof *p);
if (!tmp) {
fprintf (stderr, "%s() error: reallocation failure.\n", __func__);
// return NULL;
exit (EXIT_FAILURE);
}
p = tmp;
memset (p + *n, 0, *n * sizeof *p); /* memset new ptrs 0 */
*n *= 2;
return p;
}
Input Files
$ cat dat/words1.txt
Eye have a spelling chequer,
It came with my Pea Sea.
It plane lee marks four my revue,
Miss Steaks I can knot sea.
Eye strike the quays and type a whirred,
And weight four it two say,
Weather eye am write oar wrong,
It tells me straight aweigh.
Eye ran this poem threw it,
Your shore real glad two no.
Its vary polished in its weigh.
My chequer tolled me sew.
A chequer is a bless thing,
It freeze yew lodes of thyme.
It helps me right all stiles of righting,
And aides me when eye rime.
Each frays come posed on my screen,
Eye trussed too bee a joule.
The chequer pours over every word,
Two cheque sum spelling rule.
$ cat dat/words2.txt
I have a spelling checker,
It came with my Pin See.
It plainly skips marks for my revue,
Mistakes skip I can not see.
I strike the keys and type a word,
And wait for it to say,
Whether I am right or wrong,
It tells me straight away.
I ran this poem through it,
Your are real glad too no.
Its very polished in its way.
My checker told me so.
A checker is a blessed thing,
It frees you lots of time.
It helps me write all styles of writing,
And helps me when I rhyme.
Each pharse composed up on my screen,
I trust too bee a jewel.
The checker pours over every word,
Two check some spelling rule.
Output
$ ./bin/getline_cmplines dat/words1.txt dat/words2.txt
file1[ 0] : Eye have a spelling chequer,
file2[ 0] : I have a spelling checker,
Eye != I
chequer, != checker,
file1[ 1] : It came with my Pea Sea.
file2[ 1] : It came with my Pin See.
Pea != Pin
Sea. != See.
file1[ 2] : It plane lee marks four my revue,
file2[ 2] : It plainly skips marks for my revue,
plane != plainly
lee != skips
four != for
file1[ 3] : Miss Steaks I can knot sea.
file2[ 3] : Mistakes skip I can not see.
Miss != Mistakes
Steaks != skip
knot != not
sea. != see.
file1[ 4] : Eye strike the quays and type a whirred,
file2[ 4] : I strike the keys and type a word,
Eye != I
quays != keys
whirred, != word,
file1[ 5] : And weight four it two say,
file2[ 5] : And wait for it to say,
weight != wait
four != for
two != to
file1[ 6] : Weather eye am write oar wrong,
file2[ 6] : Whether I am right or wrong,
Weather != Whether
eye != I
write != right
oar != or
<snip>
Leak Check
$ valgrind ./bin/getline_cmplines dat/words1.txt dat/words2.txt
==5670== Memcheck, a memory error detector
==5670== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==5670== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==5670== Command: ./bin/getline_cmplines dat/words1.txt dat/words2.txt
==5670==
file1[ 0] : Eye have a spelling chequer,
file2[ 0] : I have a spelling checker,
Eye != I
chequer, != checker,
<snip>
==5670==
==5670== HEAP SUMMARY:
==5670== in use at exit: 0 bytes in 0 blocks
==5670== total heap usage: 330 allocs, 330 frees, 18,138 bytes allocated
==5670==
==5670== All heap blocks were freed -- no leaks are possible
==5670==
==5670== For counts of detected and suppressed errors, rerun with: -v
==5670== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

c reading separate words from text file using fscanf()

I am writing a quiz program. The program should read question, answers and correct answer from csv file.
Then it should store them in array.
void read(char question[][50], char answer1[10][10], char answer2[10][10], char answer3[10][10], char answer4[10][10], int correctAnswer[10], int *size, char fileName[], int noOfQuestion){
FILE *reader;
int count;
char qBuffer[50];
char ansBuffer1[50];
char ansBuffer2[50];
char ansBuffer3[50];
char ansBuffer4[50];
int iBuffer = 0;
*size = 0;
//open file
reader = fopen(fileName, "r");
//checking file is open or not
if (reader == NULL)
{
printf("Unable to open file %s", fileName);
}
else
{
fscanf(reader, "%100[^\t*\?,],%[^,],%[^,],%[^,],%[^,],%d", size);
for (count = 0; feof(reader) == 0 && count<*size && count<noOfQuestion; count++){
//Reading file
fscanf(reader, "%100[^\t*\?,],%[^,],%[^,],%[^,],%[^,],%d", qBuffer, ansBuffer1, ansBuffer2, ansBuffer3, ansBuffer4, iBuffer);
//Storing data
strcpy(question[count], qBuffer);
strcpy(answer1[count], ansBuffer1);
strcpy(answer2[count], ansBuffer2);
strcpy(answer3[count], ansBuffer3);
strcpy(answer4[count], ansBuffer4);
correctAnswer[count] = iBuffer;
// Check Correct Number of Items Read
if( count == noOfQuestion )
{
printf("There are more items in the file than MaxNoItems specifies can be stored in the output arrays.\n\n");
*size = count;
}
else if( count != *size - 1 )
{
printf("File is corrupted. Not as many items in the file as specified at the top.\n\n");
*size = count;
}
//Break if reached end of file.
if (feof(reader))
{ break;}
}
fclose(reader);
}
}
This the csv file to read from. each question and answers are in one line.
What function do you use to open a file?,fscanf,fclose,fopen,main,3
Which of the following is not a variable type?,int,float,char,string,4
How many bytes is a character?,8,4,2,1,4
What programming language have you been studying this term?,B,A,D,C,4
Which of the following is a comment?,#comment,//comment,$comment,%comment,2
Which of these is in the C Standard Library?,stdio.h,studio.h,iostream,diskio.h,1
What tool do we use to compile?,compiler,builder,linker,wrench,1
What function do you use to close a file?,fscanf,fclose,fopen,main,2
How do you include a file?,#include,//include,$include,%include,1
What are you doing this quiz on?,paper,whiteboard,computer,chalkboard,3
I worked to find a way to solve the issues in your code, however there just isn't a clean way to follow your double-read of each line an make it work in a reasonable way. The structural issue you have is attempting to read the line twice, first to determine the size and next to try and read the actual values. This has many pitfalls.
Instead of trying to read each line in a piecemeal manner, it is far better to read an entire line at a time using the line-oriented input functions provided by C (fgets, getline). It will make your code much more flexible and make life easier on you as well. The basic approach is to read a line at a time into a 'buffer', then using the tools provided, extract what you need from the line, store it in a way that makes sense, and move on to the next line.
There is just no way to hardcode a bunch of arrays in your function argument list and have it work in a sane way. The proper way to do it is to pass a pointer to some type datastruct to your function, have your function fill it, allocating memory as needed, and provide a pointer in return. In your case a simple structure makes a lot more sense that one two-dimensional array for each question you expect to read.
It is far better to define an initial size for the expected number questions, (MAXQ 128 below), and allocate storage for that amount. You can do the same for expected answers per question (MAXA 16 below). If you end up reading more than each, you can easily reallocate to handle the data.
Once you have your struct filled (or array of structs), you make that data available to your main code by a simple return. You then have a single pointer to your data that you can easily pass you a print function or wherever else you need the data. Since the storage for your data was allocated dynamically, you are responsible for freeing the memory used when it is no longer needed.
I have provided examples of both a print and free function to illustrate passing the pointer to the data between functions as well as the practical printing and freeing of the memory.
Designing your code in a similar manner will save you a lot of headaches in the long run. There are many ways to do this, the example below is simply one approach. I commented the code to help you follow along. Take a look and let me know if you have questions.
Note: I have replaced the original readQAs function with the version I originally wrote, but had a lingering issue with. When using getline you must preserve the starting address for any buffer allocated by getline or repetitive calls to getline will result in a segfault when getline attempts to reallocate its buffer. Basically, getline needs a way of keeping track of the memory it has used. You are free to chop the buffer allocated by getline up any way you want, as long as you preserve the starting address of the originally allocated buffer. Keeping a pointer to the original is sufficient.
This can be particularly subtle when you pass the buffer to functions that operate on the string such as strtok or strsep. Regardless, the result of failing to preserve the start of the buffer allocated by getline will result in a segfault at whatever loop exhausts the initial 120-byte buffer allocated by getline receiving __memcpy_sse2 () from /lib64/libc.so.6 If you never exhaust the original 120-byte buffer, you will never experience a segfault. Bottom line, always preserve the starting address for the buffer allocated by getline.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXQ 128
#define MAXA 16
typedef struct {
char *q;
char **ans;
unsigned int nans;
} ques;
ques **readQAs (char *fn);
void prn_ques (ques **exam);
void free_ques (ques **exam);
int main (int argc, char **argv) {
if (argc < 2) {
fprintf (stderr,"\n error: insufficient input. Usage: %s <csvfile>\n\n", argv[0]);
return 1;
}
ques **exam = NULL; /* pointer to pointer to struct */
/* allocate/fill exam structs with questions/answers */
if ( !( exam = readQAs (argv[1]) ) ) {
fprintf (stderr, "\n error: reading questions/answers from '%s'\n\n", argv[1]);
return 1;
}
prn_ques (exam); /* print the questions/answers */
free_ques (exam); /* free all memory allocated */
return 0;
}
/* allocate and fill array of structs with questions/answers */
ques **readQAs (char *fn)
{
FILE *fp = fopen (fn, "r"); /* open file and validate */
if (!fp) {
fprintf (stderr,"\n error: Unable to open file '%s'\n\n", fn);
return NULL;
}
char *line = NULL; /* line buff, if NULL getline allocates */
size_t n = 0; /* max chars to read (0 - no limit) */
ssize_t nchr = 0; /* num chars actually read by getline */
char *p = NULL; /* general pointer to parse line */
char *sp = NULL; /* second pointer to parse line */
char *lp = NULL; /* line ptr (preserve line start addr) */
size_t qidx = 0; /* index for questions structs */
size_t aidx = 0; /* index for answers within structs */
ques **q = calloc (MAXQ, sizeof (*q)); /* allocate MAXQ ptrs */
if (!q) { fprintf (stderr,"\n Allocation error.\n\n"); return NULL; }
/* for each line in file (fn) */
while ((nchr = getline (&line, &n, fp)) != -1)
{
/* test qidx = MAXQ-1, realloc */
aidx = 0; /* reset ans index each line */
lp = line; /* save line start address */
if (line[nchr - 1] == '\n') /* test/strip trailing newline */
line[--nchr] = 0;
q [qidx] = calloc (1, sizeof (**q)); /* allocate struct */
q [qidx]-> ans = calloc (MAXA, sizeof (*(q[qidx]-> ans)));
/* read question */
*(p = strchr (line, ',')) = 0; /* null-terminate ln at ',' */
q [qidx]-> q = strdup (line); /* alloc/read question */
sp = p + 1; /* sp now starts next ch */
/* read correct answer number */
*(p = strrchr (sp, ',')) = 0; /* null-term ln at last ',' */
q [qidx]-> nans = *(p+1) - '0'; /* save num ans, cvt to %zd */
/* read multi-choice answers */
for (p = strtok (sp, ","); p && *p; p = strtok (NULL, ","))
q [qidx]-> ans [aidx++] = strdup (p); /* alloc/read ans */
line = lp; /* avoid __memcpy_sse2 err */
qidx++; /* inc index for next Q */
}
if (line) free (line); /* free line memory */
if (fp) fclose (fp); /* close file stream */
return q; /* return ptr to array of structs holding Q/A(s) */
}
/* print formatted exam read from file */
void prn_ques (ques **exam)
{
if (!exam) {
fprintf (stderr, "\n %s() error: invalid exam pointer.\n\n", __func__);
return;
}
size_t qidx = 0; /* index for questions structs */
size_t aidx = 0; /* index for answers within structs */
printf ("\nClass Exam\n\n");
while (exam [qidx])
{
printf (" %2zd. %s\n\n", qidx + 1, exam[qidx]-> q);
aidx = 0;
while (exam[qidx]->ans[aidx])
{
if (exam[qidx]-> nans == aidx + 1)
printf ("\t(%c) %-16s (* correct)\n", (int)aidx + 'a', exam[qidx]->ans[aidx]);
else
printf ("\t(%c) %s\n", (int)aidx + 'a', exam[qidx]->ans[aidx]);
aidx++;
}
printf ("\n");
qidx++;
}
printf ("\n");
}
/* free all memory allocated */
void free_ques (ques **exam)
{
if (!exam) {
fprintf (stderr, "\n %s() error: invalid exam pointer.\n\n", __func__);
return;
}
size_t qidx = 0; /* index for questions structs */
size_t aidx = 0; /* index for answers within structs */
while (exam[qidx])
{
if (exam[qidx]->q) free (exam[qidx]->q);
for (aidx = 0; aidx < MAXA; aidx++) {
if (exam[qidx]->ans[aidx]) {
free (exam[qidx]->ans[aidx]);
}
}
free (exam[qidx]->ans);
free (exam[qidx++]);
}
free (exam);
}
output/verification:
$ ./bin/readcsvfile dat/readcsvfile.csv
Class Exam
1. What function do you use to open a file?
(a) fscanf
(b) fclose
(c) fopen (* correct)
(d) main
2. Which of the following is not a variable type?
(a) int
(b) float
(c) char
(d) string (* correct)
3. How many bytes is a character?
(a) 8
(b) 4
(c) 2
(d) 1 (* correct)
4. What programming language have you been studying this term?
(a) B
(b) A
(c) D
(d) C (* correct)
5. Which of the following is a comment?
(a) #comment
(b) //comment (* correct)
(c) $comment
(d) %comment
6. Which of these is in the C Standard Library?
(a) stdio.h (* correct)
(b) studio.h
(c) iostream
(d) diskio.h
7. What tool do we use to compile?
(a) compiler (* correct)
(b) builder
(c) linker
(d) wrench
8. What function do you use to close a file?
(a) fscanf
(b) fclose (* correct)
(c) fopen
(d) main
9. How do you include a file?
(a) #include (* correct)
(b) //include
(c) $include
(d) %include
10. What are you doing this quiz on?
(a) paper
(b) whiteboard
(c) computer (* correct)
(d) chalkboard
valgrind verification:
==16221==
==16221== HEAP SUMMARY:
==16221== in use at exit: 0 bytes in 0 blocks
==16221== total heap usage: 73 allocs, 73 frees, 3,892 bytes allocated
==16221==
==16221== All heap blocks were freed -- no leaks are possible
==16221==
==16221== For counts of detected and suppressed errors, rerun with: -v
==16221== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

Resources