0

I'm working on a program that records students grades. The user inputs first name, last name, student ID, grade, and assignment name. I'm supposed to have the input from the user separated by being tabbed rather than a space. But when I execute the program the output in the txt file isn't correct.

#include <stdio.h>

int main(void){

   FILE *cfPtr; // cfPtr = clients.txt file pointer

// fopen opens file. Exit program if unable to create file
if ((cfPtr = fopen("grades.txt", "w")) == NULL) {
    puts("File could not be opened.");
} 
else {
    puts("Enter the students first & last name, ID, grade, and assignment.");

    puts("Enter EOF to end input.");
    printf("%s", "? ");

    char firstName[30]; // student first name
    char lastName[30]; // student last name
    unsigned int stuID; // student ID
    double grade; // student grade
    char assignment[10]; // student assignment name

    scanf("%5s%5s%d%lf%5s", firstName, lastName, &stuID, &grade, assignment);

    // write first & last name, student ID, grade, and assignment name into file with fprintf

    while(!feof(stdin)) {
        fprintf(cfPtr, "%s\t%s\t%d\t%.2f\t%s\t\n", firstName, lastName, stuID, grade, assignment);
        fflush(cfPtr);
        printf("%s", "? ");
        scanf("%5s%5s%d%lf%5s", firstName, lastName, &stuID, &grade, assignment);
    } // end while

    fclose(cfPtr); // fclose closes file
   } // end else
} // end main

Heres what happens when I execute it:

Enter the students first & last name, ID, grade, and assignment.

Enter EOF to end input.

? Freddy Krueger 1234 22.33 test1

? ? Jason Vorhees 1235 33.00 test1

? ? The Hulk 1236 2.95 test1

? Bat Man 1237 100.00 test1

? ^Z

[1]+ Stopped ./seqw


grade.txt file output:

Fredd y 0 0.00 ttime

Krueg er 1234 22.33 test1

Jason Vorhe 1234 22.33 test1

es 1235 33 0.00 test1

The Hulk 1236 2.95 test1

Bat Man 1237 100.00 test1

What am I doing wrong here? And where did ttime come from? And it's strange how it's tabbing part of the last name and making it another input.

  • I would start by checking what `scanf` is returning. If you didn't scan the right number of items you should stop there. You should also test your input for when yo stop, not after the fact with `!feof`. https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong – Retired Ninja Nov 19 '18 at 23:01
  • 3
    Why are you restricting the string inputs to 5 characters? What do you think happens to the rest of the truncated strings? – Weather Vane Nov 19 '18 at 23:06
  • 1
    You have 30-character variables, but only scan 5 characters – stark Nov 19 '18 at 23:11
  • 2
    Please see [Why is `while ( !feof (file) )` always wrong?](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong). You should always check the return value from `scanf` function family, and an idiomatic way to deal with both problems is `while (scanf(...) == 5) { ... }` – Weather Vane Nov 19 '18 at 23:11
  • Your **scanf** statement should look like `scanf("%30s%30s%u%d%10s", ...)`. – eapetcho Nov 20 '18 at 04:13
  • regarding: `scanf("%5s%5s%d%lf%5s", firstName, lastName, &stuID, &grade, assignment);` 1) always check the returned value (not the parameter values) to assure the operation was successful int this case the any returned value other than 5 is an error. 2) the format string: `%5s%5s%d%lf%5s` the MAX CHARACTERS modifier on each of the '%s' input format specifiers should have a modifier that is 1 less than the length of the related input buffer (because that format specifier always appends a NUL byte to the input) to avoid any possibility of a buffer overflow and the resulting undefined behavior – user3629249 Nov 20 '18 at 06:49
  • @eapetcho, No, the format string should NOT be the string you gave. Because the '%s' input format specifier always appends a NUL byte to the input buffer, so the 'MAX CHARACTERS modifier should be 1 less than the length of the input buffer – user3629249 Nov 20 '18 at 06:52

1 Answers1

0

the following proposed code:

  1. cleanly compiles
  2. performs the desired functionality
  3. properly checks for errors
  4. eliminates the 'magic' numbers by using #define statements to give those magic numbers meaningful names
  5. The call scanf() could be modified to incorporate the MAX CHARACTERS modifier(s) to be part of the parameter list, rather than hardcoded into the format string

And now, the proposed code:

#include <stdio.h>
#include <stdlib.h>   // exit(), EXIT_FAILURE

#define MAX_FIRSTNAME_LEN 30
#define MAX_LASTNAME_LEN  30
#define MAX_ASSIGNMENT_LEN 10


int main( void )
{
    FILE *cfPtr; // cfPtr = clients.txt file pointer

    // fopen opens file. Exit program if unable to create file
    if ((cfPtr = fopen("grades.txt", "w")) == NULL) 
    {
        perror( "fopen to read grades.txt failed" );
        exit( EXIT_FAILURE );
    } 
    // implied else, fopen successful

    puts("Enter the students first & last name, ID, grade, and assignment.");
    puts("Enter EOF to end input.");
    printf( "%s", "? " );

    char firstName[ MAX_FIRSTNAME_LEN ]; // student first name
    char lastName[ MAX_LASTNAME_LEN ]; // student last name
    unsigned int stuID; // student ID
    double grade; // student grade
    char assignment[ MAX_ASSIGNMENT_LEN ]; // student assignment name

    while( scanf( "%29s %29s %u %lf %9s", firstName, lastName, &stuID, &grade, assignment) == 5 )
    {
        fprintf( cfPtr, 
                 "%s\t%s\t%d\t%.2f\t%s\t\n", 
                 firstName, 
                 lastName, 
                 stuID, 
                 grade, 
                 assignment);
        printf( "%s", "? " );
    } // end while

    fclose( cfPtr ); // fclose closes file
} // end main
user3629249
  • 16,402
  • 1
  • 16
  • 17