Program not accepting all values from file - c

I posted a problem yesterday regarding a certain segment of my code. The aim was to basically scan in data values from a .dat file into an array, print the values whilst also counting how many values were in the file.
Sounds pretty simple, but my program only seemed to print a certain number of the values. More specifically, of a data file containing over 300000 values, it would only print the last 20000 and nothing else.
So I left it, finished the rest of my code and now it's the last part I have to sort. I've made a few changes and tried actually printing an output .dat file now so I can see what I'm getting. The code is below by the way.
Initally I assumed perhaps it was something related to the memory allocation of my array (was getting a segmentation error? when putting the whole code together) so I created an external function that counted the number of values instead (that works).
My only problem now is that it still only chooses to print 20000 values and then the rest are 0s. I was thinking perhaps it had something to do with the type but they all contain 7 dps in scientific notation. Here's a sample of some of the values:
8.4730000e+01 1.0024256e+01
8.4740000e+01 8.2065599e+00
8.4750000e+01 8.3354644e+00
8.4760000e+01 8.3379525e+00
8.4770000e+01 9.8741315e+00
8.4780000e+01 9.0966478e+00
8.4790000e+01 9.4760274e+00
8.4800000e+01 7.1199807e+00
8.4810000e+01 7.1990172e+00
Anyone see where I'm going wrong? I'm sorry for the long question, it's just been bugging me for the last day or so and no matter what I change nothing seems to help. Any kind of input would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
int count(int);
const char df[]="data_file.dat";
const char of[]="output_file.dat";
int main(int argc, char *argv[])
{
FILE *input, *output;
int i, N;
float *array;
N = count(i);
input = fopen(df, "r");
output = fopen(of, "w");
array = (float*)malloc(N*sizeof(float));
if((input != (FILE*) NULL) && (output != (FILE*) NULL))
{
for(i = 0; i < N; i++)
{
fscanf(input, "%e", &array[i]);
fprintf(output, "%d %e\n", i, array[i]);
}
fclose(input);
fclose(output);
}
else
printf("Input file could not be opened\n");
return(0);
}
int count(int i)
{
FILE *input;
input = fopen(df, "r");
int N = 0;
while (1)
{
i = fgetc(input);
if (i == EOF)
break;
++N;
}
fclose(input);
return(N);
}

Your biggest problem is that count() doesn't count float values; it counts how many characters are in the file. Then you try to loop and call fscanf() more times than there are values in the file. The first times, fscanf() finds a float value and scans it; but once the loop reaches the end of file, fscanf() will be returning an EOF status. It seems possible that fscanf() will set the float value to 0.0 when it returns EOF.
I suggest you rewrite so that you don't try to pre-count the float values. Write a loop that just repeatedly calls fscanf() until it returns an EOF result, then break out of the loop and close the files.
P.S. If you are going to write a function like count(), you should pass in the filename as an argument instead of hard-coding it. And your version of count() takes an integer argument but just ignores the value; instead, just declare a temp variable inside of count().
EDIT: Okay, here is a complete working program to solve this problem.
#include <stdio.h>
int
main(int argc, char **argv)
{
FILE *in_file, *out_file;
unsigned int i;
if (argc != 3)
{
fprintf(stderr, "Usage: this_program_name <input_file> <output_file>\n");
return 1; // error exit with status 1
}
in_file = fopen(argv[1], "r");
if (!in_file)
{
fprintf(stderr, "unable to open input file '%s'\n", argv[1]);
return 1; // error exit with status 1
}
out_file = fopen(argv[2], "w");
if (!out_file)
{
fprintf(stderr, "unable to open output file '%s'\n", argv[2]);
return 1; // error exit with status 1
}
for (i = 0; ; ++i)
{
int result;
float x;
result = fscanf(in_file, "%e", &x);
if (1 != result)
break;
fprintf(out_file, "%d %e\n", i, x);
}
return 0; // successful exit
}
Note that this version doesn't need to allocate a large array; it just needs a single temporary float variable. Maybe your program will need to store all the float values. In that case, write a count() function that uses a loop similar to the above loop, using fscanf() to count float values.
Also note that this program checks for errors after calling fopen() and fscanf().

You are allocating far more floats in memory (N) than you need because your N is the number of characters in the file, not the number of values in it.
Also, how did you determine that there are 300000 values in the file?

Related

C fgets giving unexpected result

I'm doing Advent of Code 2022, and I'm hitting a strange bug for day 1 (https://adventofcode.com/2022/day/1) which I need help with please.
Currently, I'm trying to parse a text file which has groups of numbers (https://adventofcode.com/2022/day/1/input) and output the results to the screen. Code as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define IN_FILE "input.txt"
#define MAX_LINE_LEN 20480
int main(int argc, char *argv[])
{
// Variable declaration
char line[MAX_LINE_LEN];
int counter = 0;
int elfno = 0;
FILE *fd;
// Create "Key Value pair" struct. Use ints for both vars as we need to perform arithmetic on the values.
struct kv_store
{
int key;
int value;
};
struct kv_store kv[counter];
//struct kv_store kv_total;
// Open file
fd = fopen(IN_FILE, "r");
if (fd == NULL)
{
// File does not exist. Exit
fprintf(stdout, "\n[*]%s not found in current working directory.", IN_FILE);
return -1;
}
// Iterate through file line by line. Place lines into kv[n].value with kv[n].key as the elf number (elfno). Increment elfno once a blank line is found.
while(fgets(line, MAX_LINE_LEN, fd) != NULL)
{
if(strlen(line) == 1)
{
elfno ++;
continue;
}
else
{
kv[counter].key = elfno;
sscanf(line, "%d", &kv[counter].value);
//kv[counter].value = strtol(line, NULL, 0);
counter ++;
continue;
}
}
for(int i=0;i < counter;i++)
{
fprintf(stdout, "Iteration: %d - Elf: %d holds: %d \n", i, kv[i].key, kv[i].value);
}
// TODO: Upon EOF, we can iterate through kv. If elfno for next value in kv hasn't changed, add value to kv_total[elfno].value. If it does change, move on to the next efls values.
// TODO: Once complete, iterate through kv_total and find the largest number, print to screen.
return 0;
}
Everything seems to be working ok, however on the 4th iteration of the while loop which reads the value 6086 from the input, I get a garbage result. I should be seeing Elf: 1 holds: 6086. The remaining iterations give the correct output (sample screenshot below).
Using my very limited knowledge of gdb and debugging, I've looked at the line variable during runtime and can see that the variable is being modified only when line contains the value 6086 partway through the iteration (screenshot below).
Checks carried out so far:
Used a hex editor to confirm there is no rogue encoding within the input file
Tried using strtol instead of of sscanf
Tried to use malloc() and free() to clear the line variable at the end of each iteration
Any help on this would be much appreciated.

Why am I getting this segmentation fault in my code while iterating through large quantities of int values?

The C program I am trying to write is meant to take a file of integer values and sum the first number with the second, then the first with the third, the first with the forth, and so on. Then sum the second with the third, the second with the forth, and so on. It is also meant to print a message whenever a predetermined value is met while adding all the numbers (in my case, it's 10), and print out the time it took for this program to execute.
However, when the program starts going through the list of integers in the file I am taking values from (over 10,000 integers are in this file), it seems that it gets to the 124th loop and then gives me a Segmentation Fault (Core Dumped) error. When I reduce the number of integers to be added to under 124, the program works fine.
Why am I getting this Segmentation Fault error? Is it in the way that I set up my While loops? Is there any way I can remedy this?
#include<stdio.h>
#include <time.h>
int main(){
FILE *fptr1,*fptr2,*temp;
fptr1 = fopen("input.txt", "r") ;
int num1,num2;
int sum=0;
int c=0;
if (fptr1 == NULL) {
printf("File not open\n");
return 0;;
}
int count=0;
clock_t t;
t = clock();
// the problem seems to be in this while loop//
while(fscanf(fptr1, "%d", &num1) !=EOF){
count++;
fptr2 = fopen("input.txt", "r") ;
int count1=0;
while(fscanf(fptr2, "%d", &num2) !=EOF){
count1++;
if(count1>count){
sum=num1+num2;
if(sum==10){c++;}
}
}
}
printf("sum of 10 was found in %d iterations of loop",c);
t = clock() - t;
double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds
printf("Time taken :%lf", time_taken);
fclose(fptr1);
fclose(fptr2);
return 0;
}
When the file has over 124 integers, it gives me this:
the output when over 124 integers
and when he file has less than 124 integers, it gives me this:
the output when under 124 integers
The problem seems to be opening way too many file handles, but the fix should be simple:
while(fscanf(fptr1, "%d", &num1) !=EOF){
count++;
// This fopen() call might fail, you should check the result
fptr2 = fopen("input.txt", "r");
int count1 = 0;
while(fscanf(fptr2, "%d", &num2) != EOF) {
// ...
}
// If you open a file, you must close it before re-opening
fclose(fptr2);
}
This is quite wasteful, though, there's no reason to open over and over, instead
just rewind the file and use it again:
fptr2 = fopen("input.txt", "r");
while(fscanf(fptr1, "%d", &num1) !=EOF) {
// Move back to the beginning of the file before reading
rewind(fptr2);
count++;
int count1 = 0;
while(fscanf(fptr2, "%d", &num2) != EOF) {
// ...
}
}
fclose(fptr2);
Another thing to note here is your variable names are extremely opaque. fptr2 doesn't communicate anything about what this is supposed to be used for, or what that file might contain. If it correlates with an input source, consider input or finput or even fin. Likewise, count1 and count and an even more mysterious c are confusing. Counting what? For what reason?

simple C code to either read a file or stdin

Absolutely new to C. I am trying to write a program to either read integers contained in a file (passed as arg) or from stdin. First number read is supposed to indicate the array size.
I have something but it throws segmentation fault. Please help.
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[]) {
FILE *f;
int n, numbers[n], firstNum;
if (argc != 0)
{
f = fopen(argv[1], "r");
fscanf(f, "%d", & firstNum);
int numbersArray[firstNum];
for (int i = 0; i < firstNum; i++)
{
fscanf(f, "%d", &numbersArray[i]);
}
for (int i = firstNum; 0 <= i; i--)
{
printf("Numbers: %d\n\n", numbersArray[i]);
}
fclose(f);
}
else
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
fscanf(stdin, "%d", &numbers[i]);
printf("%d\n", i, numbers[i]);
}
return 0;
}
First off always initialize your variables like kaylum and myself mentioned in the comments.
Unlike some other programming languages C doesn't initialize your variables for you.
Now for the question you posted, you got the idea mostly correct but there are things that i suggest you change:
int n, numbers[n], firstNum;
to
int n = 0, numbers[1000], firstNum = 0;
If you're not happy with that you could move the numbers[n] lower when n is initialized but i wouldn't advise that.
Instead of that, since you're learning C have a look at dynamically allocated arrays using pointers.
Some links to help you understand pointers:
https://www.cprogramming.com/tutorial/c/lesson6.html
https://www.tutorialspoint.com/cprogramming/c_pointers.htm
Next have a look at your fopen it has a return value that tells you if it succeeded to open the file you requested.
So you can add:
f = fopen(argv[1], "r");
if (f == NULL)
{
printf("File Not Found!");
return -1;
}
Modify the condition to suite the way you want to handle that error.
When you're reading the file the user specifies the size of the array you're reading but you should consider the fact that the file may not contain the exact amount of elements.
The function that you are using fscanf has a way to tell you that and that's it's return value.
It will return EOF which is a special value that translates to End Of File.
Consider modifying your fscanf to this:
if (fscanf(f, "%d", &firstNum) == EOF)
{
printf("ERROR");
}
Or if you're in a for loop put break
The last thing is that you don't have to explicitly use fscanf for stdin, the user has specified that he will input it through stdin so you can just as well use scanf.
Hope this helps and good luck with your future C endeavors.

How to move values from a CSV file to a float array?

I have a .csv file formatted like this:
24.74,2.1944,26.025,7.534,9.317,0.55169 [etc]
I want to move the float values into a floating point number array.
The array would look like this:
fValues[0] = 24.74
fValues[1] = 2.1944
fValues[2] = 26.025
fValues[3] = 7.534
fValues[4] = 9.317
[etc]
I have 1000 numbers to process.
What is the code to achieve this task?
This is the closest I have gotten with my code:
int main()
{
FILE *myFile;
float fValues[10000];
int n,i = 0;
myFile = fopen("es2.csv", "r");
if (myFile == NULL) {
printf("failed to open file\n");
return 1;
}
while (fscanf(myFile, "%f", &fValues[n++]) != EOF);
printf("fValues[%d]=%f\n", i, fValues[5]); //index 5 to test a number is there.
fclose(myFile);
return 0;
}
Also, when I run this code I receive exit code 3221224725.
Would this be a memory access related issue/stack overflow)?
My environment:
Sublime Text 3,
GCC compiler,
Newer windows laptop
When you read from the file, you're not skipping over the commas in the file.
The first call to fscanf reads a float via the %f format specifier. On subsequent reads, the file pointer is at the first comma and doesn't go past that because you're still trying to read a floating point number.
You need to add a separate call to fscanf inside the loop to consume the comma:
while (fscanf(myFile, "%f", &fValues[n++]) == 1) {
fscanf(myFile, ",");
}
Also, you're not initializing n:
int n,i = 0;
When you then attempt to increment it, thereby reading an uninitialized value, you invoke undefined behavior. Initialize it like this:
int n = 0, i = 0;

Read a file then store numbers in array C

so I have this file called "score.txt" with contents
NAME
20
NAME2
2
And I'm using this code but it gets an error and I have no idea on how to put the integers from the file in an array.
int main(){
FILE* file = fopen ("score.txt", "r");
int i = 0;
fscanf (file, "%d", &i);
while (!feof (file))
{
printf ("%d ", i);
fscanf (file, "%d", &i);
}
fclose (file);
system("pause");
}
I'm only self learning and i've been trying to figure this out for 2hours already
The problem with using fscanf for input where some lines will fail the format is that the file will not be advanced per iteration of the while loop, so you get stuck.
You can get a solution by using fgets to grab the data and sscanf to grab the number:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
int i = 0;
int ret = 0;
char buf[50];
FILE *file = fopen("score.txt", "r");
if (file == NULL) {
fprintf(stderr,"Unable to open file\n");
exit(1);
}
while (fgets(buf,sizeof(buf),file)) {
ret = sscanf(buf,"%d",&i);
if (ret == 1) { // we expect only one match
printf("%d\n", i);
} else if (errno != 0) {
perror("sscanf:");
break;
}
}
fclose(file)
return(0);
}
This will output, for your input:
20
2
We check the output of sscanf as it tells us if the format has been matched correctly, which will only happen on the lines with integer, and not the 'NAME' lines. We also check for 'errno' which will be set to non-zero if sscanf encounters an error.
We used char buf[50]; to declare a char array with 50 slots, which fgets then uses to store the line its reading; however if the line is more than 50 chars in length it will be read in 50 char chunks by fgets, and you may not get the results you desire.
If you wish to store the integers you read into an array, you'll have to declare an array, then on each read assign a slot in that array to the value of the int you read i.e. int_array[j] = i (where j will have to change with each slot you use). I'll leave it as an exercise to implement this.

Resources