fread fails to read an unsigned int? [C] - c

I have a Person Struct as the following:
typedef struct Person {
char name[NUM_CHARS];
unsigned int age;
} Person;
where #define NUM_CHARS 20.
I want to write this structure to a binary file, so I wrote two function to handle that:
int writePerson(Person* person, FILE* _fp) {
int i = 0;
int count = 0;
int len = strlen(person->name);
// Write name
for(i = 0; i < len+1; i++) {
count += fwrite(&(person->name[i]), sizeof(char), 1, _fp);
// fseek(_fp, 1, SEEK_CUR);
}
// Continue
// Write age
count += fwrite(&(person->age), sizeof(unsigned int), 1, _fp);
return count;
}
int readPerson(Person* person, FILE* _fp) {
int i = 0;
int count = 0;
// Write name
for(i = 0;person->name[i] != NULL;i++) {
count += fread(&(person->name[i]), sizeof(char), 1, _fp);
}
// Continue
// Write age
count += fread(&(person->age), sizeof(unsigned int), 1, _fp);
return count;
}
So I wrote a Person to a blank file Px.bin that I've created well in advance:
int main() {
FILE* fp = fopen("Px.bin", "r+b");
Person person = {"Billie", 40};
// Person y ;
int x = writePerson(&person, fp);
printf("%d", x);
// printPerson(&y);
getchar();
fcloseall();
return 0;
}
Seems to work well, prints 8.
but when I try to read this file:
int main() {
FILE* fp = fopen("Px.bin", "r+b");
// Person person = {"Billie", 40};
Person y ;
int x = readPerson(&y, fp);
printf("%d\n", x);
printPerson(&y);
getchar();
fcloseall();
return 0;
}
I'm getting this result:
11
Billie
-858993460
Where printPerson is:
void printPerson(Person* p) {
printf("%s\n%d\n", p->name, p->age);
}
What is the problem?

This bit
for(i = 0;person->name[i] != NULL;i++) {
count += fread(&(person->name[i]), sizeof(char), 1, _fp);
}
is going to use uninitialized (IOW, containing garbage) person->name[] as the condition for reading. And it may so happen that the wrong number of bytes will get read here.
After that the following
count += fread(&(person->age), sizeof(unsigned int), 1, _fp);
can read the integer from an incorrect location within the file, not where said integer has been stored.
The fix would be to first read a character and then see if it's '\0' or not. If it is, the string's been fully read. If it's not, keep reading characters.

Your code that reads name does check for likely non-initialized (or zeroed out) person->name[i] elements. As result the loop end at some non-predictable time (i.e. if it is zeroed out loop will not fread even single character).
for(i = 0;person->name[i] != NULL;i++) {
count += fread(&(person->name[i]), sizeof(char), 1, _fp);
}
You want to change it to do/while loop or instead of reading till 0 prefix data in the file string with length and read that many characters.

Related

random numbers being read into array instead of the text file values

I am trying to make a function that reads all the numbers from a text file into an array, where each line of the file has a number, ex:
57346
40963
24580
98307
98312
32777
10
16392
16396
...
My function does allocate the necessary size to store the values, but the values being stored are random ones and 0's that aren't in my text file. Output ex:
0
0
296386
0
-485579776
-653048057
584
0
2095946880
...
This is my code:
typedef struct set{
void** values;
int size;
}Set;
int checkSize(FILE* file) {
int counter = 0;
char chr;
chr = getc(file);
while (chr != EOF) {
if (chr == '\n') {
counter = counter + 1;
}
chr = getc(file);
}
return counter;
}
Set* readSet(FILE* file){
Set* new = malloc(sizeof(Set));
new->size = checkSize(file);
new->values = malloc(sizeof(void*)*new->size);
int arrayAux[new->size];
int i = 0, n;
while(i < new->size) {
fscanf(file, "%ld", &arrayAux[i]);
new->values[i] = arrayAux[i];
i++;
}
//loop to remove the first three lines of the file, which are the number of values in the file,
//the biggest value of the file and the division between the number of values and the biggest value
for(i = 0; i < 3; i++) {
new->values[i] = new->values[i + 1];
new->size--;
}
for (i = 0; i <= new->size; i++) {
printf("%d\n", new->values[i]);
}
return new;
}
How can I fix this? Thanks in advance for any help.
Why void and not long?
You cannot do int arrayAux[new->size]; as size is a variable and thus cannot be used at compile time !!! 100% guarantee of reading out of bounds.
Read the value from file into a long and assign it to the proper space in your list.
Why have the size in every row? Use a global int
why loop to step over the first three in the list?
size -=3
i+=3
Works just as well

Taking input and output to file using pointer array

Here, the array pointer name[i] is not not working and I can't figure out how to change the pointer or the array to make it work. There was no compiler error/warning but the console terminal showed a negative return value and printf() was not working.
Also, the output file was created but it was empty: nothing was written into it.
I guess there's some problem with the fscanf() action of taking string input from file.
#include<stdio.h>
#include<math.h>
int main()
{
FILE *fr, *fw;
fr = fopen("data.txt", "r");
float wt[10], ht[10], bmi[10];
char *name[5];
for(int i = 0; i < 5; i++)
{
fscanf(fr, "%s\t%f\t%f", &name[i], &wt[i], &ht[i]);
bmi[i] = wt[i] / pow(ht[i], 2);
}
printf("%s", name[2]);
fw = fopen("bmi.txt", "w");
for(int j = 0; j < 5; j++)
{
fprintf(fw, "%s\t%f\n", name[j], bmi[j]);
}
fclose(fr);
fclose(fw);
return 0;
}
You should not assume that the file size is 5. You should dynamically allocate memory to the name variable like so:
int length;
fseek(filename, 0, SEEK_END);
length = ftell(filename);
name = malloc(length);
fread(name, 1, length, filename);

Scan set of integers into 2-D array

Consider the following set of numbers:
10 20 30
11 31 45
...
I want to define a 2-D array int_array[][3] such that int[0] contains {10, 20, 30}, then int[1] contains {11, 31, 45} and so on.
Assume that these values are in a file. The code I would use is therefore:
int int_array[NUM_ROWS][3], i;
for (i=0; i<NUM_ROWS;i++)
fscanf(filePointer, "%d", int_array[i]);
However, this produces ridiculous results. What am I doing wrong?
produces ridiculous results. What am I doing wrong?
int_array[i] points to only 1 int, not the 3 needed.
Code lacks error checking.
Create a helper function to read 1 line of data, saving the data when d != NULL.
int read3int(FILE *inf, int d[3]) {
char buf[100];
if (fgets(buf, sizeof buf, inf) == NULL) {
return EOF;
}
int n = 0;
int i[3];
sscanf(buf, "%d%d%d %n", &i[0], &i[1], &i[2], &n);
// Was scan incomplete or with extra junk?
if (n == 0 || buf[n] != 0) {
return 0;
}
if (d) {
d[0] = i[0];
d[1] = i[1];
d[2] = i[2];
}
return 1;
}
To form the array, first find how many rows.
size_t rows = 0;
while (read3int(filePointer, NULL) > 0) {
rows++;
}
// Form the array
int data[rows][3]; // VLA or use malloc
// Rewind !!
rewind(filePointer);
for (size_t r = 0; r < rows; r++) {
if (read3int(filePointer, data[r]) < = 0) {
// Unexpected 2nd pass inconsistency
break;
}
}
You said in the comments: "my real set of numbers does not have 3 columns but instead multiple"
I am posting a general method to read lines of integers from a file, storing them in a 2D array. It works regardless of the number of integers in each line.
But I haven't run this piece of code and I never used strtol, so chances are it doesn't work. I am giving you the idea, you can implement it properly.
char line[N], *ptr;
for(i = 0; fgets(line, N, filePointer) != NULL; i++) {
int_array[i][0] = (int) strtol(line, &ptr, 10);
for(j = 1; *ptr != '\n'; j++) {
int_array[i][j] = (int) strtol(ptr, &ptr, 10);
}
}
The logic is to read an entire line as a string and then convert the numbers from char to int.

Checking for null/empty float values when using sscanf

The following program attempts to read an input file line by line using fgets, and save each comma delimited float value into an array of structs using sscanf (this aspect of the code works fine). The issue lies in that the program should also detect when a float value is missing/empty, and assign it the float value 1.500 which then is saved into the array of structs.
EDIT: This is supposed to be compiled using VS2017, so on Windows.
*Note: Please note that the following questions have been studied before posting this question:
How to check if a string returned by scanf is null
How to get scanf to continue with empty scanset
An example of the input file (missing value in the second row):
0.123f, 0.234f, 0.345f, 0.456f, 0.567f
1.987f, , 7.376f, 2.356f, 5.122f
9.111f, 1.234f, 7.091f, 6.672f, 9.887f
Desired output (missing value in second row is detected and set to 1.500):
0.123 0.234 0.345 0.456 0.567
1.987 1.500 7.376 2.356 5.122
9.111 1.234 7.091 6.672 9.887
So far, the first attempt tried to scan all 5 floats (each with 'f' suffix) into strings and then check to see if those strings are null/empty or of zero length using strcmp and strlen, respectively, and finally involved trying to use sscanf again on each of those variables to read each into an array of structs.
The 2nd attempt included a check to see if the sscanf was successful by using if (sscanf(line, "%ff", &data[i].x) == NULL) { // ...some alert and assign 1.500}, which did not work either. The 3rd attempt, as seen below:
#include "stdio.h"
int main() {
typedef struct {
float x, y, vx, vy, mass;
}DATA;
FILE *file = fopen("null_detector.txt", "r");
if (file == NULL)
{
printf(stderr, "ERROR: file not opened.\n");
return EXIT_FAILURE;
}
int N= 3;
DATA* data = malloc(Nbodies * sizeof * data); // Array allocation
char line[256];
int i;
int inc = 1;
for (i = 0; i < Nbodies; i += inc)
{
fgets(line, sizeof(line), file);
// **Some info:
// Scan 5 float variables per line (this part works fine)
sscanf(line, "%ff, %ff, %ff, %ff, %ff",
&data[i].x, &data[i].y, &data[i].vx, &data[i].vy, &data[i].mass); // %ff accounts for 'f' suffix
// Now check if any of above vars are empty/NULL.
// NOTE: aware that these vars CANNOT be compared to NULL,
// but has been included to try and provide clarity for end goal
if (data[i].x == NULL)
{
//.. assign 1.500 to data[i].x
}
if (data[i].y == NULL)
{
//... same as above etc
}
// ...Repeat IF statements for all 5 vars
}
//Print the contents of array of structs to check for correct output
for (i = 0; i < Nbodies; i++)
{
printf("%.3f %.3f %.3f %.3f %.3f\n", data[i].x, data[i].y, data[i].vx, data[i].vy, data[i].mass);
}
return 0;
}
Summary:
Does anyone know how this program can be modified to:
detect missing float values in each line of the file upon reading them with fgets
replace missing float values with the float value 1.500
write these values to the array of structs, like the non-missing values successfully are doing?
As commented in the code, I am aware that the struct float variables cannot be compared to NULL. I have included this comparison in the code to only try to add some clarity as to what the end goal is.
You can use strsep to separate each line.
str = strsep(&line, ",")
Using one function to set the value of data:
void set_data(DATA *dt, int count, float f) {
switch(count) {
case 0: dt->x = f; break;
case 1: dt->y = f; break;
case 2: dt->vx = f; break;
case 3: dt->vy = f; break;
case 4: dt->mass = f; break;
}
}
The complete code:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
float x, y, vx, vy, mass;
}DATA;
void set_data(DATA *dt, int count, float f) {
switch(count) {
case 0: dt->x = f; break;
case 1: dt->y = f; break;
case 2: dt->vx = f; break;
case 3: dt->vy = f; break;
case 4: dt->mass = f; break;
}
}
int main() {
FILE *file = fopen("text.txt", "r");
if (file == NULL)
{
printf( "ERROR: file not opened.\n");
return EXIT_FAILURE;
}
int N= 3;
DATA* data = malloc(N * sizeof(data)); // Array allocation
char *line;
int i;
int inc = 1;
size_t n = 0;
for (i = 0; i < N; i += inc)
{
getline(&line, &n, file);
int count = 0;
char *str;
while((str = strsep(&line, ",")) != NULL) {
if (strcmp(str, " ") == 0) {
set_data(&data[i], count, 1.5);
} else {
set_data(&data[i], count, atof(str));
}
// printf("count = %d\n", count);
// printf("token: %s\n", str);
count++;
}
}
//Print the contents of array of structs to check for correct output
for (i = 0; i < N; i++)
{
printf("%.3f %.3f %.3f %.3f %.3f\n", data[i].x, data[i].y, data[i].vx, data[i].vy, data[i].mass);
}
return 0;
}
The input:
#cat text.txt
0.123f, 0.234f, 0.345f, 0.456f, 0.567f
1.987f, , 7.376f, 2.356f, 5.122f
9.111f, 1.234f, 7.091f, 6.672f, 9.887
The output:
0.123 0.234 0.345 0.456 0.567
1.987 1.500 7.376 2.356 5.122
9.111 1.234 7.091 6.672 9.887
It can also achieved with only sscanf if there is at least a space between the commas when there is an absence of an input value.
#include <stdio.h>
int main(void) {
char *str[] = {"0.123f, 0.234f, 0.345f, 0.456f, 0.567f",
"1.987f, , 7.376f, 2.356f, 5.122f",
"9.111f, 1.234f, 7.091f, 6.672f, 9.887f"};
float float_arr[3][5];
char temp[5][7];
for (unsigned i = 0; i < 3; i++) {
if (5 != sscanf(str[i], "%6[^,],%6[^,],%6[^,],%6[^,],%6[^,]",
temp[0], temp[1], temp[2], temp[3], temp[4]))
return printf("Error\n"), 1;
for (unsigned j = 0; j < 5; j++)
if (1 != sscanf(temp[j], "%ff", &float_arr[i][j]))
float_arr[i][j] = 1.500f;
}
// printing the result
for (unsigned i = 0; i < 3; i++) {
for (unsigned j = 0; j < 5; j++)
printf("%ff ", float_arr[i][j]);
printf("\n");
}
return 0;
}
Output
0.123000f 0.234000f 0.345000f 0.456000f 0.567000f
1.987000f 1.500000f 7.376000f 2.356000f 5.122000f
9.111000f 1.234000f 7.091000f 6.672000f 9.887000f

Array dynamically allocated by file size is too large

I am working on a class assignment and need some help with dynamically allocated arrays. I am using file_size to try to pull the file size from 3 files to allocate the array to that size, then I need to write and sort the data in the array. My issue right now is with sizing of the array; right now my output (ignoring sorting) is:
1
3
7
9
0
0
0
0
2
4
8
0
0
0
5
6
10
0
0
0
0
0
0
As you can see it is being padded with extra 0s. Here are the input files:
inputFile1:
1
3
7
9
inputFile2:
2
4
8
inputFile3:
5
6
10
0
I need some help figuring out what's going on with this and where the issue is. I want to get rid of those extra 0s, and I'm not even sure where they are coming from. Help with the sorting would also be appreciated.
file_size:
long file_size(FILE *inputFile)
{
if(inputFile == NULL)
return -1;
long pos = ftell(inputFile);
fseek(inputFile, 0, SEEK_END);
long size = ftell(inputFile);
fseek(inputFile, pos, SEEK_SET);
return size;
}
Main:
int main(void)
{
FILE *file0 = fopen("list0.txt", "r");
FILE *file1 = fopen("list1.txt", "r");
FILE *file2 = fopen("list2.txt", "r");
FILE *output = fopen("hw3.out", "w");
long size0 = file_size(file0);
long size1 = file_size(file1);
long size2 = file_size(file2);
long totalSize = size0 + size1 + size2;
int *numbers = malloc(totalSize * sizeof(int));
int i;
int index = 0;
for(i = 0; i < file_size(file0); i++)
{
if(!feof(file0))
{
fscanf(file0, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < file_size(file1); i++)
{
if(!feof(file1))
{
fscanf(file1, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < file_size(file2); i++)
{
if(!feof(file2))
{
fscanf(file2, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < totalSize; i++)
{
fprintf(output, "%i\n", numbers[i]);
}
fclose(file0);
fclose(file1);
fclose(file2);
fclose(output);
free(numbers);
return 0;
}
Your input files have several lines, each of which has the textual representation of a number. Your file size function however is counting the total number of bytes in the file. These are not the same.
While you can still use the file size to allocate space (you'll just get more than you need), you need to instead check the return value of scanf to see if a number was read. If not, you jump out of the loop.
int index = 0;
while (fscanf(file0, "%i", &numbers[index]) == 1) {
index++;
}
while (fscanf(file1, "%i", &numbers[index]) == 1) {
index++;
}
while (fscanf(file2, "%i", &numbers[index]) == 1) {
index++;
}
for(i = 0; i < index; i++)
{
fprintf(output, "%i\n", numbers[i]);
}
The size calculation includes the carriage return "\n" character at the end of the file. Therefore, you are allocating space for 8 integers for the first file, 6 integers for the second file and 10 integers for the third file (10, because the number "10" has two digits).
The correct allocation strategy would be not to count the bytes in the files, but the lines (which actually contain numbers, thus allowing you to skip empty lines).
But that's just too much trouble. Instead, consider just allocating 1000 bytes, read in until you run out, then re-alloc into a larger buffer.

Resources