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)
Related
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;
}
I am trying to parse a csv into a dynamically allocated array of structures, however my attempt crashes with a segmentation fault.
Here is the structure of my data:
SO02773202,5087001,0
SO02773203,5087001,0
SO02773204,5087001,0
SO02773205,5087001,0
SO02773206,5087001,14
This is the struct I am parsing the data into:
typedef struct saleslines{
char* salesid;
char* smmcampaignid;
int numberofbottles;
} saleslines_t;
Here is my attempt at parsing the file:
int read_saleslines(saleslines_t* saleslines, int number_of_lines){
char c;
FILE* fp;
fp = fopen(FILENAME, "r"); /* Open the saleslines file */
if(fp == NULL){ /* Crash if file not found */
printf("Error - file not found\n");
return 0;
}
c = getc(fp);
while (c != EOF){
if (c == '\n'){
number_of_lines += 1;
}
c = getc(fp);
}
printf("Number of lines is %d\n", number_of_lines);
saleslines = (saleslines_t*) malloc((number_of_lines * 2) * sizeof(saleslines_t));
/* allocation of the buffer for every line in the File */
char *buf = (char*) malloc(1000);
char *tmp;
if ( ( fp = fopen(FILENAME, "r" ) ) == NULL )
{
printf( "File could not be opened.\n" );
}
int i = 0;
while (fgets(buf, 255, fp) != NULL){
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
tmp = strtok(buf, ",");
saleslines[i].salesid = strdup(tmp);
tmp = strtok(NULL, ",");
saleslines[i].smmcampaignid = strdup(tmp);
tmp = strtok(NULL, ",");
saleslines[i].numberofbottles = atoi(tmp);
printf("Salesid: %s\nCampaign: %s\nBottles: %i\n\n", saleslines[i].salesid , saleslines[i].smmcampaignid, saleslines[i].numberofbottles);
i++;
}
free(buf);
fclose(fp);
printf("Number of lines is %i\n", number_of_lines);
return number_of_lines;
}
For some reason it parses the file and prints the resulting array of structs, however when I call this function immediately after, it crashes with a segfault:
void print_saleslines_struct(saleslines_t* saleslines, int number_of_lines{
int i;
printf("Number of lines is %i", number_of_lines);
for(i = 0; i < number_of_lines; i++){
printf("Salesid:\t %s\n", saleslines[i].salesid);
printf("Campaign:\t %s\n", saleslines[i].smmcampaignid);
printf("# of Bottles:\t %d\n", saleslines[i].numberofbottles);
}
}
I can't seem to find where this memory bug is.
Here is the initialization and main:
saleslines_t* saleslines;
saleslines_summary_t* saleslines_summary;
saleslines_grouped_t* saleslines_grouped;
int number_of_lines = 0;
int* number_of_linesp = &number_of_lines;
/* Main */
int main(){
int chosen_option;
while(1){
printf("What would you like to do?\n");
printf("1. Read saleslines.txt\n");
printf("2. Print saleslines\n");
printf("3. Summarise saleslines\n");
printf("4. Exit the program\n");
scanf("%d", &chosen_option);
switch(chosen_option){
/* case 1 : number_of_lines = read_saleslines_file(saleslines, number_of_lines); break; */
case 1 : number_of_lines = read_saleslines(saleslines, number_of_lines); break;
case 2 : printf("Number of lines is %i", number_of_lines); print_saleslines_struct(saleslines, number_of_lines); break;
case 3 : summarise_saleslines(saleslines, number_of_linesp, saleslines_summary, saleslines_grouped); break;
case 4 : free(saleslines); free(saleslines_summary); free(saleslines_grouped); return 0;
}
}
return 0;
}
Update
The issue seems to be with my initialization of the array of structures.
When I initialize it like this: saleslines_t* saleslines;
and then malloc like this: saleslines = malloc(number_of_lines + 1 * sizeof(saleslines_t);
I get a segfault.
But if I initialize like this: saleslines[600]; (allocating more than the number of lines in the file), everything works.
How can I get around this? I would like to be able to dynamically allocate the number of entries within the struct array.
Edit 2
Here are the changes as suggested:
int read_saleslines(saleslines_t** saleslines, int number_of_lines);
saleslines_t* saleslines;
int number_of_lines = 0;
int main(){
while(1){
printf("What would you like to do?\n");
printf("1. Read saleslines.txt\n");
printf("2. Print saleslines\n");
printf("3. Summarise saleslines\n");
printf("4. Exit the program\n");
printf("Number of saleslines = %i\n", number_of_lines);
scanf("%d", &chosen_option);
switch(chosen_option){
/* case 1 : number_of_lines = read_saleslines_file(saleslines, number_of_lines); break; */
case 1 : number_of_lines = read_saleslines(&saleslines, number_of_lines); break;
case 2 : printf("Number of lines is %i", number_of_lines); print_saleslines_struct(saleslines, number_of_lines); break;
case 3 : summarise_saleslines(saleslines, number_of_linesp, saleslines_summary, saleslines_grouped); break;
case 4 : free(saleslines); free(saleslines_summary); free(saleslines_grouped); return 0;
}
}
return 0;
}
int read_saleslines(saleslines_t** saleslines, int number_of_lines)
{
char c;
FILE* fp;
fp = fopen(FILENAME, "r"); /* Open the saleslines file */
if(fp == NULL){ /* Crash if file not found */
printf("Error - file not found\n");
return 0;
}
c = getc(fp);
while (c != EOF){
if (c == '\n'){
number_of_lines += 1;
}
c = getc(fp);
}
fclose(fp);
printf("Number of lines is %d\n", number_of_lines);
*saleslines = (saleslines_t*) malloc((number_of_lines + 1) * sizeof(saleslines_t));
/* allocation of the buffer for every line in the File */
char *buf = malloc(25);
char *tmp;
if ( ( fp = fopen(FILENAME, "r" ) ) == NULL )
{
printf( "File could not be opened.\n" );
}
int i = 0;
while (fgets(buf, 25, fp) != NULL){
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
tmp = strtok(buf, ",");
(*saleslines)[i].salesid = strdup(tmp);
tmp = strtok(NULL, ",");
(*saleslines)[i].smmcampaignid = strdup(tmp);
tmp = strtok(NULL, ",");
(*saleslines)[i].numberofbottles = atoi(tmp);
printf("Salesid: %s\nCampaign: %s\nBottles: %i\n\n", saleslines[i]->salesid , saleslines[i]->smmcampaignid, saleslines[i]->numberofbottles);
i++;
}
free(buf);
fclose(fp);
printf("Number of lines is %i\n", number_of_lines);
return number_of_lines;
}
The program now segfaults after reading the first element in the struct array.
You have a problem with the arguments of read_saleslines(). The first argument should be a pointer to an array of your structs, meaning a double pointer.
In
int read_saleslines(saleslines_t* saleslines, int number_of_lines){
you want to modify where saleslines is pointing. saleslines is a local variable of the function, and the scope is that function. Once you exit read_saleslines(), the variable is "destroyed", meaning that the value it holds it is not accessible anymore. Adding another level of indirection, a pointer, you can modify the variable that's defined outside the function, being that (ugly) global or other. So, change that argument so that the function prototype matches
int read_saleslines(saleslines_t** saleslines, int *);
and change the places where you access it inside the function (adding an * to access it, for example:
saleslines = (saleslines_t*) malloc((number_of_lines * ...
to
*saleslines = (saleslines_t*) malloc((number_of_lines * ...
and
saleslines[i].salesid = strdup(tmp);
to
(*saleslines)[i].salesid = strdup(tmp);
Then add an & where you use the variable outside the function:
number_of_lines = read_saleslines(saleslines, number_of_lines);
changes to
some_var = read_saleslines(&saleslines, &number_of_lines);
That will make you code work.
You have a large number of errors in your code, and with your approach in general. There is no need to make two-passes over the file to determine the number of lines before allocating and then re-reading the file in an attempt to parse the data. Further, there is no need to tokenize each line to separate the comma-separated-values, sscanf() to parse the two strings and one int is sufficient here after reading each line with fgets.
While you are free to pass any mix of parameters you like and return whatever you like, since you are allocating for an array of struct and reading values into the array, it makes sense to return a pointer to the allocated array from your function (or NULL on failure) and simply update a parameter passed as a pointer to make the total number of lines read available back in the caller.
Further, generally you want to open and validate the file in the caller and pass a FILE* parameter passing the open file stream to your function. With that in mind, you could refactor your function as:
/* read saleslines into array of saleslines_t, allocating for
* salesid, and smmcampaignid within each struct. Return pointer
* to allocated array on success with lines updated to hold the
* number of elements, or NULL otherwise.
*/
saleslines_t *read_saleslines (FILE *fp, size_t *lines)
{
Within your function, you simply need a buffer to hold each line read, a counter to track the number of elements allocated in your array, and a pointer to your array to return. For example, you could do something like the following to handle all three:
char buf[MAXC]; /* buffer to hold line */
size_t maxlines = MINL; /* maxlines allocated */
saleslines_t *sales = NULL; /* pointer to array of struct */
(note: since you are tracking the number of lines read through the pointer lines passed as a parameter, it would make sense to initialize the value at that address to zero)
Now the work of your function begins, you want to read each line into buf and parse the needed information from each line. Since salesid and smmcampaignid are both pointers-to-char in your struct, you will need to allocate a block of memory for each string parsed from the line, copy the string to the new block of memory, and then assign the beginning address for the bock to each of your pointers. To "dynamically" handle allocating elements for your struct, you simply check if the number of lines (*lines) filled equals against the number allocated (maxlines), (or if *lines is zero indicating a need for an initial allocation), and realloc in both cases to either realloc (or newly allocate) storage for your array of struct.
When you realloc you always realloc using a temporary pointer so if realloc fails and returns NULL, you don't overwrite your pointer to the currently allocated block with NULL thereby creating a memory leak.
Putting all that together at the beginning of your function may seem daunting, but it is actually straight forward, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line in file */
char id[MAXC], cid[MAXC]; /* temp arrays to hold strings */
int bottles; /* temp int for numberofbottles */
if (*lines == maxlines || !*lines) { /* check if realloc req'd */
/* always realloc with a temp pointer */
void *tmp = realloc (sales, 2 * maxlines * sizeof *sales);
if (!tmp) { /* if realloc fails, original pointer still valid */
perror ("realloc-sales"); /* throw error */
return sales; /* return current pointer */
} /* (don't exit or return NULL) */
sales = tmp; /* assign reallocated block to sales */
/* (optional) zero newly allocated memory */
memset (sales + *lines, 0, maxlines * sizeof *sales);
maxlines *= 2; /* update maxlines allocated */
}
Now you are ready to parse the wanted information from your line with sscanf, and then following a successful parse of information, you can allocate for each of your salesid and smmcampaignid pointers, copy the parsed information to the new blocks of memory assigning the beginning address to each pointer, respectively, e.g.
/* parse needed data from line (sscanf is fine here) */
if (sscanf (buf, "%1023[^,],%1023[^,],%d", id, cid, &bottles) == 3) {
size_t idlen = strlen (id), /* get lengths of strings */
cidlen = strlen (cid);
sales[*lines].salesid = malloc (idlen + 1); /* allocate string */
if (!sales[*lines].salesid) { /* validate! */
perror ("malloc-sales[*lines].salesid");
break;
}
sales[*lines].smmcampaignid = malloc (cidlen + 1); /* ditto */
if (!sales[*lines].smmcampaignid) {
perror ("malloc-sales[*lines].smmcampaignid");
break;
}
memcpy (sales[*lines].salesid, id, idlen + 1); /* copy strings */
memcpy (sales[*lines].smmcampaignid, cid, cidlen + 1);
sales[(*lines)++].numberofbottles = bottles; /* assign int */
} /* (note lines counter updated in last assignment) */
(note: you can use strdup to both get the length of each string parsed and allocate sufficient memory to hold the string and assign that to your pointer in one-shot, e.g. sales[*lines].salesid = strdup (id);, but... strdup is not required to be included in C99 or later, so it is just as simple to get the length, allocate length + 1 bytes and then memcpy your string manually to ensure portability. Further, since strdup allocates memory, you must validate the pointer returned -- something overlooked by 99% of those using it.)
That's it, when fgets() fails, you have reached EOF, now simply:
return sales; /* return dynamically allocated array of struct */
}
Putting it altogether in a short, working example that takes the filename to read as the first argument to your program (or reads from stdin by default if no argument is given), you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MINL 2
typedef struct saleslines{
char *salesid;
char *smmcampaignid;
int numberofbottles;
} saleslines_t;
/* read saleslines into array of saleslines_t, allocating for
* salesid, and smmcampaignid within each struct. Return pointer
* to allocated array on success with lines updated to hold the
* number of elements, or NULL otherwise.
*/
saleslines_t *read_saleslines (FILE *fp, size_t *lines)
{
char buf[MAXC]; /* buffer to hold line */
size_t maxlines = MINL; /* maxlines allocated */
saleslines_t *sales = NULL; /* pointer to array of struct */
*lines = 0; /* zero lines */
while (fgets (buf, MAXC, fp)) { /* read each line in file */
char id[MAXC], cid[MAXC]; /* temp arrays to hold strings */
int bottles; /* temp int for numberofbottles */
if (*lines == maxlines || !*lines) { /* check if realloc req'd */
/* always realloc with a temp pointer */
void *tmp = realloc (sales, 2 * maxlines * sizeof *sales);
if (!tmp) { /* if realloc fails, original pointer still valid */
perror ("realloc-sales"); /* throw error */
return sales; /* return current pointer */
} /* (don't exit or return NULL) */
sales = tmp; /* assign reallocated block to sales */
/* (optional) zero newly allocated memory */
memset (sales + *lines, 0, maxlines * sizeof *sales);
maxlines *= 2; /* update maxlines allocated */
}
/* parse needed data from line (sscanf is fine here) */
if (sscanf (buf, "%1023[^,],%1023[^,],%d", id, cid, &bottles) == 3) {
size_t idlen = strlen (id), /* get lengths of strings */
cidlen = strlen (cid);
sales[*lines].salesid = malloc (idlen + 1); /* allocate string */
if (!sales[*lines].salesid) { /* validate! */
perror ("malloc-sales[*lines].salesid");
break;
}
sales[*lines].smmcampaignid = malloc (cidlen + 1); /* ditto */
if (!sales[*lines].smmcampaignid) {
perror ("malloc-sales[*lines].smmcampaignid");
break;
}
memcpy (sales[*lines].salesid, id, idlen + 1); /* copy strings */
memcpy (sales[*lines].smmcampaignid, cid, cidlen + 1);
sales[(*lines)++].numberofbottles = bottles; /* assign int */
} /* (note lines counter updated in last assignment) */
}
return sales; /* return dynamically allocated array of struct */
}
int main (int argc, char **argv) {
saleslines_t *sales = NULL; /* pointer to saleslines_t */
size_t nlines;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
sales = read_saleslines (fp, &nlines); /* read saleslines */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < nlines; i++) { /* loop over each */
printf ("sales[%2zu]: %s %s %2d\n", i, sales[i].salesid,
sales[i].smmcampaignid, sales[i].numberofbottles);
free (sales[i].salesid); /* free salesid */
free (sales[i].smmcampaignid); /* free smmcampaignid */
}
free (sales); /* free sales */
return 0;
}
Example Use/Output
$ ./bin/saleslines dat/saleslines.txt
sales[ 0]: SO02773202 5087001 0
sales[ 1]: SO02773203 5087001 0
sales[ 2]: SO02773204 5087001 0
sales[ 3]: SO02773205 5087001 0
sales[ 4]: SO02773206 5087001 14
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/saleslines dat/saleslines.txt
==19819== Memcheck, a memory error detector
==19819== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19819== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==19819== Command: ./bin/saleslines dat/saleslines.txt
==19819==
sales[ 0]: SO02773202 5087001 0
sales[ 1]: SO02773203 5087001 0
sales[ 2]: SO02773204 5087001 0
sales[ 3]: SO02773205 5087001 0
sales[ 4]: SO02773206 5087001 14
==19819==
==19819== HEAP SUMMARY:
==19819== in use at exit: 0 bytes in 0 blocks
==19819== total heap usage: 13 allocs, 13 frees, 935 bytes allocated
==19819==
==19819== All heap blocks were freed -- no leaks are possible
==19819==
==19819== For counts of detected and suppressed errors, rerun with: -v
==19819== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
There is nothing difficult in dynamically allocating for anything. Just take it in small enough pieces that you dot all "I's" and cross all "T's" for each pointer requiring allocation. Look things over and let me know if you have further questions.
I 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.
This code reads a text file line by line. But I need to put those lines in an array but I wasn't able to do it. Now I am getting a array of numbers somehow. So how to read the file into a list. I tried using 2 dimensional list but this doesn't work as well.
I am new to C. I am mostly using Python but now I want to check if C is faster or not for a task.
#include <stdio.h>
#include <time.h>
#include <string.h>
void loadlist(char *ptext) {
char filename[] = "Z://list.txt";
char myline[200];
FILE * pfile;
pfile = fopen (filename, "r" );
char larray[100000];
int i = 0;
while (!feof(pfile)) {
fgets(myline,200,pfile);
larray[i]= myline;
//strcpy(larray[i],myline);
i++;
//printf(myline);
}
fclose(pfile);
printf("%s \n %d \n %d \n ","while doneqa",i,strlen(larray));
printf("First larray element is: %d \n",larray[0]);
/* for loop execution */
//for( i = 10; i < 20; i = i + 1 ){
// printf(larray[i]);
//}
}
int main ()
{
time_t stime, etime;
printf("Starting of the program...\n");
time(&stime);
char *ptext = "String";
loadlist(ptext);
time(&etime);
printf("time to load: %f \n", difftime(etime, stime));
return(0);
}
This code reads a text file line by line. But I need to put those lines in an array but I wasn't able to do it. Now I am getting an array of numbers somehow.
There are many ways to do this correctly. To begin with, first sort out what it is you actually need/want to store, then figure out where that information will come from and finally decide how you will provide storage for the information. In your case loadlist is apparently intended load a list of lines (up to 10000) so that they are accessible through your statically declared array of pointers. (you can also allocate the pointers dynamically, but if you know you won't need more than X of them, statically declaring them is fine (up to the point you cause StackOverflow...)
Once you read the line in loadlist, then you need to provide adequate storage to hold the line (plus the nul-terminating character). Otherwise, you are just counting the number of lines. In your case, since you declare an array of pointers, you cannot simply copy the line you read because each of the pointers in your array does not yet point to any allocated block of memory. (you can't assign the address of the buffer you read the line into with fgets (buffer, size, FILE*) because (1) it is local to your loadlist function and it will go away when the function stack frame is destroyed on function return; and (2) obviously it gets overwritten with each call to fgets anyway.
So what to do? That's pretty simple too, just allocate storage for each line as it is read using the strlen of each line as #iharob says (+1 for the nul-byte) and then malloc to allocate a block of memory that size. You can then simply copy the read buffer to the block of memory created and assign the pointer to your list (e.g. larray[x] in your code). Now the gnu extensions provide a strdup function that both allocates and copies, but understand that is not part of the C99 standard so you can run into portability issues. (also note you can use memcpy if overlapping regions of memory are a concern, but we will ignore that for now since you are reading lines from a file)
What are the rules for allocating memory? Well, you allocate with malloc, calloc or realloc and then you VALIDATE that your call to those functions succeeded before proceeding or you have just entered the realm of undefined behavior by writing to areas of memory that are NOT in fact allocated for your use. What does that look like? If you have your array of pointers p and you want to store a string from your read buffer buf of length len at index idx, you could simply do:
if ((p[idx] = malloc (len + 1))) /* allocate storage */
strcpy (p[idx], buf); /* copy buf to storage */
else
return NULL; /* handle error condition */
Now you are free to allocate before you test as follows, but it is convenient to make the assignment as part of the test. The long form would be:
p[idx] = malloc (len + 1); /* allocate storage */
if (p[idx] == NULL) /* validate/handle error condition */
return NULL;
strcpy (p[idx], buf); /* copy buf to storage */
How you want to do it is up to you.
Now you also need to protect against reading beyond the end of your pointer array. (you only have a fixed number since you declared the array statically). You can make that check part of your read loop very easily. If you have declared a constant for the number of pointers you have (e.g. PTRMAX), you can do:
int idx = 0; /* index */
while (fgets (buf, LNMAX, fp) && idx < PTRMAX) {
...
idx++;
}
By checking the index against the number of pointers available, you insure you cannot attempt to assign address to more pointers than you have.
There is also the unaddressed issue of handling the '\n' that will be contained at the end of your read buffer. Recall, fgets read up to and including the '\n'. You do not want newline characters dangling off the ends of the strings you store, so you simply overwrite the '\n' with a nul-terminating character (e.g. simply decimal 0 or the equivalent nul-character '\0' -- your choice). You can make that a simple test after your strlen call, e.g.
while (fgets (buf, LNMAX, fp) && idx < PTRMAX) {
size_t len = strlen (buf); /* get length */
if (buf[len-1] == '\n') /* check for trailing '\n' */
buf[--len] = 0; /* overwrite '\n' with nul-byte */
/* else { handle read of line longer than 200 chars }
*/
...
(note: that also brings up the issue of reading a line longer than the 200 characters you allocate for your read buffer. You check for whether a complete line has been read by checking whether fgets included the '\n' at the end, if it didn't, you know your next call to fgets will be reading again from the same line, unless EOF is encountered. In that case you would simply need to realloc your storage and append any additional characters to that same line -- that is left for future discussion)
If you put all the pieces together and choose a return type for loadlist that can indicate success/failure, you could do something similar to the following:
/** read up to PTRMAX lines from 'fp', allocate/save in 'p'.
* storage is allocated for each line read and pointer
* to allocated block is stored at 'p[x]'. (you should
* add handling of lines greater than LNMAX chars)
*/
char **loadlist (char **p, FILE *fp)
{
int idx = 0; /* index */
char buf[LNMAX] = ""; /* read buf */
while (fgets (buf, LNMAX, fp) && idx < PTRMAX) {
size_t len = strlen (buf); /* get length */
if (buf[len-1] == '\n') /* check for trailing '\n' */
buf[--len] = 0; /* overwrite '\n' with nul-byte */
/* else { handle read of line longer than 200 chars }
*/
if ((p[idx] = malloc (len + 1))) /* allocate storage */
strcpy (p[idx], buf); /* copy buf to storage */
else
return NULL; /* indicate error condition in return */
idx++;
}
return p; /* return pointer to list */
}
note: you could just as easily change the return type to int and return the number of lines read, or pass a pointer to int (or better yet size_t) as a parameter to make the number of lines stored available back in the calling function.
However, in this case, we have used the initialization of all pointers in your array of pointers to NULL, so back in the calling function we need only iterate over the pointer array until the first NULL is encountered in order to traverse our list of lines. Putting together a short example program that read/stores all lines (up to PTRMAX lines) from the filename given as the first argument to the program (or from stdin if no filename is given), you could do something similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
enum { LNMAX = 200, PTRMAX = 10000 };
char **loadlist (char **p, FILE *fp);
int main (int argc, char **argv) {
time_t stime, etime;
char *list[PTRMAX] = { NULL }; /* array of ptrs initialized 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;
}
printf ("Starting of the program...\n");
time (&stime);
if (loadlist (list, fp)) { /* read lines from fp into list */
time (&etime);
printf("time to load: %f\n\n", difftime (etime, stime));
}
else {
fprintf (stderr, "error: loadlist failed.\n");
return 1;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
while (list[n]) { /* output stored lines and free allocated mem */
printf ("line[%5zu]: %s\n", n, list[n]);
free (list[n++]);
}
return(0);
}
/** read up to PTRMAX lines from 'fp', allocate/save in 'p'.
* storage is allocated for each line read and pointer
* to allocated block is stored at 'p[x]'. (you should
* add handling of lines greater than LNMAX chars)
*/
char **loadlist (char **p, FILE *fp)
{
int idx = 0; /* index */
char buf[LNMAX] = ""; /* read buf */
while (fgets (buf, LNMAX, fp) && idx < PTRMAX) {
size_t len = strlen (buf); /* get length */
if (buf[len-1] == '\n') /* check for trailing '\n' */
buf[--len] = 0; /* overwrite '\n' with nul-byte */
/* else { handle read of line longer than 200 chars }
*/
if ((p[idx] = malloc (len + 1))) /* allocate storage */
strcpy (p[idx], buf); /* copy buf to storage */
else
return NULL; /* indicate error condition in return */
idx++;
}
return p; /* return pointer to list */
}
Finally, in any code your 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.
Use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an uninitialized value and finally to confirm that you have freed 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.
Look things over, let me know if you have any further questions.
It's natural that you see numbers because you are printing a single character using the "%d" specifier. In fact, strings in c are pretty much that, arrays of numbers, those numbers are the ascii values of the corresponding characters. If you instead use "%c" you will see the character that represents each of those numbers.
Your code also, calls strlen() on something that is intended as a array of strings, strlen() is used to compute the length of a single string, a string being an array of char items with a non-zero value, ended with a 0. Thus, strlen() is surely causing undefined behavior.
Also, if you want to store each string, you need to copy the data like you tried in the commented line with strcpy() because the array you are using for reading lines is overwritten over and over in each iteration.
Your compiler must be throwing all kinds of warnings, if it's not then it's your fault, you should let the compiler know that you want it to do some diagnostics to help you find common problems like assigning a pointer to a char.
You should fix multiple problems in your code, here is a code that fixes most of them
void
loadlist(const char *const filename) {
char line[100];
FILE *file;
// We can only read 100 lines, of
// max 99 characters each
char array[100][100];
int size;
size = 0;
file = fopen (filename, "r" );
if (file == NULL)
return;
while ((fgets(line, sizeof(line), file) != NULL) && (size < 100)) {
strcpy(array[size++], line);
}
fclose(file);
for (int i = 0 ; i < size ; ++i) {
printf("array[%d] = %s", i + 1, array[i]);
}
}
int
main(void)
{
time_t stime, etime;
printf("Starting of the program...\n");
time(&stime);
loadlist("Z:\\list.txt");
time(&etime);
printf("Time to load: %f\n", difftime(etime, stime));
return 0;
}
Just to prove how complicated it can be in c, check this out
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
struct string_list {
char **items;
size_t size;
size_t count;
};
void
string_list_print(struct string_list *list)
{
// Simply iterate through the list and
// print every item
for (size_t i = 0 ; i < list->count ; ++i) {
fprintf(stdout, "item[%zu] = %s\n", i + 1, list->items[i]);
}
}
struct string_list *
string_list_create(size_t size)
{
struct string_list *list;
// Allocate space for the list object
list = malloc(sizeof *list);
if (list == NULL) // ALWAYS check this
return NULL;
// Allocate space for the items
// (starting with `size' items)
list->items = malloc(size * sizeof *list->items);
if (list->items != NULL) {
// Update the list size because the allocation
// succeeded
list->size = size;
} else {
// Be optimistic, maybe realloc will work next time
list->size = 0;
}
// Initialize the count to 0, because
// the list is initially empty
list->count = 0;
return list;
}
int
string_list_append(struct string_list *list, const char *const string)
{
// Check if there is room for the new item
if (list->count + 1 >= list->size) {
char **items;
// Resize the array, there is no more room
items = realloc(list->items, 2 * list->size * sizeof *list->items);
if (items == NULL)
return -1;
// Now update the list
list->items = items;
list->size += list->size;
}
// Copy the string into the array we simultaneously
// increase the `count' and copy the string
list->items[list->count++] = strdup(string);
return 0;
}
void
string_list_destroy(struct string_list *const list)
{
// `free()' does work with a `NULL' argument
// so perhaps as a principle we should too
if (list == NULL)
return;
// If the `list->items' was initialized, attempt
// to free every `strdup()'ed string
if (list->items != NULL) {
for (size_t i = 0 ; i < list->count ; ++i) {
free(list->items[i]);
}
free(list->items);
}
free(list);
}
struct string_list *
loadlist(const char *const filename) {
char line[100]; // A buffer for reading lines from the file
FILE *file;
struct string_list *list;
// Create a new list, initially it has
// room for 100 strings, but it grows
// automatically if needed
list = string_list_create(100);
if (list == NULL)
return NULL;
// Attempt to open the file
file = fopen (filename, "r");
// On failure, we now have the responsibility
// to cleanup the allocated space for the string
// list
if (file == NULL) {
string_list_destroy(list);
return NULL;
}
// Read lines from the file until there are no more
while (fgets(line, sizeof(line), file) != NULL) {
char *newline;
// Remove the trainling '\n'
newline = strchr(line, '\n');
if (newline != NULL)
*newline = '\0';
// Append the string to the list
string_list_append(list, line);
}
fclose(file);
return list;
}
int
main(void)
{
time_t stime, etime;
struct string_list *list;
printf("Starting of the program...\n");
time(&stime);
list = loadlist("Z:\\list.txt");
if (list != NULL) {
string_list_print(list);
string_list_destroy(list);
}
time(&etime);
printf("Time to load: %f\n", difftime(etime, stime));
return 0;
}
Now, this will work almost as the python code you say you wrote but it will certainly be faster, there is absolutely no doubt.
It is possible that an experimented python programmer can write a python program that runs faster than that of a non-experimented c programmer, learning c however is really good because you then understand how things work really, and you can then infer how a python feature is probably implemented, so understanding this can be very useful actually.
Although it's certainly way more complicated than doing the same in python, note that I wrote this in nearly 10min. So if you really know what you're doing and you really need it to be fast c is certainly an option, but you need to learn many concepts that are not clear to higher level languages programmers.
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.