I have an input file which contains space separated values
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
I want content of the first line moved to s1, s2 and n respectively
and next n lines contents 2 space separated strings these will move to
production_left and production_right respectively.
The layout will be repeated for subsequent blocks of lines also. The sample data has two blocks of input.
My code given below
int main()
{
char *s1[20], *t1[20];
char *production_left[20], *production_right[20];
int n;
FILE *fp;
fp = fopen("suffix.txt","r");
int iss=0;
do{
fscanf(fp,"%s %s %d",s1[iss],t1[iss],&n);
int i=0;
while(i<n)
{
fscanf(fp,"%s %s",production_left[i],production_right[i]);
i++;
}
}while(!eof(fp));
}
Every time it is giving segmentation fault.
A lot of C, is just keeping clear in your mind, what data you need available in what scope (block) of your code. In your case, the data you are working with is the first (or header) line for each section of your data file. For that you have s1 and t1, but you have nothing to preserve n so that it is available for reuse with your data. Since n holds the number of indexes to expect for production_left and production_right under each heading, simply create an index array, e.g. int idx[MAXC] = {0}; to store each n associated with each s1 and t1. That way you preserve that value for use in iteration later on. (MAXC is just a defined constant for 20 to prevent using magic numbers in your code)
Next, you need to turn to your understanding of pointer declarations and their use. char *s1[MAXC], *t1[MAXC], *production_left[MAXC], *production_right[MAXC]; declares 4 array of pointers (20 pointers each) for s1, t1, production_left and production_right. The pointers are uninitialized and unallocated. While each can be initialized to a pointer value, there is no storage (allocated memory) associated with any of them that would allow copying data. You can't simply use fscanf and assign the same pointer value to each (they would all end up pointing to the last value -- if it remained in scope)
So you have two choices, (1) either use a 2D array, or (2) allocate storage for each string and copy the string to the new block of memory and assign the pointer to the start of that block to (e.g. s1[x]). The strdup function provides the allocate and copy in a single function call. If you do not have strdup, it is a simple function to write using strlen, malloc and memcopy (use memmove if there is a potential that the strings overlap).
Once you have identified what values you need to preserve for later use in your code, you have insured the variables declared are scoped properly, and you have insured that each is properly initialized and storage allocated, all that remains is writing the logic to make things work as you intend.
Before turning to an example, it is worth noting that you are interested in line-oriented input with your data. The scanf family provides formatted input, but often it is better to use a line-oriented function for the actual input (e.g. fgets) and then a separate parse with, e.g. sscanf. In this case it is largely a wash since your first values are string values and the %s format specifier will skip intervening whitespace, but that is often not the case. For instance, you are effectively reading:
char tmp1[MAXC] = "", tmp2[MAXC] = "";
...
if (fscanf (fp, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
That can easily be replaced with a bit more robust:
char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
...
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
(which will validate both the read and the conversion separately)
Above, also note the use of temporary buffers tmp1 and tmp2 (and buf for use with fgets) It is very often advantageous to read input into temporary values that can be validated before final storage for later use.
What remains is simply putting the pieces together in the correct order to accomplish your goals. Below is an example that reads data from the filename given as the first argument (or from stdin if no filename is given) and then outputs the data and frees all memory allocated before exiting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 20
int main (int argc, char **argv) {
char *s1[MAXC], *t1[MAXC],
*production_left[MAXC], *production_right[MAXC];
int idx[MAXC] = {0}, /* storage for 'n' values */
iss = 0, ipp = 0, pidx = 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;
}
/* loop until no header row read, protecting array bounds */
for (; ipp < MAXC && iss < MAXC; iss++) {
char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
int n = 0;
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
idx[iss] = n;
s1[iss] = strdup (tmp1); /* strdup - allocate & copy */
t1[iss] = strdup (tmp2);
if (!s1[iss] || !t1[iss]) { /* if either is NULL, handle error */
fprintf (stderr, "error: s1 or s1 empty/NULL, iss: %d\n", iss);
return 1;
}
/* read 'n' data lines from file, protecting array bounds */
for (int i = 0; i < n && ipp < MAXC; i++, ipp++) {
char ptmp1[MAXC] = "", ptmp2[MAXC] = "";
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s", ptmp1, ptmp2) != 2) {
fprintf (stderr, "error: read failure, ipp: %d\n", iss);
return 1;
}
production_left[ipp] = strdup (ptmp1);
production_right[ipp] = strdup (ptmp2);
if (!production_left[ipp] || !production_right[ipp]) {
fprintf (stderr, "error: production_left or "
"production_right empty/NULL, iss: %d\n", iss);
return 1;
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < iss; i++) {
printf ("%-8s %-8s %2d\n", s1[i], t1[i], idx[i]);
free (s1[i]); /* free s & t allocations */
free (t1[i]);
for (int j = pidx; j < pidx + idx[i]; j++) {
printf (" %-8s %-8s\n", production_left[j], production_right[j]);
free (production_left[j]); /* free production allocations */
free (production_right[j]);
}
pidx += idx[i]; /* increment previous index value */
}
return 0;
}
Example Input File
$ cat dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
Example Use/Output
$ ./bin/production <dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/production <dat/production.txt
==3946== Memcheck, a memory error detector
==3946== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3946== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3946== Command: ./bin/production
==3946==
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
==3946==
==3946== HEAP SUMMARY:
==3946== in use at exit: 0 bytes in 0 blocks
==3946== total heap usage: 18 allocs, 18 frees, 44 bytes allocated
==3946==
==3946== All heap blocks were freed -- no leaks are possible
==3946==
==3946== For counts of detected and suppressed errors, rerun with: -v
==3946== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
Related
Here is my code. It reads the first header line and ignores it but everything after the first header line should be entered in the struct. But when I try to print out the struct array it doesn't work.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct User {
char *name;
char *process;
int arrival;
int duration;
}User;
User userList[100];
int numOfJobs = 0;
char header[20];
int main(int argc, char **argv) {
scanf("%s %s %s %s", header, header, header, header);
while(EOF != scanf("%s %s %d %d", userList[numOfJobs].name, userList[numOfJobs].process, &userList[numOfJobs].arrival, &userList[numOfJobs].duration)) {
numOfJobs++;
}
for(int i = 0; i < numOfJobs; i++) {
printf("%s %s %d %d", userList[i].name, userList[i].process, userList[i].arrival, userList[i].duration);
}
return 0;
}
In struct User you declare two pointers, name and process, e.g.
typedef struct User {
char *name;
char *process;
int arrival;
int duration;
} User;
Before you can make use of either, they must point to valid storage. If you intent to copy character strings to them, then you must allocate blocks of storage sufficient to hold each string, assign the starting address of a block to each pointer so they point to valid memory, and then copy your string to the allocated block which you can then reference through the pointer.
The simplest way to make it all work is to first read name and process into aa temporary character array so that you can obtain the length of the string you need to store, then allocate storage (+1 for the nul-terminating character) and copy the string to the allocated block.
You cannot build any type of robust input routing around scanf(). It is full of pitfalls for the new C programmer. Instead, all users input should be done with fgets() to ensure you consume a complete line of user-input each time and then parrse the values you need from the buffer filled with fgets() using sscanf(). That way you can validate (1) the read of the input; and (2) parsing the information from the input separately. Most importantly, subsequent reads are not thwarted by a matching failure leaving characters unread in your input buffer.
From your description, you want to read/discard the first line and that is what lead to your use of scanf("%s %s %s %s", header, header, header, header);. Don't do that. Just read the first line into a buffer with fgets() and ignore it. That way if you have less than 4 words on the first line, your codes isn't stuck blocking for additional input.
Putting it altogether, you would do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXU 100
typedef struct User {
char *name;
char *process;
int arrival;
int duration;
} User;
int main (void) {
char buf[MAXC]; /* buffer to handle user-input */
User userList[MAXU]; /* don't declare global, pass as parameter */
int numOfJobs = 0;
if (!fgets (buf, MAXC, stdin)) /* read/discard first line */
return 1;
while (numOfJobs < MAXU && fgets (buf, MAXC, stdin)) { /* protect bounds, read lines */
char tmpname[MAXC], tmpproc[MAXC]; /* temporary storage for name, process */
/* split line with strings into tmpname, tmpproc */
if (sscanf (buf, "%s %s %d %d", tmpname, tmpproc,
&userList[numOfJobs].arrival,
&userList[numOfJobs].duration) == 4) {
size_t len = strlen (tmpname); /* get lenth of tmpname */
/* allocate / validate storage for name */
if (!(userList[numOfJobs].name = malloc (len + 1))) {
perror ("malloc-name");
break;
} /* copy tmpname to name */
memcpy (userList[numOfJobs].name, tmpname, len + 1);
len = strlen (tmpproc); /* get length of tmpproc */
/* allocate / validate storage for process */
if (!(userList[numOfJobs].process = malloc (len + 1))) {
perror ("malloc-process");
break;
} /* copy tmpproc to process */
memcpy (userList[numOfJobs].process, tmpproc, len + 1);
}
numOfJobs++; /* only increment on success */
}
for(int i = 0; i < numOfJobs; i++) {
printf ("%s %s %d %d\n", userList[i].name, userList[i].process,
userList[i].arrival, userList[i].duration);
free (userList[i].name); /* free all allocated memory */
free (userList[i].process);
}
}
(note: the use of numOfJobs < MAXU && fgets (buf, MAXC, stdin) as your read-loop condition where numOfJobs < MAXU protects your userList array bounds by preventing you from writing beyond the array if you input has more than MAXU (100) lines of information)
(note2: there is no need to declare global variables. Instead declare them in main() and pass as parameters to any function that needs them)
Since you haven't provided a sample input file, this code has not been tested, but based on your description it should work as intended. I have made up a bit of sample data to check the code and show you how to check the memory usage for any errors:
Example Input File
$ cat dat/alloc_struct_ptrs.txt
my dog has fleas -- bummer
name_1 process-1.8 2542 1542
name_2 process-2.9 2982 2982
name_3 process-3.0 1124 3124
name_4 process-4.1 1118 4118
name_5 process-5.2 4323 5323
name_6 process-6.3 2761 6761
name_7 process-7.4 6914 7914
name_8 process-8.5 2022 8022
name_9 process-9.6 9539 9539
Example Use/Output
$ ./bin/alloc_struct_ptrs < dat/alloc_struct_ptrs.txt
name_1 process-1.8 2542 1542
name_2 process-2.9 2982 2982
name_3 process-3.0 1124 3124
name_4 process-4.1 1118 4118
name_5 process-5.2 4323 5323
name_6 process-6.3 2761 6761
name_7 process-7.4 6914 7914
name_8 process-8.5 2022 8022
name_9 process-9.6 9539 9539
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/alloc_struct_ptrs < dat/alloc_struct_ptrs.txt
==18317== Memcheck, a memory error detector
==18317== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18317== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18317== Command: ./bin/alloc_struct_ptrs
==18317==
name_1 process-1.8 2542 1542
name_2 process-2.9 2982 2982
name_3 process-3.0 1124 3124
name_4 process-4.1 1118 4118
name_5 process-5.2 4323 5323
name_6 process-6.3 2761 6761
name_7 process-7.4 6914 7914
name_8 process-8.5 2022 8022
name_9 process-9.6 9539 9539
==18317==
==18317== HEAP SUMMARY:
==18317== in use at exit: 0 bytes in 0 blocks
==18317== total heap usage: 20 allocs, 20 frees, 5,291 bytes allocated
==18317==
==18317== All heap blocks were freed -- no leaks are possible
==18317==
==18317== For counts of detected and suppressed errors, rerun with: -v
==18317== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
Bascially, Im trying to accept a command line text file so that when I run the program as "program instructions.txt", it stores the values listed in instructions. However I am having trouble testing what i currently have, as it is giving me the error "segmentation fault core dumped".
int main(int argc, char* argv[]) {
setup_screen();
setup();
// File input
char textExtracted[250];
FILE* file_handle;
file_handle = fopen(argv[1], "r");
while(!feof(file_handle)){
fgets(textExtracted, 150, file_handle);
}
fclose(file_handle);
printf("%s", textExtracted[0]);
return 0;
}
Inside the text file is
A 1 2 3 4 5
B 0 0
C 1 1
Im just trying to store each line in an array and then print them.
Some points:
int main(int argc, char* argv[]) {
I suggest you check number of arguments here before proceeding further
setup_screen();
setup();
// File input
char textExtracted[250];
Declaration can be joined but always always check return values from I/O
FILE* file_handle = = fopen(argv[1], "r");
if (NULL == file_handle)
{
perror(argv[1]);
return EXIT_FAILURE;
}
below is not the correct way to read from a file, instead you should
try and read from the file first, then check for error/eof/enuff bytes read
// while(fgets(textExtracted,sizeof(textExtracted), 1, file_handle) > 0) {}
while(!feof(file_handle)){
fgets(textExtracted, 150, file_handle);
}
It looks like you think fgets appends to textExtracted when you call it
multiple times, it doesn't! every line in the file will overwrite the previously read line. Note also that the \n character is included in your buffer.
But since your file appears to be pretty small, you could read the whole
content into your buffer and work with that.
// int size = fread(textExtracted, sizeof(textExtracted), 1, file_handle);
Better is to check the size of the file first and then allocate a buffer with malloc to hold the whole file or read the file character by character and do whatever commands you need to do on the fly. e.g. a switch statement is excellent as a statemachine
switch( myreadchar )
{
case 'A':
break;
case 'B':
break;
...
}
textExtracted[0] is one character, textExtracted is the whole array so instead of
printf("%s", textExtracted[0]);
write
printf("%s", textExtracted);
or even better
fputs(textExtracted, stdout);
return 0;
The problem you present is the classic problem of "How do I read an unknown number of lines of unknown length from a file?" The way you approach the problem in a memory efficient manner is to declare a pointer-to-pointer to char and allocate a reasonable number of pointers to begin with and then allocate for each line assigning the starting address for the block holding each line to your pointers as you read each line and allocate for it.
An efficient way to do that is to read each line from a file into a fixed buffer (of size sufficient to hold your longest line without skimping) with fgets or by using POSIX getline which will allocate as needed to hold the line. You then remove the trailing '\n' from your temporary buffer and obtain the length of the line.
Then allocate a block of memory of length + 1 characters (the +1 for the nul-terminating character) and assign the address for your new block of memory to your next available pointer (keeping track of the number of pointers allocated and the number of pointers used)
When the number of pointers used equals the number allocated, you simply realloc additional pointers (generally by doubling the current number available, or by allocating for some fixed number of additional pointers) and you keep going. Repeat the process as many times as needed until you have read all of the lines in your input file.
There are a number of ways to implement it and arrange the differing tasks, but all basically boil down to the same thing. Start with a temporary buffer of reasonable size to handle your longest line (without skimping, just in case there is some variation in your input data -- a 1K buffer is cheap insurance, adjust as needed). Add your counters to keep track of the number of pointers allocated and then number used (your index). Open and validate the file given on the command line is open for reading (or read from stdin by default if no argument was given on the command line) For example you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", /* temp buffer to hold line read from file */
**lines = NULL; /* pointer-to-pointer to each line */
size_t ndx = 0, alloced = 0; /* current index, no. of ptrs allocated */
/* 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;
}
...
With your file open and validated, you are ready to read each line, controlling your read loop with your read function itself, and following the outline above to handle storage for each line, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line */
size_t len; /* for line length */
if (ndx == alloced) { /* check if realloc needed */
void *tmp = realloc (lines, /* alloc 2X current, or 2 1st time */
(alloced ? 2 * alloced : 2) * sizeof *lines);
if (!tmp) { /* validate EVERY allocation */
perror ("realloc-lines");
break; /* if allocation failed, data in lines still good */
}
lines = tmp; /* assign new block of mem to lines */
alloced = alloced ? 2 * alloced : 2; /* update ptrs alloced */
}
Note: above, the first thing that happens in your read loop is to check if you have pointers available, e.g. if (ndx == alloced), if your index (number used) is equal to the number allocated, you reallocate more. The ternary above alloced ? 2 * alloced : 2 simply asks if you have some previously allocated alloced ? then double the number 2 * alloced otherwise (:) just start with 2 pointers and go from there. In that doubling scheme, you allocate 2, 4, 8, 16, ... pointers with each successive reallocation.
Also note: when you call realloc you always use a temporary pointer, e.g. tmp = realloc (lines, ...) and you never realloc using the pointer itself, e.g. lines = realloc (lines, ...). When (not if) realloc fails, it returns NULL, and if you assign that to your original pointer -- you have just created a memory leak because the address for lines has been lost meaning you cannot reach or free() the memory you previously allocated.
Now you have confirmed you have a pointer available to assign the address of a block of memory to hold the line, remove the '\n' from buf and get the length of the line. You can do that conveniently in a single call to strcspn which returns the initial number of characters in the string not containing the delimiter "\n", e.g.
buf[(len = strcspn(buf, "\n"))] = 0; /* trim \n, get length */
(note: above you are simply overwriting the '\n' with the nul-terminating character 0, equivalent to '\0')
Now that you have the length of the line, you simply allocate length + 1 characters and copy from the temporary buffer buf to your new block of memory, e.g.
if (!(lines[ndx] = malloc (len + 1))) { /* allocate for lines[ndx] */
perror ("malloc-lines[ndx]"); /* validate combined above */
break;
}
memcpy (lines[ndx++], buf, len + 1); /* copy buf to lines[ndx] */
} /* increment ndx */
At that point you are done reading and storing all lines and can simply close the file if not reading from stdin. Here, for example, we just output each of the lines, and then free the storage for each line, finally freeing the memory for the allocated pointers as well, e.g.
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) { /* loop over each storage line */
printf ("lines[%2zu] : %s\n", i, lines[i]); /* output line */
free (lines[i]); /* free storage for strings */
}
free (lines); /* free pointers */
}
That's it. Putting it altogether, you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", /* temp buffer to hold line read from file */
**lines = NULL; /* pointer-to-pointer to each line */
size_t ndx = 0, alloced = 0; /* current index, no. of ptrs allocated */
/* 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 */
size_t len; /* for line length */
if (ndx == alloced) { /* check if realloc needed */
void *tmp = realloc (lines, /* alloc 2X current, or 2 1st time */
(alloced ? 2 * alloced : 2) * sizeof *lines);
if (!tmp) { /* validate EVERY allocation */
perror ("realloc-lines");
break; /* if allocation failed, data in lines still good */
}
lines = tmp; /* assign new block of mem to lines */
alloced = alloced ? 2 * alloced : 2; /* update ptrs alloced */
}
buf[(len = strcspn(buf, "\n"))] = 0; /* trim \n, get length */
if (!(lines[ndx] = malloc (len + 1))) { /* allocate for lines[ndx] */
perror ("malloc-lines[ndx]"); /* validate combined above */
break;
}
memcpy (lines[ndx++], buf, len + 1); /* copy buf to lines[ndx] */
} /* increment ndx */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) { /* loop over each storage line */
printf ("lines[%2zu] : %s\n", i, lines[i]); /* output line */
free (lines[i]); /* free storage for strings */
}
free (lines); /* free pointers */
}
Example Use/Output
$ ./bin/fgets_lines_dyn dat/cmdlinefile.txt
lines[ 0] : A 1 2 3 4 5
lines[ 1] : B 0 0
lines[ 2] : C 1 1
Redirecting from stdin instead of opening the file:
$ ./bin/fgets_lines_dyn < dat/cmdlinefile.txt
lines[ 0] : A 1 2 3 4 5
lines[ 1] : B 0 0
lines[ 2] : C 1 1
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/fgets_lines_dyn dat/cmdlinefile.txt
==6852== Memcheck, a memory error detector
==6852== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6852== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6852== Command: ./bin/fgets_lines_dyn dat/cmdlinefile.txt
==6852==
lines[ 0] : A 1 2 3 4 5
lines[ 1] : B 0 0
lines[ 2] : C 1 1
==6852==
==6852== HEAP SUMMARY:
==6852== in use at exit: 0 bytes in 0 blocks
==6852== total heap usage: 6 allocs, 6 frees, 624 bytes allocated
==6852==
==6852== All heap blocks were freed -- no leaks are possible
==6852==
==6852== For counts of detected and suppressed errors, rerun with: -v
==6852== 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.
While allocating storage for each pointer and each line may look daunting at first, it is a problem you will face over and over again, whether reading and storing lines from a file, integer or floating point values in some 2D representation of the data, etc... it is worth the time it takes to learn. Your alternative is to declare a fixed size 2D array and hope your line length never exceeds the declared width and the number of lines never exceeds your declared number of rows. (you should learn that as well, but the limitation become quickly apparent)
Look things over and let me know if you have further questions.
"fruits.txt" is a text file that starts with a number, say n, followed by the names of n fruits.I want to store those n names into an array of strings, but while trying to declare that array, I am getting "Segmentation fault(core dumped) error.
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
int count;
fp = fopen("fruits.txt", "r");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fscanf(fp, "%d", &count);
printf("%d\n", count);
char *fruits[count]; // This line is giving Segmentation fault.
fclose(fp);
return 0;
}
Depending on how you wish to provide storage (memory) to hold the fruit names as you read them, you have 2 options (2) use the little know 'm' field modifier (it was 'a' on older implementations so if you are on windoze, read the documentation to make a determination (or just try both and see which one works).
The immediate issue with your code was the fact that the fopen was called with the "w" file mode. That will only work if the fruits.txt already exhisted as the "w" mode will NOT create a file if it doesn't exist. The proper mode is "w+" (or "a" or "a+", either of which will also create a non-existing file). Simply changing your mode to "w+" will allow the fruit info to be written to a newly created fruits.txt.
The issue regarding use of a VLA (variable length array) on some MS compilers may present issues. You will just have to look at your version and the changelogs or documentation (or just try and read the error or warnings. Worse case, you can use a pointer-to-pointer-to-type, or a static array of sufficient size. Given your use of fscanf that approach is continued below. Specifically though:
char *fruits[count]; /* here you delare an array of pointers to type char */
/* utilizing a variable length array. some MS compiler */
/* version do not handle VLA's, VS13/VS15 should be ok */
for (i = 0; i < count; i++) /* read & allocate for each array element */
if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */
fprintf (stderr, "error: read/allocation for fruits[%d] failed.\n", i);
exit (EXIT_FAILURE);
}
(Note the format string used above " %ms" which has the caveat that the pointer to accept the allocation block must be a pointer-to-pointer to char * -- which is the address of the pointer to allocate/file, e.g. &fruits[i])
The remainder of the code should be very familiar, except now all input and other critical points in the code have been validated by checking the return provided by whatever function to insure no error condition existed up to that point and that none was caused by the operation in question. That is the only way you can have confidence in the operation of your code. Get in the habit.
Putting that together, you could come up with the following.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int count, i;
FILE *fp;
if (!(fp = fopen ("fruits.txt", "w+"))) { /* "w+" required to create file */
fprintf (stderr, "error: file open failed 'fruits.txt'.\n");
exit (EXIT_FAILURE);
}
fputs ("4 Apple Banana mango berry", fp); /* write string to fruits.txt */
fclose(fp);
if (!(fp = fopen ("fruits.txt", "r"))) { /* validate file open for reading */
fprintf (stderr, "error: file open failed 'fruits.txt'.\n");
exit (EXIT_FAILURE);
}
if (fscanf (fp, " %d", &count) != 1) { /* read all fruir from fruits.txt */
fprintf (stderr, "error: in read of value from 'fruits.txt'.\n");
exit (EXIT_FAILURE);
}
printf ("\n Quantity read from 'fruits.txt' is '%d'.\n\n", count);
char *fruits[count]; /* here you delare an array of pointers to type char */
/* utilizing a variable length array. some MS compiler */
/* version do not handle VLA's, VS13/VS15 should be ok */
for (i = 0; i < count; i++) /* read & allocate for each array element */
if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */
fprintf (stderr, "error: read/allocation for fruits[%d] failed.\n", i);
exit (EXIT_FAILURE);
}
fclose(fp); /* close file for the last time */
for (i = 0; i < count; i++) /* output array */
printf (" fruit[%d] : %s\n", i, fruits[i]);
for (i = 0; i < count; i++) /* free allocated memory */
free (fruits[i]);
return 0;
}
A basic compile string for the code (in abc.c) and the executable placed in bin/abc could would be:
$ gcc -Wall -Wextra -o bin/abc abc.c -std=gnu11
Example Use/Output
$ ./bin/abc
Quantity read from 'fruits.txt' is '4'.
fruit[0] : Apple
fruit[1] : Banana
fruit[2] : mango
fruit[3] : berry
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.
It is imperative that you 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 unintitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice.
$ valgrind ./bin/abc
==30980== Memcheck, a memory error detector
==30980== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==30980== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==30980== Command: ./bin/abc
==30980==
Quantity read from 'fruits.txt' is '4'.
fruit[0] : Apple
fruit[1] : Banana
fruit[2] : mango
fruit[3] : berry
==30980==
==30980== HEAP SUMMARY:
==30980== in use at exit: 0 bytes in 0 blocks
==30980== total heap usage: 10 allocs, 10 frees, 1,561 bytes allocated
==30980==
==30980== All heap blocks were freed -- no leaks are possible
==30980==
==30980== For counts of detected and suppressed errors, rerun with: -v
==30980== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts. (although note: some OS's do not provide adequate memeory exclusion files (the file that excludes system and OS memory from being reported as in use) which will cause valgrind to report that some memory has not yet been freed (despite the fact you have done your job and freed al blocks you allocoted and under your control.
That's a lot to take in for a simple problem, and we didn't even talk about the preferred manner of reading the file would have been to read and entire line at a time with fgets or POSIX getline and then to either parse the individual fruits from the line read, or to tokenize the line with strtok. Take the time to digest the cocde and answer for yourself the two compiler questions you must look up (1) support of VLA's for fruits[count] and (2) whether m or a is use by your compiler to allocate.
When using fscanf, you must always check the return value. Only if the function returns a successful value (read the fscanf documentation for that), the program should continue.
I've tried below code and found working.
The difference here is first i've created the file and filled the content in it.
If i don't fill contents in file then i gets segmentation fault (not on all compilers).
So it seems in your case the fscanf() is reading some junk and returning some big junk number.
As others also suggested, please check what fscanf returns. if it returns-1, means nothing is found in your file.
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
int count;
int i;
fp = fopen ("fruits.txt", "w");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fputs("4 Apple Banana mango berry", fp);
fclose(fp);*/
fp = fopen("fruits.txt", "r");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fscanf(fp, "%d", &count);
printf("%d\n", count);
fclose(fp);
char *fruits[count]; // This line is giving Segmentation fault.
return 0;
}
This prints 4 with no error/fault.
I am writing a program that takes a file of ordered pairs of numbers as it's input, and I want to split those ordered pairs and convert them to an integer for storage in an array.
The file could be like this:
0 1
1 4
9 11
12 45
I want to write a function that takes in the line, (assumed to be already null terminated in another part of the program), splits the numbers at the space and then stores them in a integer array of size two:
int *store = malloc(2 * sizeof(store));
I have looked into strtok but am I correct to say that it's not thread-safe and hence not the best option in this situation? Also it won't convert the numbers to integers which is something the function also needs to have.
How can I solve this problem?
If you want to read an unknown number of ordered pairs into an array of ordered pairs, there are a number of ways to approach this. As noted in the comment, if you can guarantee that you will always have 2-integers per line, the probably the simplest way to approach reading/converting the values to integers is with fscanf
(when you have the exact same format of data on each line is one of the only times I would recommended fscanf over fgets and sscanf, and then it is only to simplify the example)
When you have an unknown number of elements to read, the basic scheme is to allocate memory for some reasonably anticipated number pairs and then realloc as needed. Since you are reading ordered-pairs, you can simplify the process by allocating storage for an pointer-to-array-of-two-int. For example with your array of ordered-pairs op declared as follows:
int (*op)[2] = NULL;
you can allocate storage for MXLINE number of pairs with:
op = calloc (MXLINE, sizeof *op);
(you can use either malloc or calloc to allocate)
After you allocate initial memory to hold your ordered pairs, you can simply read each line with a format string containing two decimal conversion specifiers (e.g. "%d %d") and have fscanf perform the conversion. You must validate 2 conversions take place and then store the values in your array.
Keep a running count of the number of pairs you add so you can realloc when you reach your limit. An example is shown below. Don't forget to free all memory when it is no longer needed, and also run your code through a memory checker to validate your use of dynamically allocated memory.
Putting it all together in a short example using your datafile, you could do something like the following. Note: the program will read from the file given as the first argument on the command line (or from stdin by default if no filename is given):
#include <stdio.h>
#include <stdlib.h>
#define MXLINE 2
int main (int argc, char **argv) {
int i, idx = 0, mxline = MXLINE;
int (*op)[2] = NULL;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate initial memory */
if (!(op = calloc (MXLINE, sizeof *op))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
/* read each ordered pair into 'op' array */
while (fscanf (fp, "%d %d", &op[idx][0], &op[idx][1]) == 2) {
if (++idx == mxline) { /* realloc when limit reached */
void *tmp = realloc (op, sizeof *op * mxline * 2);
if (!tmp) { /* validate reallocation */
fprintf (stderr, "error: realloc failed.\n");
break;
}
op = tmp; /* assign reallocated pointer to op */
mxline *= 2; /* update line allocation limit */
}
}
for (i = 0; i < idx; i++) /* show ordered pairs */
printf ("pair[%3d] %3d : %3d\n", i, op[i][0], op[i][1]);
free (op); /* free allocated memory */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
return 0;
}
Example Use/Output
$ ./bin/array_ptr2array <dat/orderedpairs.txt
pair[ 0] 0 : 1
pair[ 1] 1 : 4
pair[ 2] 9 : 11
pair[ 3] 12 : 45
Memory/Error Check
Do not forget to validate your use of dynamically allocated memory and that your are freeing all memory when it is no longer needed:
$ valgrind ./bin/array_ptr2array <dat/orderedpairs.txt
==8107== Memcheck, a memory error detector
==8107== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==8107== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==8107== Command: ./bin/array_ptr2array
==8107==
pair[ 0] 0 : 1
pair[ 1] 1 : 4
pair[ 2] 9 : 11
pair[ 3] 12 : 45
==8107==
==8107== HEAP SUMMARY:
==8107== in use at exit: 0 bytes in 0 blocks
==8107== total heap usage: 3 allocs, 3 frees, 112 bytes allocated
==8107==
==8107== All heap blocks were freed -- no leaks are possible
==8107==
==8107== For counts of detected and suppressed errors, rerun with: -v
==8107== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
(note: the initial allocation was for MXLINE (2) pairs. The was intentionally done to force reallocation. In the real world you will want to minimize the number of times your must allocate memory. So if you think you could have 20 or so pairs, set the initial allocation for 24 or 32 pairs or whatever is your best reasonable guess of the number you will have (plus a couple for good-measure))
Look it over and let me know if you have any questions.
Maybe this is a solution:
gets(str, 80, fp); // get a line
sscanf(str, "%d %d", &store[0], &store[1]); // read numbers from that line
I'm trying to create a code, which reads from textile, and then stores the data into memory, prints out to the screen so the user can read it, but it is still saved into the memory so you can use it for the rest of the program..
Here is the sample of the textile
75
nevermind
nvm
not much
nm
no problem
np
people
ppl
talk to you later
ttyl
because
cuz
i don't know
idk
as soon as possible
asap
yeah
ya
how are you
hru
you
the list goes on, it has a total of 150 words, 151 lines if the first number is included. The 75 serves to tell you how many pairs there are.
anyways, here is the code that i Have written so far, it uses this struct
typedef struct
{
char *English;
char *TextSpeak;
}Pair;
The code i have written so far is:
FILE *infile =fopen("dictionary.txt","r");
int phraseCounter;
fscanf(infile, "%i", &phraseCounter); //Now you have the number of phrase pairs
//Allocate memory
Pair *phrases=malloc(sizeof(Pair) * phraseCounter);
//Run loop
for(int i=0; i < phraseCounter; i++){
//Get the english word
phrases[i].English = malloc(sizeof(char));
fscanf(infile,"%s",phrases[i].English);
//run loop to read next line
for(int a=0; a < phraseCounter; a++){
phrases[i].TextSpeak = malloc(sizeof(char));
fscanf(infile,"%s",phrases[i].TextSpeak);
}
printf("%s - %s\n", phrases[i].English, phrases[i].TextSpeak);
}
fclose(infile);
for(int i=0; i < phraseCounter; i++)
free(phrases[i].English);
free(phrases);
The output i keep getting is:
nevermind - atm
by - definitely
def -
-
-
-
-
-
And it keeps going for 75 lines.
Now I'm not sure whether I should use a 2D array or if this will be acceptable. Any help will be appreciated! Thanks
phrases[i].English = malloc(sizeof(char));
Here the problem lies, you are allocating a single byte and then trying to cram a string into it, which leads to undefined behavior here:
fscanf(infile,"%s", phrases[i].English);
Generally, you should assume a sensible buffer length and read that, and check whether the new line is contained. If that's not the case, read again into another buffer or enlarge the old one (using realloc).
Either that or use a non-standard function like getline that does this already for you under the hood.
Given that your lines are pretty short sentences, a constant buffer size could suffice (let's call it MAX_LINE), which provides us a little easier way to achieve the same:
fscanf(infile, "%*[^\n]s", MAX_LINE, buf);
This reads a string of length MAX_LINE into the buffer buf and terminates before the '\n' is encountered.
When reading strings, you should refrain from using fscanf("%s", buf) and use fgets() or scanf("%*s", MAX_LINE, ...) instead. This ensures that there will be no attempt to write more data than what you specified, avoiding buffer overflows.
Edit: The nested loop shouldn't be there. You are basically overwriting phrases[i].TextSpeak a total of phraseCounter times for no benefit. And in the process of that you are leaking a lot of memory.
There are a number of ways to do this. However, one suggestion I would have would be to make use of line-input tools such as fgets or getline to make reading the file more robust. It is fine to use fscanf for discrete variables (I left it for reading phraseCounter), but for reading string data of unknown length/content, line-input really should be used.
Below is an example of your code with this employed. The code is commented to explain the logic. The real logic here is the fact that you will read 2-lines for each allocated struct. A simple line counter using % (mod) as a toggle can help you keep track of when to allocate a new struct. I also added code to accept the filename as the first argument to the program. (to run, e.g ./progname <filename>). Let me know if you have questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 128
typedef struct Pair
{
char *English;
char *TextSpeak;
} Pair;
int main (int argc, char** argv) {
if (argc < 2) {
fprintf (stderr, "error: insufficient input. Usage: %s filename\n", argv[0]);
return 1;
}
Pair **pair = NULL; /* pointer to array of pointers */
char line[MAXL] = {0}; /* variable to hold line read */
FILE* infile = NULL; /* file pointer for infile */
unsigned int phraseCounter = 0; /* count of pairs */
unsigned int index = 0; /* index to pairs read */
size_t nchr = 0; /* length of line read */
size_t lnum = 0; /* line number read */
/* open file and validate */
if (!(infile = fopen ((argv[1]), "r"))) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read phraseCounter */
if (!fscanf (infile, "%u%*c", &phraseCounter)) {
fprintf (stderr, "error: failed to read phraseCounter.\n");
return 1;
}
/* allocate phraseCounter number of pointers to Pair */
if (!(pair = calloc (phraseCounter, sizeof *pair))) {
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
/* read each line in file */
while (fgets (line, MAXL - 1, infile) != NULL)
{
nchr = strlen (line); /* get the length of line */
if (nchr < 1) /* if blank or short line, skip */
continue;
if (line[nchr-1] == '\n') /* strip newline from end */
line[--nchr] = 0;
if (lnum % 2 == 0) /* even/odd test for pair index */
{
/* allocate space for pair[index] */
if (!(pair[index] = calloc (1, sizeof **pair))) {
fprintf (stderr, "error: memory allocation failed for pair[%u].\n", index);
return 1;
}
pair[index]-> English = strdup (line); /* allocate space/copy to English */
}
else
{
pair[index]-> TextSpeak = strdup (line);/* allocate space/copy to TextSpeak */
index++; /* only update index after TextSpeak read */
}
lnum++; /* increment line number */
}
if (infile) fclose (infile); /* close file pointer after read */
/* print the pairs */
printf ("\n Struct English TextSpeak\n\n");
for (nchr = 0; nchr < index; nchr++)
printf (" pair[%3zu] %-24s %s\n", nchr, pair[nchr]-> English, pair[nchr]-> TextSpeak);
/* free memory allocated to pair */
for (nchr = 0; nchr < index; nchr++)
{
if (pair[nchr]-> English) free (pair[nchr]-> English);
if (pair[nchr]-> TextSpeak) free (pair[nchr]-> TextSpeak);
if (pair[nchr]) free (pair[nchr]);
}
if (pair) free (pair);
return 0;
}
Input
$ cat dat/pairs.txt
10
nevermind
nvm
not much
nm
no problem
np
people
ppl
talk to you later
ttyl
because
cuz
i don't know
idk
as soon as possible
asap
yeah
ya
how are you
hru
Output
$ ./bin/struct_rd_pairs dat/pairs.txt
Struct English TextSpeak
pair[ 0] nevermind nvm
pair[ 1] not much nm
pair[ 2] no problem np
pair[ 3] people ppl
pair[ 4] talk to you later ttyl
pair[ 5] because cuz
pair[ 6] i don't know idk
pair[ 7] as soon as possible asap
pair[ 8] yeah ya
pair[ 9] how are you hru
Verify no memory leaks
$ valgrind ./bin/struct_rd_pairs dat/pairs.txt
==3562== Memcheck, a memory error detector
==3562== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3562== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3562== Command: ./bin/struct_rd_pairs dat/pairs.txt
==3562==
<snip>
==3562==
==3562== HEAP SUMMARY:
==3562== in use at exit: 0 bytes in 0 blocks
==3562== total heap usage: 32 allocs, 32 frees, 960 bytes allocated
==3562==
==3562== All heap blocks were freed -- no leaks are possible
==3562==
==3562== For counts of detected and suppressed errors, rerun with: -v
==3562== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)