fscanf usage in c - Values not saving properly - c

I have a small sample program to illustrate my issue below: I have a simple text file with three words (each in a new line) which fscanf reads, assigns to a temporary variable and then transfers to a string array. Yet, the values do not seem to transfer to the array. Also, when I remove the comment // from the second printf in the while loop I get a seg fault.
I'm fairly new to C, so only now learning the usage of these functions! Thanks in advance for assistance!
#include <stdio.h>
#include <string.h>
int main (int argc, char* argv[])
{
char* words[15];
char tmp[45];
int i = 0;
FILE* fp = fopen("small", "r");
while (fscanf(fp, "%s", tmp) == 1)
{
printf("%s\n", tmp);
words[i] = tmp;
i++;
//printf("%s ", words[i]);
}
printf("\n");
printf("Words 0 = %s\n", words[0]);
printf("Words 2 = %s\n", words[1]);
printf("Words 3 = %s\n", words[2]);
fclose(fp);
}
Output
pears
apples
zipper
Words 0 = zipper
Words 2 = zipper
Words 3 = zipper

In your code, words[i] = tmp; is not the way to store each input to the words array. That only stores the base address of the tmp array into each words[i] and later, while printing, it actually prints the latest content of the tmp on every iteration.
If you want to get the contents of the tmp array into each words[i], you need to either
Allocate memory to each words[i] and use strcpy()
Use strdup() and assign that to words[i].
In either of cases, you have to free() the allocated memories before exit.

I had the same problem in the past.
The problem is that when you read from the file, the word is kept in the buffer, and then you store it to the variable temp.
The thing is that when you read the next word, the contents of the buffer change. And this affects the previous call too!
So you read "pears", you print "pears" and words[0] = "pears"
Then you read "apples", you print apples and words[1] = "apples". BUT ALSO words[0] = "apples" now!!
And so on...
What you need to do is before reading the file, to allocate memory with malloc for every words[i] and get it equal to "".
e.g. words[0] = "" etc.
Then when you start reading the file, you should use the strcpy() function for temp and words[i]. This will solve your problem.
I tried to answer this as simply as i could because in the past that issue troubled me and confused me a lot.

The first important problem with your code is this line
char* words[15];
It gives you an array of 15 char pointers (char*). That is not the same as an array of 15 strings. There are no memory for storing the strings.
To get memory for storing the strings, you can do:
char words[15][45];
// ^ ^^
// no * memory for each of the 15 strings
Now you have memory for 15 strings. Each string can be up to 44 chars.
With this change you don't need the tmpvariable - just read directly into words. Something like:
#include <stdio.h>
#include <string.h>
int main (int argc, char* argv[])
{
char words[15][45];
int i = 0;
FILE* fp = fopen("small", "r");
if (!fp)
{
printf("no such file\n");
return 0;
}
while ((i < 15) && (fscanf(fp, "%44s", words[i]) == 1))
{ // ^^^^^^^^ is the same as &words[i][0]
i++;
}
printf("\n");
int t;
for (t = 0; t < i; ++t)
{
printf("Words %d = %s\n", t, words[t]);
}
fclose(fp);
return 0;
}
Some other important changes added:
1) After fopen you must check for NULL
2) For scanf with %s always give a max size (i.e. %44s) so there can't be buffer overflow
3) Make sure to stop the while when you have read 15 strings (to prevent buffer overflow)
4) Only print as many strings as you have read in
Finally I added return 0 to the end of main

Related

Troubles with pointers when reading from a txt file

Im trying to print out the strings from a txt file in order.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// Check for command line args
if (argc != 2)
{
printf("Usage: ./read infile\n");
return 1;
}
// Create buffer to read into
char buffer[7];
// Create array to store plate numbers
char *plates[8];
FILE *infile = fopen(argv[1], "r");
int idx = 0;
while (fread(buffer, 1, 7, infile) == 7)
{
char buffer2[7];
// Replace '\n' with '\0'
buffer[6] = '\0';
strcpy(buffer2, buffer);
// Save plate number in array
plates[idx] = buffer2;
idx++;
}
for (int i = 0; i < 8; i++)
{
printf("%s\n", plates[i]);
}
}
The pasted code just writes one and the same string over and over again, and I cant for the life of me figure out what Im doing wrong. When I debug the "while" method, I see that the buffer updates keep overwriting every entry to the plates array.
In this for loop
while (fread(buffer, 1, 7, infile) == 7)
{
char buffer2[7];
// Replace '\n' with '\0'
buffer[6] = '\0';
strcpy(buffer2, buffer);
// Save plate number in array
plates[idx] = buffer2;
idx++;
}
you declared a local array with automatic storage duration
char buffer2[7];
that will not be alive after exiting the loop. And all elements of the array plates are set by the address of the first element of the array buffer2. That is within the for loop they all point to the same extent of memory.
After exiting the loop the pointers will be invalid.
You need to allocate character arrays dynamically and their addresses to assign to the elements of the array plates.
Also pay attention to that the function fread does not read a string. So this statement
buffer[6] = '\0';
overwrites the last character stored in the array.
Using dynamic allocation should fix your problem. You could try something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// Check for command line args
if (argc != 2)
{
printf("Usage: ./read infile\n");
return 1;
}
// Create buffer to read into
char buffer[7];
// Create array to store plate numbers
char *plates[8];
FILE *infile = fopen(argv[1], "r");
int idx = 0;
while (fread(buffer, 1, 7, infile) == 7)
{
// Replace '\n' with '\0'
buffer[6] = '\0';
// Save plate number in array
plates[idx] = malloc(sizeof(buffer));
strcpy(plates[idx++], buffer);
}
for (int i = 0; i < 8; i++)
{
printf("%s\n", plates[i]);
free(plates[i];
}
}
The pasted code just writes one and the same string over and over again, and I cant for the life of me figure out what Im doing wrong. When I debug the "while" method, I see that the buffer updates keep overwriting every entry to the plates array.
#Vlad from Moscow gave you an explanation for this:
that will not be alive after exiting the loop. And all elements of the array plates are set by the address of the first element of the array buffer2. That is within the for loop they all point to the same extent of memory.
"Im trying to print out the strings from a txt file in order."
As noted in comments fread() as used in your implementation is not the best way to read lines in a text file.
Answering these 2 questions (at minimum the first one) will provide important values to help in declaring and initializing the right sized (and shaped) buffers for reading lines from a file...
What is the longest line in the file?
How many lines are in the file? (may be optional if not storing all lines)
The following example(s) can be accomplished knowing only the answer to the first question, but knowing the answer to the second would be useful if it was necessary for example to store all of the lines into an array of strings. (This is out of scope here as you did not list that as a requirement for your code)
Unless you are comfortable with making an assumption on the maximum line length, i.e. hard-coded...
char line[guessed_max_line_length] = {0};
...a run-time assessment to determine the length of the longest line in the file is necessary to size the buffer such that it can safely contain lines that will later be read from file. Once this assessment is done, use the length of the longest line to create a line buffer during run-time. (dynamically allocate memory):
char *line = malloc(max_length + 1);
memset(line, 0, max_length + 1);
Using these methods, (and providing the implementation linked above) your code can be simplified to the following adaptation....
//prototype to get max line length in file
size_t longestLine(FILE *fi);
int main(int argc, char *argv[])
{
// Check for command line args
if (argc != 2)
{
printf("Usage: ./read infile\n");
return 1;
}
FILE *infile = fopen(argv[1], "r");
if(infile)
{
size_t max_length = longestLine(infile); //see linked implemenation from above
rewind(infile);//suggest adding this line to longestLine() implementation.
char *line = malloc(max_length + 1);
if(line)
{
memset(line, 0, max_length + 1);
while(fgets(line, max_length, infile))
{
fputs(line, stdout);
//or alternatively
//printf("%s", line);
}
free(line);
}
fclose(infile);
}
return 0;
}

C fscanf to read each word from a file not working

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;
}
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.
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 chars. That can be done either by allocating a 1D array of pointers to char and populating it with pointers to 1D arrays of chars (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 ith string of the array as words+i*Y.

C / Getting line string from text file and storing them in array/pointer

I want to get all lines from the text file and store them in my char** pointer (array of strings). The problem is that when I try to set indices for pointer's strings, the program assigns the last scanned sentence for all indices.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 10000
int main()
{
FILE *fp = NULL;
char line[MAX_LINE];
char** lines = (char**) malloc(10000*200*sizeof(char));
int count = 0;
fp = fopen("test.txt","r");
while(fgets(line,10000,fp)) {
lines[count] = line;
count++;
}
fclose(fp);
for(int i =0; i<2000;i++){
printf("%s",lines[i]);
}
return 0;
}
lets assume test.txt is like this:
Alice was beginning to get very tired of sitting by her sister on the
bank, and of having nothing to do: once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
it, and what is the use of a book, thought Alice without pictures or
conversations?
When I print like this, every time I get the last sentence (in this case conversations? ) in my text file. However, I want to set every scanned sentence from the text file to the different index in my char**. For example, I want to set like this:
lines[0] gives "Alice was beginning to get very tired of sitting by her sister on the"
lines[1] gives "bank, and of having nothing to do: once or twice she had peeped into the"
and so on.
You can't copy characters from one string buffer to another simply by assigning a pointer (all that does is to make the destination point to the source, as you have noticed).
Instead, you must actually copy the characters, using the strcpy function. So, instead of:
lines[count] = line; // Just makes each pointer point to the same buffer
use:
strcpy(lines[count], line); // Copies the CURRENT contents of "line"
You also have a severe problem in the way you are using your char** lines buffer. If you want an array of 200 lines, each with a maximum length of 10000 characters, you should allocate them as follows:
char** lines = malloc(200 * sizeof(char*)); // Make 200 pointers
// Now allocate 10000 chars to each of these pointers:
for (int i = 0; i < 200; ++i) lines[i] = malloc(10000 * sizeof(char));
Note: The 200 buffers will be uninitialized (contain random data) so, in your print loop, you should only use those you have copied real data to, using the count variable as the loop limit:
for(int i = 0; i < count; i++) {
printf("%s", lines[i]);
}
Also, don't forget to free the memory allocated when you're done:
for (int i = 0; i < 200; ++i) free(lines[i]); // Free each line buffer...
free(lines); // ... then free the array of pointers itself
strdup resolve the issue, free resources as said by Adrian when finished.
int main()
{
FILE *fp = NULL;
char line[MAX_LINE];
char** lines = (char**) malloc(10000*200*sizeof(char));
int count = 0;
fp = fopen("test.txt","r");
while(fgets(line,10000,fp)) {
lines[count] = strdup(line);
count++;
}
fclose(fp);
for(int i =0; i<count;i++){
printf("%s",lines[i]);
}
for (int i = 0; i < count; ++i) free(lines[i]);
free(lines);
return 0;
}
If you are looking for better performance look at my repo (https://github.com/PatrizioColomba/strvect)

C read file content into an array of strings

I need to load the contents of a file into two string arrays. I tried the following and it is not working.
file.txt contains 10 records and each record has two string values separated by whitespace.
CODE:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char line[12][20];
FILE *fptr = NULL;
int i = 0;
int tot = 0;
fptr = fopen("file.txt", "r");
char arr[20][20];
while (fgets(line, sizeof(line), fptr)) {
strcpy(arr[i],line);
i++;
}
tot=i;
for (int i=0; i<tot; i++) {
printf("first value %s",arr[i][0]);
printf("second value is %s",arr[i][1]);
printf("\n");
}
return 0;
}
If I understand correctly, you're trying to store data in a structure like:
{{"line1A", "line1B"}, {"line2A", "line2B"}, {"line3A", "line3B"}}
It looks like you need an array where each element consists of two arrays (strings), one for the first value and one for the second value on each line. If this is the case, you need a three dimensional array of chars.
In the example below I've declared arrayOfLines as array with 12 elements each of which has 2 arrays of chars (for your two values per line), with space for 20 chars in each string (NULL terminated char array)
There are some other problems with your code:
The first parameter for fgets() should be a char * - a pointer to a string buffer. Your code passes in a multi-dimensional array of chars.
Your while loop should continue until fgets returns NULL
You need to split each line into multiple strings
Check for buffer overruns when copying strings with strcpy()
In the example code I used strtok() delimited by a " " space character - you may need to play around with this - strtok can accept an array of chars to be used as a delimiter. In the example, I split the first string using the first space char, and the second string is delimited by the end of line.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
// Array for 12 lines, each with 2 strings, each string max 20 chars
// Adjust values as required.
char arrayOfLines[12][2][20];
FILE *fptr = NULL;
int i = 0;
int tot = 0;
fptr = fopen("file.txt", "r");
// char arr[20][20]; not needed
char line[20];
while(fgets(line, sizeof(line) / sizeof(line[0]), fptr) != NULL)
{
// Rudimentary error checking - if the string has no newline
// there wasn't enough space in line
if (strchr(line, '\n') == NULL) {
printf("Line too long...");
return EXIT_FAILURE;
}
// Split string into tokens
// NB: Check for buffer overruns when copying strings
char *ptr1 = strtok(line, " ");
strcpy(arrayOfLines[i][0], ptr1);
char *ptr2 = strtok(NULL, "\n");
strcpy(arrayOfLines[i][1], ptr2);
i++;
}
tot=i; // Unecessary - just use a different variable in your loop and use i as the upper bound
for (int i=0;i<tot;i++)
{
printf("first value %s\n", arrayOfLines[i][0]);
printf("second value is %s\n", arrayOfLines[i][1]);
printf("\n");
}
return 0;
}
printf("first value %s",arr[i][0]);
printf("second value is %s",arr[i][1]);
Basicly all you are doing is printing 2 chars from i word when you want to print full string you should do it like this: printf("%s",arr[i]); You said that value is separated by whitespace so when you are getting line from file you will save it to arr[i] (if first line in file contains "Hello World", your arr[0] will contain "Hello World") when you want to split it into 2 printf you need to print them char by char until space.
Edit: I reminded myself about function sscanf you can use it to get data from file array like you whould do it with keyboard input
You can use this to do that
Code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void){
char line[12][20];
char arr[20][20];
FILE *fptr=NULL;
int i=0;
fptr = fopen("file.txt", "r");
if(!fptr){
printf("cant open file\n");
exit(1);
}
while(fgets(*line, sizeof(line), fptr)){
strncpy(arr[i],*line, sizeof(*line));
i++;
}
for (int j=0;j<i;j++){
printf("%s\n", arr[j]);
}
return 0;
}
Notes and changes I made on your code:
Check fptr as return value of open() if it's NULL decide what to do.
Remove unnecessary tot variable and use another index j in last for loop.
Use strncpy() as a better version of strcpy()
Correct way of print arr, printf("%s\n", arr[j]);
\n can be embed on first printf()

C/ String data from file to array. Segmentation Fault

I am writing a program that streams words from a text file and puts them into an array. I've also added a display of array data to see if everything is ok but I get a segmentation fault after compiling.
I am new to system programming so I am aware I might have done some basic mistakes. Please find the code below and tell me where have I gone wrong.
#include <stdio.h>
#define BUFFER 100
int main() {
char tab[BUFFER];
FILE *wp;
wp = fopen("tab_file_b_words.txt", "r");
if(wp == NULL)
{
printf("error/n");
return -1;
}
int i;
for(i=0; i<sizeof(wp); i++) {
if(wp != NULL)
fscanf(wp, "%s", &tab[i]);
else
break;
}
printf("Content: \n");
for(i = 0; i < BUFFER; i++) {
if(tab[i] != NULL)
printf("%s\n", tab[i]);
else
break;
}
fclose(wp);
return 0;
}
As of now you are having array of char
what you need is
char tab[BUFFER]; --> char tab[BUFFER][SOME_LENGTH];
And
fscanf(wp, "%s", &tab[i]); --> fscanf(wp, "%s", tab[i]);
%s expects null terminated char * but you are passing char.
printf("%s\n", tab[i]);
sizeof(wp) is size of pointer.
for(i=0; i<sizeof(wp); i++)
What you need is
while (1 == fscanf(wp, "%s", tab[i]))
{
...
}
This section:
for(i=0; i<sizeof(wp); i++) {
if(wp != NULL)
fscanf(wp, "%s", &tab[i]);
else
break;
}
Is problematic.
First, there are a couple of issues with the line: fscanf(wp, "%s", &tab[i]); It should be written as:
fscanf(wp, "%s", tab); //removed & and array notation.
Unlike int or float variable types, the name of your char array (i.e. tab) is already a pointer pointing to the address of the variable, making it unnecessary (and incorrect) to use the address of operator (&).
Related to above... (and likely cause of segmentation fault.)
Because the definition of tab is for a simple array of char ( char tab[BUFFER]; ), the notation tab[i] refers only to the ith byte (or char) of the array, not the entire array. Because of the "%s" format specifier used, the function fscanf() expects a char * not a char, making tab the correct argument to use.
If you want to use an array of lines the variable must be created as a 2D array of char:
#define NUM_LINES 100
#define LINE_LEN 80
int main() {
char tab[NUM_LINES][LINE_LEN] = {{0}}; // zero initialized array of
// NUM_LINE strings
// each of LINE_LEN-1 capacity
In the statement for(i=0; i<sizeof(wp); i++) {
sizeof(wp)
will be equal to the number of bytes of the pointer wp, either 32 or 64 depending on the target addressing of your application. This is probably not what you intended. (or want.)
Consider a different approach:
Given you are working with text files, try using a combination of while() and fgets() to read lines from the file. Then you can process each line based on its known syntax.
(Following example uses a single dimension char array for simplified illustration.)
char line[some_len];
wp = fopen(some_file_name, "r");
if(wp)
{
while(fgets(line, some_len, wp))
{
// use line. In this case, just print to stdout
printf("%s\n", line);
}
fclose(wp);
}
sizeof(wp) is the problem.
sizeof returns the length of a type in bytes and not the length of the file.
If you want to get the size of a file this may help you.

Resources