0

The scenario goes like this,

A medical center needs to store appointment details in a text file called appointment.dat.

It includes the Patient name and the Appointment type. The appointment types can either be 'Consulting', 'Scanning' or 'Testing'. Only the first letter is stored in the file.

The requirement is to,

  1. Create the appointment.dat file
  2. Get 5 patient details through keyboard input and write the data into the appointment.dat file under the given sample format.
  Dave                C
  Ryan                T
  Mary                S
  George              C
  Julian              S
  1. Read the appointment.dat file and calculate and display the Number of Patients under the given format.
  Appointment Type       Number of patients
  Consulting                     2
  Scanning                       2
  Testing                        1

Here's the code I tried,

#include <stdio.h>
int main()
{
     int cCount, sCount, tCount;
     char name, chan, C, S, T, c, s, t;

     chan = " ";
     cCount = sCount = tCount = 0;

     FILE *cPtr;
     cPtr = fopen ("appointment.dat", "r");

     while (!feof(cPtr))
     {
          fscan (cPtr, "%s", &chan);

          if (chan == C)
               cCount++;

          else if (chan == S)
               sCount++;

          else if (chan == T)
               tCount++;
     }

     printf ("Appointment Type       Number of patients\n");
     printf ("Consulting                     %d        \n");
     printf ("Scanning                       %d        \n");
     printf ("Testing                        %d        \n");

     return 0;
}

I'm having trouble getting the displaying part right. The Number of Patients displays some addresses instead of the patient count.

How do I modify the code to get the patient count right?

  • you're checking if chan is equal to C, S, and T which are variables that have been declared but not yet initialized. You also forgot an extra `=` in the first `else if` statement, which you'll also need to fix. – Matthew Follegot Apr 23 '20 at 23:54
  • You will want to look at [**Why is while ( !feof (file) ) always wrong?**](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) – David C. Rankin Apr 24 '20 at 00:07
  • You also forgot to add the second parameter to the `printf()` statements that contain `%d` as a format specifier. I presume that you want the values of your count variables to be displayed there, but if you don't pass them to `printf()` they clearly won't print. I don't see how anything is printing out for you, because there's nothing in the code you posted that would print anything for the three count values at all. In fact, there's an obvious typo in the code you've posted that would keep it from compiling at all, so this clearly isn't your actual code. – Ken White Apr 24 '20 at 00:12
  • @MatthewFollegot I updated the code like you said. But the output is still the same. Could you help me identify where else I went wrong? – Kushani Devendra Apr 24 '20 at 00:23
  • @KenWhite could you please help me correct my code? Can you post a correct version of my code? That would be a great help! – Kushani Devendra Apr 24 '20 at 00:27

1 Answers1

1

You have fallen into one of the first pitfalls most new C-programmers fall into. Why is while ( !feof (file) ) always wrong? On your call to fscan (cPtr, "%s", &chan); (which should be fscanf), for the last line of input, the read succeeds and EOF is not set. You test while (!feof(cPtr)) -- and it's NOT. You loop again and reach fscan (cPtr, "%s", &chan); which now fails due to an input-failure and EOF is returned -- but you blindly proceed to check if (chan) which may fail at this point or may appear to work correctly (adding an additional erroneous count to the variable corresponding to whatever the last value of chan was). 1

You further invoke Undefined Behavior in your use of printf by failing to provide any argument for the "%d" conversion specifier contained in the format string, e.g.

 printf ("Consulting                     %d        \n");

C11 Standard - 7.21.6.1 The fprintf function(p2) (this explains your "The Number of Patients displays some addresses instead of the patient count.")

When you are reading a line-of-input at a time, use a line-oriented input function like fgets() or POSIX getline() to read a complete line into a sufficiently sized array, and then use sscanf() to separate the array into the needed values, e.g.

#define MAXC 128        /* if you need a constant, #define one (or more) */
...
    char buf[MAXC], name[MAXC], type;
    ...
    while (fgets (buf, MAXC, fp)) {                     /* read each line into buf */
        if (sscanf (buf, "%s %c", name, &type) == 2) {  /* split buf into name, type */

You use the return of the read function itself to control the continuation of the read-loop. You cannot use any user-input function correctly without Checking the return.

Now you can check the type. A simple way is to use a switch() statement -- though there is nothing wrong with a sequence of if() statements. You can use a switch() similar to:

            switch (type) {                             /* switch on type */
                case 'C': C++; break;
                case 'S': S++; break;
                case 'T': T++; break;
                default:
                    fprintf (stderr, "error: invalid type '%c'\n", type);
                    break;
            }

Putting it altogether, and allowing the filename to be passed as the first argument to the program (or read from stdin by default if no filename is given), you could do:

#include <stdio.h>

#define MAXC 128        /* if you need a constant, #define one (or more) */

int main (int argc, char **argv) {

    char buf[MAXC], name[MAXC], type;
    size_t C = 0, S = 0, T = 0;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {                     /* read each line into buf */
        if (sscanf (buf, "%s %c", name, &type) == 2) {  /* split buf into name, type */
            switch (type) {                             /* switch on type */
                case 'C': C++; break;
                case 'S': S++; break;
                case 'T': T++; break;
                default:
                    fprintf (stderr, "error: invalid type '%c'\n", type);
                    break;
            }
        }
    }
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

    printf ("Appointment Type       Number of patients\n"   /* output results */
            "Consulting             %9zu\n"
            "Scanning               %9zu\n"
            "Testing                %9zu\n", C, S, T);
}

(note: you only need one printf statement for each contiguous output. The C compiler will concatenate all adjacent whitespace separated strings, e.g. "..." "..." into a single string)

Example Use/Output

Simply providing your input to the program on stdin using a bash heredoc, you would get:

$ cat << eof | ./bin/appointments
> Dave C
> Ryan T
> Mary S
> George C
> Julian S
> eof
Appointment Type       Number of patients
Consulting                     2
Scanning                       2
Testing                        1

Reading From A File

With your data in the file dat/appointment_types.txt, e.g.

$ cat dat/appointment_types.txt
Dave C
Ryan T
Mary S
George C
Julian S

Your use and output would be:

$ ./bin/appointments dat/appointment_types.txt
Appointment Type       Number of patients
Consulting                     2
Scanning                       2
Testing                        1

Look things over and let me know if you have further questions.

Footnotes:

1. The C-Standard does not define what chan will hold after an input-failure occurs. C11 Standard - 7.21.6.2 The fscanf function(p9) The behavior is simply undefined because chan is indeterminate at this point and used while it has an indeterminate value. C11 Standard - J.2 Undefined Behavior "The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8)." and see discussion Undefined, unspecified and implementation-defined behavior and What is indeterminate behavior in C++ ? How is it different from undefined behavior?

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • wow, I can't thank you enough! This was what I really needed! Your instructions and guidance really helped me learn so many things. Most of the questions I post on SO gets rude comments, then ends up getting closed by other people even though I'm desperately seeking for someones help. I'm so glad nobody closed this one. Thank you so much for committing your time to help me with my question. I really appreciate it! – Kushani Devendra Apr 24 '20 at 10:17
  • You are welcome. You will get better at asking questions and your reputation will build. The key is to try and make your questions comply with the StackOverflow rules for [How to Ask a Question](http://stackoverflow.com/questions/how-to-ask) and [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve). Don't take it personally if a question gets closed, if it does it's probably due to one of those two reasons (and you can edit and then ask to re-open -- If I recall I had to edit and fix the formatting, etc..), but I could tell you were trying. Good luck! – David C. Rankin Apr 24 '20 at 12:50