I'm doing an assignment where I have to create a grade report program. Everything was working, but the final section has me altering the code so it reads data from a file (student.dat) and uses that as data for the output.
Sample of student.dat (there are 100 lines similar to these in total).
Julius Hoffman,95,92,79,90,90
Dianne Conner,100,100,80,90,85
Mitchell Cooper,100,95,89,85,95
My issue is with the fscanf command as it is only reading the first name
Sample Output (the final program will have 100 lines total)
Please enter a filename: student.dat
Student Name W(30%) Q(15%) T(20%) P(15%) E(20%) TOTAL(100%) GRADE
Julius Hoffman 0 0 0 0 0 0.00 F
0 0 0 0 0 0.00 F
0 0 0 0 0 0.00 F
The function that handles the file reading is below.
int read(char filename[], char name[][MAX_NAME_SIZE], float w[], float q[], float t[],float p[],float e[]){
int i =0;
FILE *fp = NULL;
fp = fopen(filename, "r");
if (fp != NULL) {
for(i=0; i < MAX_CLASS_SIZE; i++){
fscanf(fp, "%[^,]%[^,]%[^,]%[^,]%[^,]%[^,]", name[i], w[i], q[i], t[i], p[i], e[i]);
}
fclose(fp);
} else {
printf("Failed to open file\n");
}
return 0;
}
And just in case, this is the function that handles presenting the report to the user
void display( char name[][MAX_NAME_SIZE], float w[], float q[], float t[], float p[], float e[], float total[], char grade[][MAX_LETTER_SIZE] ) {
int i = 0;
printf("Student Name W(30%) Q(15%) T(20%) P(15%) E(20%) TOTAL(100%) GRADE\n===================================================================================\n");
for(i = 0; i < MAX_CLASS_SIZE; i++){
printf("%-15s %-3.0f %-3.0f %-3.0f %-3.0f %-3.0f %-3.2f %-15s\n", name[i], w[i], q[i], t[i], p[i], e[i], total[i], grade[i]);
}
}
Any advice is appreciated.
Mis-match scanf() format specifier. Use "%f" with float *
Also save debug time. Simply read the line into a buffer and then scan it.
" %n" scans white-space and then save the index of the scan into n. So if n > 0, code knows the entire line scanned. if buf[n], the scan did not finish at the end of the line.
for(i=0; i < MAX_CLASS_SIZE; i++) {
char buf[200];
if (fgets(buf, sizeof buf, fp) == NULL) break;
int n = 0;
sscanf(buf, fp, " %[^,],%f ,%f ,%f ,%f ,%f %n",
name[i], &w[i], &q[i], &t[i], &p[i], &e[i], &n);
if (n > 0) break; // format error
if (buf[n]) break; // extra text
// Use name[i], w[i], q[i], t[i], p[i], e[i]
}
Your fscanf should look like this:
fscanf(fp, " %[^,],%d,%d,%d,%d,%d", name[i], &w[i], &q[i], &t[i], &p[i], &e[i]);
Notes:
%[^,] control format is only for reading characters until the comma. There is no reason why you use that control format for all your data (which include several int afterwards I suppose).
You should also check the return results of your fscanf, make sure that it returns the correct value, in this case you are reading 6 values from your text file at a time.
Related
I was given a problem to solve using fscanf which states the following:
Write a function to initialize array x of size SIZE with integers read from a text file. The function receives the name of the file as an argument.
The solution states the following below. Why would fscanf return a value < 1 if you're scanning each integer in the file? Shouldn't it be > 0 since fscanf returns the number of elements scanned?
void init (char *name)
{
FILE *fp;
int i;
fp = fopen (name, "r");
if (fp == NULL)
{
printf ("no file\n");
return;
}
for (i = 0; i < SIZE; i++)
{
if (fscanf (fp, "%d", &x[i]) < 1)
{
printf ("got %d numbers\n", i);
return;
}
}
fclose (fp);
return;
}
You seem to be misunderstanding the intent of the code:
for (i = 0; i < SIZE; i++)
{
if (fscanf (fp, "%d", &x[i]) < 1)
{
printf ("got %d numbers\n", i);
return;
}
}
What this will do is return if the fscanf failed to read in an integer (it returns the number of items scanned). So, at that point, the function will return(a).
If you get a positive integer, it means you successfully read in the value and your loop will continue.
(a) You probably should break here rather than return since the file doesn't get closed the way it is at the moment.
Why would fscanf return a value < 1 if you're scanning each integer in the file?
fscanf (fp, "%d", &x[i]) returns:
1 when one int scanned successfully. Code loops to read additional int
0 when only non-numeric non-white-space input detected
EOF (EOF is some negative value) on end-of-file.
EOF on input error (rare).
With if (fscanf (fp, "%d", &x[i]) < 1), the branch is taken as one of the last 3 conditions occurred - likely end-of-file.
I am novice to C langugage, so please bear with me. I've tried to read a file which contains strings but output obtained is single character.
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define CALLOC(num, type) ((char*)calloc (num, sizeof(char)))
#define FREE(addr) (free((void*) (addr)))
int i, count;
char *x, *y, *z;
int main (void)
{
FILE *stream;
if ( (stream = fopen ( "test.txt", "r" )) == NULL )
{ printf ("Cannot read the new file\n");
exit (1);
}
count = 3;
x=CALLOC(count, char);
y=CALLOC(count, char);
z=CALLOC(count, char);
for ( i=0; i<count; i++ )
{ fscanf (stream,"%c %c %c", &x[i], &y[i], &z[i]);
printf ("\n %d %c %c %c ", i, x[i], y[i], z[i]);
}
FREE(x);
FREE(y);
FREE(z);
fclose (stream);
}
Input test.txt file contains
1 ab 1
2 aa 5
1 cc 1
current output
0 1 a b
1 1 2
2 a a
Expected output
0 1 ab 1
1 2 aa 5
2 1 cc 1
I doubt whether I should use a character array but it seems not working and I feel reading a int using char is acceptable. Here I require the expected output, for this any method/suggestion is appreciated.
%c reads in only one char. So it's not going to read ab as a single char. Your lines in file and your formats don't correctly to read an entire line.
A simple approach is to use fgets() and print the entire line:
char line[256];
i = 0;
while(fgets(line, sizeof line, stream))
{
printf ("%d %s", i, line);
i++;
}
By the way, macros for calloc and free are unnecessary. They really don't make the code any easier to read than directly using those functions.
And the casts in them are also unnecessary.
The problem is you have the scan file. %c read a 8bit value. You scanned 3 char, but the file is contain 4 characters. If you don't use to be the value of the x, y, z I don't understand why use malloc.
Here a working source:
#include <stdio.h>
#include <stdlib.h>
int main() {
int count,i;
char w,x,y,z;
FILE *stream;
if((stream = fopen("test.txt","r")) == NULL) {
printf("Cannot read the new file\n");
return 1;
}
count = 3;
for(i=0;i<count;++i) {
fscanf(stream,"%c %c%c %c\n",&w,&x,&y,&z);
printf("%d %c %c%c %c\n",i,w,x,y,z);
}
return 0;
}
for ( i=0;i<count; i++ )
{
fscanf (stream,"%s %s %s", x, y, z);
printf ("\n %d %s %s %s ", i, x, y, z);
}
You can modify your loop to this.This loop will read file until end of file and you have to use %s as ab is a string not charater so it can't be stored in a char variable.
I have my txt file
4
110
220
112
335
4 is the number of lines and 4*3 the number of int. I have to read "4" then read the remaining and input them into an array
This is what I have
void main(){
int a,n;
int i=0,j=0,k[30]; //
int *N;
FILE *fp = fopen("test.txt", "r");
if(fscanf(fp, "%d", &a) != 1) { //
// something's wrong
}
n=3*a; //3*a numbers in the file
N = malloc(3 * a * sizeof(int));
for(i = 0; i <n;++i) {
int result=fscanf(fp, "%d", &N[i] );
}
fclose(fp);
for(j=0;j<3*a;j++){
k[j]=N[j];
}
printf("%d",k[0]);
}
When I print k[0] it was supposed to print "1" but instead the whole line "110" is printed
Is there any other way to do this???
The format specifier %d does not specify a length, so fscanf will read as many digits as it can; this is why you get 110 instead of just 1.
If you specify a length, like %1d, it will only read as many digits as you tell it to:
for(i = 0; i <n;++i) {
int result=fscanf(fp, "%1d", &N[i] );
}
When you use fscanf with %d format parameter, it retrieves an integer type from the file. Since 110 and the others are all integers, it will directly fetch 110 from file.
So you can either use fscanf with %d parameters in a loop which iterates for a times, or if you want to get it character by character, you can use fscanf with %c parameter but it needs much more effort. So, you should use fscanf with %d parameter and fetch all digits from it by a loop for every number.
The fscanf(fp, "%d", &N[i] ) will catch a number and not a digit. So
fscanf(fp, "%d", &N[0] ) //will catch 110
fscanf(fp, "%d", &N[1] ) //will catch 220
...
If you want to catch digits in your array you have to use the following code:
for(i = 0; i <n;++i) {
int result=fscanf(fp, "%c", &N[i] );
if (isdigit (N[i])) N[i]-='0';
else i--;
}
All night I have been looking for answers for this & haven't slept, yet, I can't make it work...
I have a binary file and I want to read it and take the value of each integer...
Here is some code:
FILE *f;
char ch;
char t1[3];
int l, c, grayScale, i, j;
int p =0;
f = fopen(pgm, "rb");
(...)
c = 0;
l = 0;
fscanf(f, "%d", &c);
fscanf(f, "%d", &l);
fscanf(f, "%d", &grayScale);
A = alocar_memoria_matriz(l,c);
for(i = 0; i<l; i++){
for(j=0; j<c; j++){
if(fread(&p,sizeof(int),1,f) !=1){
fprintf(stderr, "\nError!\n");
fclose(f);
free_memoria_matriz(A,l);
return NULL;
}else if(p>grayScale|| p<0){
fprintf(stderr, "\nError!\n");
fclose(f);
free_memoria_matriz(A,l);
return NULL;
}else{
A[i][j] = (tdf_elemento) p;
}
}
}
I did some printf of that p (with %d and %i) and I get values like -1161602550, 1213357911, 994462027...
I really don't know what's going on, I also tried to change p for char[5] but didn't work so far...
Thank you in advance...
The "binary" open mode does not change the way fscanf parses the file. Its only effect is to (perhaps) prevent conversion of \r\n line endings to \n as the file is read by stdio facilities.
To read from a stdio file f into an integer i, you can do fread( & i, sizeof i, 1, f ). But note that this does not respect endianness. Without explicit byte-swapping your binary format will be platform-dependent on endianness.
I currently have code that reads 4 lines and I want to be able to change that until EOF or my MAX const int value. I can not get the !EOF to work right and was wondering how would I change my code to accomplish this?
Thanks in advance
#include <stdio.h>
struct record{
char name[2];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
int main(void)
{
struct record jobs[MAX];
int i = 0;
int j;
FILE *f = fopen("data.dat","rb");
while (fscanf(f, "%s %d %d %d", &jobs[i].name, &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority) == 4 && i < MAX)
i++;
for (j = 0; j < i; j++)
printf("%s %d %d %d\n", jobs[j].name, jobs[j].arrival_time,
jobs[j].job_length, jobs[j].job_priority);
fclose(f);
return(0);
}
Something like
while (fscanf(f, " %s ", &etc) != EOF) {
}
Then use feof(f) to check if it was a fscanf error or actually EOF.
Your code seems to do what you want, except:
char name[2];
Names will probably be longer than 1 character.
FILE *f = fopen("data.dat","rb");
You seem to be reading text ("r") file, not binary ("rb").
&jobs[i].name should be jobs[i].name
You need to change the order of the tests in your while() loop - you must test i < MAX before calling fscanf(), or else you'll potentially call it one too many times (you should also be passing jobs[i].name without the & to fscanf):
while (i < MAX && fscanf(f, "%s %d %d %d", jobs[i].name, &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority) == 4)
Personnaly, I would code like this:
for(i=0 ; i<MAX ; ++i) {
fscanf(f, "%s %d %d %d", &jobs[i].name, &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority);
if(ferror(f) || feof(f)) break;
}
The key point is that, at the best of my knowledge, you cannot know that a file is come to end without trying to read it. That is the reason why I check feof() and ferror() after having read data.
At the end of the loop, the variable i contains the number of read data