0

I have an issue with my homework where part of it requires scanning and reading some specific input. The related C code is:

typedef struct Train {
    int train_number;
    int priority;   // 0 is low priority, 1 is high priority
    int direction;  // 0 is Westbound, 1 is Eastbound
    float loading_time;
    float crossing_time;
    int loaded;
} Train;

#define MAX_TRAIN_COUNT 777
Train station[MAX_TRAIN_COUNT];

int main(int argc, char* argv[]) {
    //read the input file
    FILE *input;
    char c;
    int train_count = 0;
    input = fopen(argv[1], "r");

    while((c = getc(input)) != EOF) {
        if(c == '\n')
            train_count++;
    }

    int i;
    for (i = 0; i < train_count; i++) {
        char dir;
        int load, cross;
        fscanf(input, "%c %d %d\n", &dir, &load, &cross);
        printf("%c %d %d\n", dir, load, cross);
    }
    fclose(input);

The input is 3 rows consisting of one char and two integers separated by spaces.

e 10 6
W 6 7
E 3 10

The output I get is:

 4195728 0 

Where there is a space before the 4195728. I cannot seem to find a solution to fix this.

What seems to be the problem?

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 3
    Check your io operations instead of assuming they succeed. Assumption is the mother of all.... Anyway, you didn't rewind your file. All those `fscanf` calls are failing, and you don't know because you never bother to check. Unrelated, from what I see the line-counting portion of this is not necessary if you do decide to start doing properly checked IO. – WhozCraig Mar 02 '18 at 09:16
  • In `fscanf(input, "%c %d %d\n", &dir, &load, &cross);` add a space before `%c`. Please see [scanf() leaves the new line char in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer). – Weather Vane Mar 02 '18 at 09:18
  • 1
    It's much more robust to read whole lines using `fgets()`, then use `sscanf()` to parse what you got. Also check the return value of the scanning function, before relying on the values having been scanned. – unwind Mar 02 '18 at 09:19
  • Btw, `int loaded = 0;` really works in regular C as a member decl? I must have been sleeping when that was added to the standard. – WhozCraig Mar 02 '18 at 09:21
  • 1
    Thanks WhozCraig, I didn't know about the rewind thing. Got it working now. About the int loaded = 0; thing, I must have been sleeping lol. – Willytheboy Mar 02 '18 at 09:29
  • BTW: change `char c;` to `int c;` for proper functionality for all input and avoid an infinite loop when `char` is an _unsigned char_. – chux - Reinstate Monica Mar 02 '18 at 15:25
  • @WeatherVane Pre-pending a space before `"%c"` is a good general advice, yet not needed here in this case given the final `"\n"`. `input` needs to be re-wound.and other problems exist too - yet not the usual missing leading space before `%c`. – chux - Reinstate Monica Mar 02 '18 at 15:31

1 Answers1

1

Code fails to rewind the file before reading the data a 2nd time. Lack of checking input function success contributed to not exposing the problem. @WhozCraig.

int i;
rewind(input); // add
for (i = 0; i < train_count; i++) {

  // fscanf(input, "%c %d %d\n", &dir, &load, &cross);
  if (fscanf(input, "%c %d %d\n", &dir, &load, &cross) != 3) Handle_Error();

A better approach would handle reading the lines of the data file in a helper function.

Using fgets() is much better at handling troublesome input than fscanf(). @unwind

int ReadTrainData(Train *data, FILE *stream) {
  char buffer[200];
  if (fgets(buffer, sizeof buffer, stream) == NULL) {
    return EOF;
  }
  // Various ways to parse data.
  // This one look for a completed scan and checks that `n` was changed.
  int n = 0;
  //              v-- add space to consume optional leading white-space (if desired)
  sscanf(buffer, " %c %d %d %n", &data.direction, &data.loading_time,
      &data.crossing_time, &n);
  if (n == 0) return 0; // data incomplete
  if (buffer[n]) return 0; // extra junk in line

  // maybe valid data
  if (strchr("NSEWnsew", &data.direction) == NULL) return 0;
  if (data.loading_time < 0 || data.loading_time > TRAIN_TIME_MAX) return 0;
  // add other validation tests as needed

  return 1;
}

Sample usage

size_t train_count = 0;
if (input) {
  Train data = {0};
  while(ReadTrainData(&data, input) == 1) {
    train_count++;
    printf("%c %d %d\n", data.direction, data.loading_time, data.crossing_time);
  }
  fclose(input);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256