Reputation: 163
I already know how read word by word from a file (using fgets then strok each other), however itd like to find the simplest way and from what Ive seen fscanf, should work.
If fscanf will allocate the pointer of a word inside array[i], why is it not storing anything.
Natural Reader is
john make tame
michael george meier
Bonus Second pass
Im expecting
word = Natural
word = reader
word = is
word = john
...
word = pass
int main(int argc, char *argv[]) {
FILE *file = fopen(argv[1], "r");
int ch;
int count = 0;
while ((ch = fgetc(file)) != EOF){
if (ch == '\n' || ch == ' ')
count++;
}
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fseek(file, 0, SEEK_SET);
char** words = calloc(count, size * sizeof(char*) +1 );
int i = 0;
int x = 0;
char ligne [80];
while(fscanf(file, "%s", words[i]) != EOF ){ //or != 1
printf("%s\n", words[i]);
i++;
}
free(words);
fclose(file);
return 0;
}
Upvotes: 0
Views: 173
Reputation: 23822
char** words = calloc(count, size * sizeof(char*) +1 );
is not what you'd want, size
is the number of total bytes in the file, by using sizeof(char*)
you are multiplying the size you need by the size of a pointer, which will likely give you 8 + 1 times more space than you need, take a good look at calloc
manual, the first parameter is the number of items, the second is the size of each item.
It will also not give you a 2D array, for that you would need to get the size of each word and allocate each line with the needed space, this would be an obvious overkill just to print the words.
If you want to to read a file to the end word by word and only print the words, you don't need all that, you can use only fscanf
:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
if(argc > 1){
FILE* file = fopen(argv[1], "r");
if(file == NULL){
return EXIT_FAILURE;
}
char word[100];
while(fscanf(file, "%99s", word) > 0){
printf("%s\n", word);
}
fclose(file);
}
}
Note that you should validate fopen
return and also check the number command line arguments, if you are to use malloc
, also check its return value.
Notice that I added a width specifier in fscanf
, this avoids potential buffer overflow.
Upvotes: 2
Reputation: 197
Short answer: because calloc
does not support allocating memory for 2D arrays and the words
pointer ends up not working as expected.
Long answer: the call
char** words = calloc(count, size * sizeof(char*) +1 );
allocates some memory, fills it with zeros and stores its address at words
. The value of words[i]
is then defined as "whatever sits in the memory at the position words+i*sizeof(char*)
". In your case that is zeros, which become NULL
when they are interpreted as a pointer. fscanf
requires some valid memory to store whatever it fetches from the file, so when it gets words[i]
it detects that it's a NULL
and refuses to write anything there. Then you read from there with printf
and get a segfault.
For your code to work as intended, you need to dynamically allocate a 2D array of char
s. That can be done either by allocating a 1D array of pointers to char
and populating it with pointers to 1D arrays of char
s (see methods 2 and 4 here), or by using variable length arrays (Jens Gustedt's answer here).
Or, you could try to avoid using a dynamic 2D array in C. That's what I do unless I absolutely have to. For example, you could replace char words[X][Y]
with a 1D array char words[X*Y]
, then obtain i
th string of the array as words+i*Y
.
Upvotes: 1