3

This is a homework assignment for my C Programming class.

I am given a text file, with two data columns; the first column is the age; the second column is the avgprice. I'm able to read and print the values fine. However, for some reason the age and avgprice are flipped in the output. I have no clue why.

Here is the code

#include "stdafx.h"
#include <stdio.h>

int main() {

double age, avgprice; //age = 1st column, avgprice = 2nd column
FILE *corolla; //ptr for file
char eof; //needed for end of file check

corolla = fopen("C:/Users/Nate/Downloads/DataFiles/corolla.txt", "r");
if (corolla == NULL) { //makes sure the file exists
    printf("File does not exist!\n");
    return 0; //prevents crashing
}
else {
    printf("Age \t\t Average Price\n"); //header for data when printed
    /*prints values until we're at the end of the file*/
    while (fscanf(corolla, "%c", &eof) != EOF) {
        fscanf(corolla, "%lf %lf", &age, &avgprice); //scans in data from file
        printf("%.1f \t\t $%.2f\n", age, avgprice); //prints data from file
    }
}
fclose(corolla); //closes file
return 0;
}

This is what the output looks like

bad_output

It's puzzling to me because I have used this exact format to do the same thing with other data files--no issues. For some reason, this one file is having difficulty.

Here is the datafile I'm supposed to be reading. I've uploaded it to my Dropbox that way you can inspect the formatting if necessary. Corolla.txt

Nxt3
  • 1,970
  • 4
  • 30
  • 52
  • 1
    You get a +1 for being honest that it is homework and you have posted code. – Ed Heal Mar 09 '14 at 19:55
  • Your first `fscanf` swallows the "1". After that, your input is wrong, becausen `fscanf` does not know about new-lines. (Well, it does, but it treats them like any other space.) – M Oehm Mar 09 '14 at 19:56
  • See e.g. for how to use fscanf http://stackoverflow.com/a/3351926/297323 – Fredrik Pihl Mar 09 '14 at 19:59

2 Answers2

1

Your input file uses a line-based format. fscanf reads the input chunk by chunk. A chunk is usually something separated by white space, which can be space, tabs or even the new-line. Therefore fscanf is not suited to read line-based formats.

In my opinion, it is better to read the input in two steps: first, read a line with fgets, then read the data from that line with sscanf. For example:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    FILE *f;
    int line = 0;

    f = fopen("kk", "r");
    if (f == NULL) {
        printf("File does not exist!\n");
        return 0;
    }
    printf("%20s%20s\n", "age", "avg. price ($)");

    for (;;) {
        char buffer[80];
        int age, price;

        if (fgets(buffer, sizeof(buffer), f) == NULL) break;
        line++;

        if (sscanf(buffer, "%d %d", &age, &price) < 2) {
            printf("(Skipping bad input in line %d).\n", line);
        } else {
            printf("%20d%20d\n", age, price);
        }
    }
    fclose(f);

    return 0;
}

This also gives you a kind of low-level error reporting.

Also, there's usually no need to do extra checking for EOF. The file input functions return a special value when the end of the file is reached. fscanf and getc return EOF; fgets returns NULL. It is usually always better to stop reading based on these return values.

In your case, the fscanf("%c", &oef) eats up the first character in your file, the digit 1. Luckily, after that it only feeds on new-line so your input doesn't get tripped up worse. (But change your scan format to "%lf %lf " for a drastic price reduction.)

M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • I'd consider using `sizeof buffer` instead of `100` , that would help avoid buffer overflows...:) Also, if the `fgets` does not consume the whole line, then the code should discard the remainder of that line before going onto the next loop iteration. – M.M Mar 09 '14 at 20:19
  • @MOehm, as much as I would like to do that--that's not what my professor wants us to use. He wants us to use `fscanf` for the sake of this assignment. We haven't started learning arrays and buffers yet. – Nxt3 Mar 09 '14 at 20:26
  • @MattMcNabb: How is 100 a bad estimate for a buffer size of 80? (Fixed that, with a slight blush.) You're also right about overlong lines being treated as several lines. I'll leave that as is for now for the sake of simplicity. – M Oehm Mar 09 '14 at 20:29
  • @Nate: Okay, didn't know that. Anyway, Matt McNabb has an answer for you then. – M Oehm Mar 09 '14 at 20:35
  • I appreciate your answer though! Sorry I didn't clarify in the OP. – Nxt3 Mar 09 '14 at 20:42
1

This line:

while (fscanf(corolla, "%c", &eof) != EOF)

reads a character from the file. The first character in the file is 1 so it reads that 1 into eof.

Your next line is:

fscanf(corolla, "%lf %lf", &age, &avgprice); 

which reads the next two entries from the file, which are 13990 and 2, in that order. So the first age is 13990 and the first avgprice is 2.

After that, the file pointer is now pointing to the blank space after the 2. When you go:

fscanf(corolla, "%c", &eof)

it reads a space into eof.

Then when you get to:

fscanf(corolla, "%lf %lf", &age, &avgprice); 

It reads the next two values, 13495 and 3 respectively. And so on.

To fix this you should stop doing fscanf(corolla, "%c", &eof). I don't know what you are expecting this to do exactly , but it does not test whether you're at the end of the file or not. Instead it reads a character, ignores the character, and checks the return value of fscanf.

To fix your code:

while (2 == fscanf(corolla, "%lf %lf", &age, &avgprice))
{
    printf("%.1f \t\t $%.2f\n", age, avgprice); //prints data from file
}

The return value of fscanf is the number of items successfully read (if it succeeded). When it returns something other than 2 you know you must have hit the end of the file.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • NB. as M Oehm points out, this method of reading a file cannot detect if you have a line with a number of items on it that is not 2 , and it can't skip over mistakes in the input file, etc. If you want to handle a wider variety of input then you will need to do something more complicated, such as the approach he suggests. – M.M Mar 09 '14 at 20:18
  • I get this and this makes more sense. What I don't get is that I was able to use this code: [Code](http://pastebin.com/RCgKAV9p), and it printed this data fine: [Data](http://pastebin.com/UKnYeeHA) – Nxt3 Mar 09 '14 at 21:14