Having trouble with file I/O and string arrays - c

The file I'm reading from just has names separated by a line. What happens is the program tries to print the contents of line_array, and it will print out about 20 of the last line in the txt file.
#include <stdio.h>
FILE* fp;
int main(){
char* line;
const char* line_array[255];
int i= 0;
int b =0;
fp = fopen("noob.txt","r");
while(fgets(line,255,fp)){
line_array[i]=line;
printf("%s",line);
printf("%s",line_array[i]);
i++;
}
for(;b<i;b++){
printf("%s",line_array[b]);
}
fclose(fp);
return 0;
}

The first issue, in your code,
while(fgets(line,255,fp))
line is used uninitialized. There is no memory allocated to line. It invokes undefined behavior.
Then, you did not check for the success of fopen() before using the returned file pointer. Again, possible UB.
And finally, by saying
line_array[i]=line;
what you did is to store the line itself to all the occurrences of line_array[n], so for the later printf() loop, the latest content of line is being printed over and over again.
Solution(s):
Allocate memory to line or use a fixed length-array.
Check for the success of fopen()before using the returned pointer.
Allocate memory to each line_array[n] and use strcpy() to copy the content. Ottherwise, you can directly use strdup(), too.

Related

Segmentation Fault after reading a file into a struct array

I am trying to write a program that reads data from a file and puts it into a struct array. I have succeeded in putting it into the array but I then get a segmentation fault. I believe I have to use malloc to initialize the struct array but I am new to programming and don't really understand how to do this. Thanks for your help! I have copied pieces of my code so you can see what I've done. I have not included my functions in this code.
struct Weather
{
char location;
int daynum;
double temp;
double precip;
};
int main (void)
{
FILE*openFile;
char buffer[COLS][ROWS];
int i = 0;
struct Weather loc1; //initialize here?
for (i = 0; i <200; i++)
{
fgets (buffer[i], ROWS, openFile);
parseLine(buffer[i], &loc1);
printf ("%d %c %d %.2lf %.2lf\n",i, loc1.location, loc1.daynum, loc1.temp, loc1.precip);
}
}
Your file stream (openFile) is not initialized; it does not actually point to a file. As yano said in his comment, use fopen() in order to properly initialize the file pointer.
You must initialize the file stream with fopen() before any I/O operations!
int main()
{
char filename[64];
FILE *fp;
snprintf(filename, sizeof(filename), "hello1234.txt");
if(NULL == (fp = fopen(filename, "r")))
{
printf("err, failed when fopen(), %s %s[%d].\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
//your code here
return 0;
}
Initialize the struct
Note that malloc() cannot initialize the struct.
two methods:
M0:
struct Weather loc1;
memset(&loc1, 0, sizeof(struct Weather));
M1:
struct Weather loc1 = {0};
man malloc or click the link for a malloc manual.
Multiple problems in your code:
The stream pointer openFile is uninitialized, calling fgets() for it invokes undefined behavior. You want to open a file for fopen() or set the value of openFile to the standard input stream stdin.
The 2D char array should be defined in the other order:
char buffer[ROWS][COLS];
you should use the same constant for the loop counter and the 2D array definition: ROWS might be defined to something less than 200.
the size of the line buffer is COLS, pass that to fgets().
you should test the return value of fgets(): it returns NULL at end of file and the contents of the destination array is indeterminate in this case.
whether or not to initialize loc1 depends on what the parseLine() function does. It would make sense that parseLine() make no assumptions about the contents of the destination structure, but the source has not been posted, so we cannot know for sure.
the printf format specifier for type double is %f, the extra l is simply ignored.

How read lines of file and get them to array in C?

I would like to create a function to read file line by line. One every line is one name.
int readConfig(char ** path, FILES ** files )
{
FILE* f;
f = fopen("file", "r");
int ch;
while ((ch=fgetc(f)) != EOF )
{
}
return 0;
}
How to use the fgetc function to parse the file? And how to get the results to the files[count].name?
Right off the bat:
char configFile [11] = "kernels.cfg";
[11] is too small. Try:
char configFile [12] = "kernels.cfg";
or
char configFile [] = "kernels.cfg"; /* let the compiler do the counting */
Also char is too small for ch -- use:
int ch;
You may also find fgets() -- which reads a whole line at at time -- simpler to use than fgetc().
You are getting SIGSEGV because of modifying string literal and that causes an undefined behavior (e.g. your SIGSEGV). I am not sure what should be stored in filename and name variables. If by line:
strcpy(files[count].filename,'.bin');
you've meant to add a '.bin' to filename variable, then this approach is wrong. You should use strcat. strcpy would write to filename from beginning of this variable, so some chars previously saved there would be overwritten. strcpy also adds a null termination char, so if you wanted to print it out, printf would stop on that \0 char and won't go further. However, the real problem is that you should allocate with malloc some space for your variables in struct. Then you will be able to modify them.
Consider simple example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct file {
char* name;
char* filename;
};
int main(void)
{
struct file x;
x.name = malloc(30);
x.filename = malloc(40);
strncpy(x.name, "copied_string", 13);
printf("%s\n", x.name);
strcat(x.name, "_suffix");
printf("%s\n", x.name);
strcpy(x.name, "erased");
printf("%s\n", x.name);
free(x.name);
free(x.filename);
return 0;
}
output:
copied_string
copied_string_suffix
erased
This should make it a little more clear what's the origin of your crash. You should also consider using fgets or getline. Remember to free what you've malloc'd.
EDIT:
Calling readConfig(&path, &files); results in passing to readConfig a pointer of type FILES (*)[256]. Consider changing FILES files[256]; to
FILES* files = malloc(sizeof(FILES)*256);
and later call your function like readConfig(&path, files);
Then you would pass to readConfig function a compatible type of files.

array is full of the last element in a file

I have a procedure readTokens() which takes a file and an array and tokenizes the file and puts the things into the array but when I access the array, it is full of the last element. why? here is my readTokens() method:
void
readTokens(char *fileName, char** a[])
{
FILE *fp;
char *token;
int count = 0;
fp = fopen(fileName, "r");
if (fp == 0)
{
fprintf(stderr,"file %s could not be opened for reading\n", fileName);
exit(1);
}
token = readLine(fp);
while(!feof(fp))
{
a[count] = token;
++count;
free(token);
token = readLine(fp);
}
fclose(fp);
}
You should not call free(token), since you still have a pointer to it in a[count]. Since you're freeing it, that memory can be reused, and apparently it's being used when readLine() reads the next line. So each time you read a line, it reuses the same memory and overwrites it with the next line. As a result, all elements of a[] contain pointers to the same line.
a[count] = token doesn't make a copy of the token, it simply assigns the pointer.
This all assumes that readLine() uses malloc() to allocate the memory for the line it reads. If it doesn't, then it's even worse, since you must not call free() on memory that wasn't allocated with malloc() or calloc().
It's possible that readLine() is just using a static array variable, which would also explain why all your lines are the same. If you don't want it to keep overwriting the token, you need to make a copy of it in readTokens(). It would help if you posted the definition of readLine().

How do I read lines from a file in C?

I need to read in a file. The first line of the file is the number of lines in the file and it returns an array of strings, with the last element being a NULL indicating the end of the array.
char **read_file(char *fname)
{
char **dict;
printf("Reading %s\n", fname);
FILE *d = fopen(fname, "r");
if (! d) return NULL;
// Get the number of lines in the file
//the first line in the file is the number of lines, so I have to get 0th element
char *size;
fscanf(d, "%s[^\n]", size);
int filesize = atoi(size);
// Allocate memory for the array of character pointers
dict = NULL; // Change this
// Read in the rest of the file, allocting memory for each string
// as we go.
// NULL termination. Last entry in the array should be NULL.
printf("Done\n");
return dict;
}
I put some comments because I know that's what I'm to do, but I can't seem to figure out how to put it in actual code.
To solve this problem you need to do one of two things.
Read the file as characters then convert to integers.
Read the file directly as integers.
For the first, you would use freed into a char array and then use atoi to convert to integer.
For the second, you would use fscanf and use the %d specify to read directly into an int variable;
fscanf does not allocate memory for you. Passing it a random pointer as you have will only cause trouble. (I recommend avoid fscanf).
The question code has a flaw:
char *size;
fscanf(d, "%s[^\n]", size);
Although the above may compile, it will not function as expected at runtime. The problem is that fscanf() needs the memory address of where to write the parsed value. While size is a pointer that can store a memory address, it is uninitialized, and points to no specific memory in the process' memory map.
The following may be a better replacement:
fscanf(d, " %d%*c", &filesize);
See my version of the spoiler code here

Why is this program not showing the first line again and again?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *readLine(FILE *inFile) //Simply reads line in a text file till "\n"
{
char *line = realloc(NULL, 1);
char c;
int i=0;
while (!feof(inFile))
{
c = fgetc(inFile);
if (ferror(inFile)) printf("Error reading");
if (c == 10)
{
realloc(line,i+1);
line[i]= 10;
break;
}
realloc(line, i+1);
line[i++] = c;
}
return line;
}
int main(int argc,char **argv)
{
FILE *inFile;
inFile = fopen("testFile","r");
printf("%s",readLine(inFile));
printf("%s",readLine(inFile));
printf("%s",readLine(inFile));
return 0;
}
If the contents of testFile is:-
abc
def
ghi
The three printf statements should show "abc" three times.. But the output is:-
abc
def
ghi
I know I am wrong in the concept somewhere. Pls help.
Usage of realloc() is incorrect.
realloc(line,i+1); // wrong
// OK
void *new_line = realloc(line,i+1);
if (!new_line)
{
free(line);
return NULL;
}
line = new_line;
Because line is passed by value, it's not changed. The actual re-allocated memory is in the return value. Therefore line remains the same over and over again, and you are seeing the same line over and over again. Edit: just realized that's even though it's a bug, it's not what would cause repeating lines. Other points are still valid.
What's worse:
You have a memory leak by losing the newly re-allocated pointer every time.
You are potentially accessing freed memory, because old line value may become invalid after reallocation, if it was reallocated in a different part of the heap.
You are re-allocating memory every character, which is potentially an expensive operation.
But I am passing file pointer by value. So i should get output "abc" again and again
Ah, I understand your confusion.
A file pointer only points to the actual file structure. State such as the current offset are not part of the pointer but are part of the internal structure.
Another way to think about this is that the actual object representing the file is FILE. To get pass-by-reference semantics, you pass a pointer to the object. Since you are passing by reference, each line picks up where the last one left off.
fgetc() advances the file pointer (which is "where the next character to be read is located"). That's how you're able to call it in a loop and read a whole line of characters.
After it advances past the newline character, it naturally moves on to the next character, which is the beginning of the next line.
You could modify the file pointer with the fseek() function. For example, calling fseek(inFile, 0, SEEK_SET) would reset it to the beginning of the file, causing the next fgetc() call to start over from the first character of the file.

Resources