fprintf nested in for loop does not write the first element - c

Description
I am trying to write a csv table, tablepath, and I need to include in it the name of the variables, which are in a text file, filepath. I am using a first function, read_par, to retrieve the names from filepath and a second function, store, to write in the table.
Problem
The table created is systematically missing the name of the first variable from the text file. The read_par function is functional and produces the expected output: a string containing the name of the variable, I also included it for context.
filepath
Here is the structure of the text file:
par1
0 1 0.5
par2
1 1 1
par3
0 1 1
par4
0 1 1
par5
0 1 1
par6
0 1 1
store
Here is the store function:
int store(int par_num, float sim_num, float **tab_param, char *filepath, char *tablepath){
int j;
char *name = NULL;
FILE* sim_table = NULL;
sim_table = fopen(tablepath, "w");
// Start the first line in the table
fprintf(sim_table,"simulation_number");
for(j=1; j < par_num+1; j++){
// If it is the last parameter -> create a new line
if(j == par_num){
name = read_name(j, filepath);
fprintf(sim_table,",%s\n", name);
}else{
/* If it is not the last parameter -> continue writing on
* the same line */
name = read_name(j, filepath);
fprintf(sim_table,",%s", name);
}
}
fclose(sim_table);
return 0;
}
read_name
Here is the read_name function:
char *strtok(char *line, char *eof);
char *read_name(int par_id, char *filepath){
char *par_name;
int count = 0;
FILE *file = fopen(filepath, "r");
if ( file != NULL ){
char line[256]; /* or other suitable maximum line size */
while (fgets(line, sizeof line, file) != NULL){ /* read a line */
if (count == (2*par_id)-2){
// strtok removes the \n character from the string line
strtok(line, "\n");
par_name = line;
fclose(file);
}
else{
count++;
}
}
}
else
{
printf("\n\n ERROR IN FUNCTION READ_PAR\n\nTEXT FILE IS EMPTY\n\n");
}
return par_name;
}
tablepath
The table I am obtaining looks like this:
┌─────────────────┬┬────┬────┬────┬────┬────┐
│simulation_number││par2│par3│par4│par5│par6│
└─────────────────┴┴────┴────┴────┴────┴────┘
With the par1 name missing, but all the other variable name successfully printed. I do not know where is the problem. Is it a problem in the for loop conditions or something to do with the par1 string itself?
Thanks for any help on this.

The problem is that read_name returns the address of a local variable (line). That local variable goes out of scope (and technically ceases to exist) when the function returns. So using the returned pointer results in undefined behavior.
Too see the problem more clearly, here's a simplified version of read_name with only the relevant lines shown:
char *read_name(int par_id, char *filepath){
char *par_name;
char line[256]; // line is a local variable
while (fgets(line, sizeof line, file) != NULL){
par_name = line; // par_name now points to the local variable
}
}
return par_name; // returning the address of the local variable
}
It was noted in the comments under the question that read_name was tested and was found to be functional. So how can it be wrong? That's the worst thing about undefined behavior in C. Sometimes the code appears to work during testing even though it is technically incorrect. And by technically incorrect, I mean that it will break at some point. For example, in the store function, if you add another function call between the call to read_name and the call to fprintf, there's a good chance that name will be corrupted, and won't print properly.
An easy solution in this case is to declare line with the static keyword:
static char line[256];
That way, line has static storage duration, which means it will continue to exist after read_name returns.

Related

reading data from text file and add it into array

I tried to read the data from a text file and add it to an array. However, it raises an error ["Warning:" assignment makes integer from pointer without a cast]. I declared fp is a variable of FILE, and already open text file with read-only mode.
char arrRequest[100];
char strRow[10];
int i = 0;
.....................
void request(){
for (;;){
fgets(strRow, 10, (FILE *)fp);
i = i + 1;
arrRequest[i] = strRow; // it raises an error here
printf("value of %d is %s:\n", i, arrRequest[i]);
}
}
Could you please tell me why it happens?
Thanks.
In your code, you declared:
char arrRequest[100];
char strRow[10];
But in for loop:
arrRequest[i] = strRow;
It means you assign a character to an array of character. So it raises an error here.
If you want to copy a string, you should usestrcpy function. You can declare an array of pointer or 2D array as the comment above:
char arrRequest[100][10]
// using strcpy
strcpy(arrRequest[i], strRow);
Or
char * arrRequest[100]
// allocate for each arrRequest element
arrRequest[i] = malloc(strlen(strRow)*sizeof(char) + 1);
if (!arrRequest[i])
// Handle error
OT, you should use while loop to stop reading the file if there is nothing still to read. You can use for loop as you used in your code, but have to stop reading until you meet EOF. Do not need to cast fp if you declare FILE *fp before.
while(fgets(strRow, 10, fp);)

Load Text File Into 2D Array Then Compare Against Literal

I have 2D array I want populate then compare to literal
below is compare code, i try different things with no success
char** list;
load(list);
if(strcmp(list[0], "aasdf"))
{
printf("win\n");
}
the above segfaults on strcmp
load function
void load(char **list)
{
int MAX_NUM_LINES = 1000;
FILE *fp;
list = malloc(MAX_NUM_LINES*sizeof(char*));
fp = fopen("list", "r");
line_ct = 0;
char line[256];
while ( fgets(line, 256, fp) != NULL )
{
int len = strlen(line);
list[line_ct] = malloc(len * sizeof(char));
strcpy(list[line_ct], line);
line_ct++;
if(line_ct == MAX_NUM_LINES)
{
break;
}
}
fclose(fp);
}
any ideas on why is segfault?
also i try before strcmp
printf("Line: %s\n", *list[0]);
it segfault to
when you come back from load the var list is not set, so when you do
if(strcmp(list[0], "aasdf"))
you have an undefined behavior using list (typically a crash)
The first solution is use an output var
you need to change
load(list);
by
load(&list);
and you need to change the type of list and dereference it in load, so :
void load(char ***list)
{
*list = malloc(MAX_NUM_LINES*sizeof(char*));
...
(*list)[line_ct] = malloc((len + 1) * sizeof(char));
strcpy((*list)[line_ct], line);
I also added 1 to len to have place for the ending null character.
(edit) The use of a *** is not very common, as suggested by #user3629249 in a remark you can look at Triple pointers in C: is it a matter of style? reading carefully the answers.
The second solution is to return the allocated array :
char** list = load();
with
char ** load()
{
char **list;
...
return list;
also adding 1 to len when you allocate each line
Out of that if you read more than MAX_NUM_LINES lines you write out of the array again with an undefined behavior, and the caller of load does not know how much lines you read.
To avoid that you can first initialize list with malloc(0) then use realloc to increase the size of list each time you read a line, that allows to allocate the right size. To indicate the size to the caller you can use an additional output var or to allocate one entry more to place NULL in the last entry (all depends on how you use the read array in the code calling load)

C File pointer returns the contents of a file twice

With the following program, I wish to read a file (say, a text file) and store all of it's contents in a variable. So, to achieve it, I cooked up the following with the help of Stack Overflow suggestions. However, this program returns the contents of a file twice.
For instance, let the following program read a text file with the following contents:
John Start 0
Using *,15
Then, the program will display the following:
John Start 0
Using *,15
John Start 0
Using *,15
Thus, I would like your help in finding out the problem. Thanks a lot in advance!
//Program to read a file and store it's contents in a single variable
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
/*Reads contents of a file and returns address of variable, in which file contents are stored*/
char* fileRead(char*);
//Concatenates 2 strings
char* concat(char*,char*);
//Calculates the size of the file
unsigned long fsize(char* file);
int main()
{
char *prt, *out;
//Allocate memory & take the input of the file name to read
prt = malloc(256);
printf("\nEnter the name of the file : \t");
scanf("%255s",prt);
//Copy the address of read data & output it
out = fileRead(prt);
printf("\nContents : \n-----------------\n%s", out);
free(out);
free(prt);
return 0;
}
char* fileRead(char *file)
{
//function to read the contents of a file
FILE *fip;
char *text, *temp, read[1024];
int size, i=0;
fip=fopen(file, "r");
size=(int)fsize(file);
temp = malloc(size*10);
text = temp;
//If the file doesn't exist then...
if(fip==NULL)
{
temp = strdup("ERROR : File doesn't exist! Please try again!\n");
return temp;
}
//Begin concatenation, once after the file reading has been initiated
while(fgets(read, 1024, fip) != NULL)
{
temp = concat(text,read);i++;
}
fclose(fip);
return text;
}
char* concat(char* dest, char* src)
{
//function to concatenate src to dest
while(*dest) dest++;
while(*dest++ = *src++);
return --dest;
}
unsigned long fsize(char* file)
{
//calculates the size of file
FILE * f = fopen(file, "r");
fseek(f, 0, SEEK_END);
unsigned long len = (unsigned long)ftell(f);
fclose(f);
return len;
}
Edit 1: Thanks a lot people. For your quick response& efficient answers. As for the size*10 thing, it was a random idea, I came up with, to deal with one of the Segmentation Fault errors. Never thought the size+1 option. I've learned a lot from you all. Will come with a new question soon. Thanks again!
Your problem is that you use "text" as a string but failed to do it.
A "string" in C is a char array terminated by '\0'.
If you use any string related function, you have to unsure that you give string !
So after you allocated "text" (througth "temp"), "text" is not yet a string since it doesn't contain '\0'.
Since it's an "empty" string at the beginning, you have to initialize by doing
text[0] = '\0';
Now, why your file is printed twice ? Dunno, but I have to bet on an UB since your string wasn't properly initialized.

Value inside a double pointer isn't updated correctly (C)

I have a function that reads an input file and is supposed to modify the contents of a char** and a int*. The function is as follows:
void
input_parser(arguments* args, char** input, int* files) {
char buffer[MAX];
FILE *fr;
fr = fopen(args->file,"r");
if (fr == NULL) {
printf("No correct input file was entered\n");
exit(0);
}
while(fgets(buffer,MAX,fr) != NULL) {
input[*files] = strtok(buffer,"\n");
(*files)++;
}
fclose(fr);
return;
}
I have defined input and files as follows in the main program:
char* input[25];
files = 0;
I call the function as follows:
input_parser(args, input, &files);
The input file contains 3 lines as follows:
output1.xml
output2.xml
output3.xml
I notice that during the while loop the 'current' value is read correctly but stored in all input[*] resulting in:
input[0] = output3.xml
input[1] = output3.xml
input[2] = output3.xml
I would greatly appreciate if someone has any idea what is going wrong here.
The function is storing the address of the local variable buffer to each element in the input array: you need to copy the value returned by strtok(). The code as it stands is undefined behaviour as the buffer is out of scope once input_parser() returns, even it was not the logic is incorrect anyway.
If you have strdup(), you just use it:
input[*files] = strdup(strtok(buffer,"\n")); /* NULL check omitted. */
otherwise malloc() and strcpy(). Remember to free() the elements of input when no longer required.
Initialise input to be able determine which elements point to valid strings:
char* input[25] = { NULL };
You are going to end up having danging pointers, which are pointing inside your buffer after the buffer has been deallocated.

How can I count the occurrences of a character in a file?

The function is called as so,
printf("%d occurrences of %c in %s\n",
countoccurrences(argv[1], argv[1][0]),
argv[1][0], argv[1]);
and this is my function so far:
/* countcharinfile
* input: char *filename, char c
* output: the number of occurrences of char c inside file filename
*/
int countoccurrences(char *filename, char c)
{
// count the number of occurrences of c in the file named filename
FILE *fp = fopen(filename,"r");
int ch,count=0;
while ((ch = fgetc(fp) != EOF))
{
if (ch == c)
count++;
}
return count;
}
When I run the program, ./main Today is a beutiful day
I get the error Segmentation fault (core dumped)
It looks like you're using your function countoccurrences in main before it's been defined.
Add a function signature before main:
int countoccurrences(char *, char);
Or move the function itself to a place in your code before your main function.
Also:
you need to initialize your count variable to zero in countoccurences, and
you should check that fp != NULL before you use the file pointer. fopen will return NULL if it can't open the file.
When I run the program, ./main Today is a beutiful day
When you run your program this way, you are passing it 5 arguments, one for each word in your sentence. Review your function and your function call in main: the function wants a file name to search, which should be the first argument to your program, not the text to search. And the second argument should be the character to search for.
Since you're not checking the return value of fopen, your invocation here will cause problems, because you probably don't have a file named Today in the working directory.
C needs to know about your function signature before the call. Either:
Move your function before the call, or
Put a declaration before the call (at the global scope of course)
int countoccurrences(char *filename, char c);
You also need to initialize count (presumably to 0). You should make sure you are calling it with the right value. If you want to use the first character of the second parameter you should use argv[2][0].
The error indicates that the function declaration or definition was not visible at the point where it is called. Move the definition or put a declaration prior to main().
Other points:
Check return value of fopen()
Initialise count
buf is an unused local variable
For example:
FILE *fp = fopen(filename,"r");
if (fp)
{
int ch,count=0;
while ((ch = fgetc(fp)) != EOF)
{
if (ch == c) count++;
}
fclose(fp);
}
else
{
fprintf(stderr,
"Failed to open %s: %s\n",
filename,
strerror(errno));
}

Resources