Home > Enterprise >  fscanf repeating characters
fscanf repeating characters

Time:12-19

When data is read from a text file with fscanf, data from the next column is appended to the string data. How can I get only the data from that column?

Input file: student.txt

Donald 23 KOR CE
Mitchell 24 USA EE
John 22 KOR CE

Output:

Donald 23 KORCE CE
Mitchell 24 USAEE EE
John 22 KORCE CE

In the data in the first row, the country should be KOR, but it comes out as KORCE.

My code so far:

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

#define MAX_LINE_LENGTH 100

typedef struct
{
  char name[20];
  int age;
  char country[3];
  char dept[2];
} STUDENT;

int main()
{
  int i = 0;
  STUDENT student[15];

  // Initialize the student array
  for (i = 0; i < 15; i  ) {
    memset(student[i].name, 0, sizeof(student[i].name));
    student[i].age = 0;
    memset(student[i].country, 0, sizeof(student[i].country));
    memset(student[i].dept, 0, sizeof(student[i].dept));
  }

  // Open the file for reading
  FILE *fp = fopen("student.txt", "r");
  if (fp == NULL)
  {
    puts("Error: Could not open file 'student.txt'");
    return 1;
  }

  // Read the file line by line
  char line[MAX_LINE_LENGTH];
  while (fgets(line, MAX_LINE_LENGTH, fp) != NULL) {
    // Parse the line and fill the structure
    if (sscanf(line, "%s %d %s %s\n", student[i].name, &student[i].age, student[i].country, student[i].dept) != 4) {
      printf("Error: Invalid data in file 'student.txt'\n");
      return 1;
    }

    // Print the structure
    printf("Name: %s\n", student[i].name);
    printf("Age: %d\n", student[i].age);
    printf("Country: %s\n", student[i].country);
    printf("Dept: %s\n", student[i].dept);

    i  ;
  }

  // Close the file
  fclose(fp);

  return 0;
}

I want the country name to read normally as 3 characters.

CodePudding user response:

No widths leads to undefined behavior

The data was too large for student[i].country[3], causing a buffer overflow as format lacked a width to prevent that. The width for "%s" needs to be 1 less than the buffer size. Recall the "%s" appends a null character after storing input text. Had OP used widths, the trouble would rapidly have made itself known.

The "\n" at the end of the format serves scant useful purpose.

// sscanf(line, "%s %d %s %s\n", 
sscanf(line, "s %d %2s %1s", 
    student[i].name, &student[i].age, 
    student[i].country, student[i].dept)

Certainly though OP wants larger, by 1, buffers.


Alternative error detection

Use "%n" to save the scanning offset, if it got that far. It also detects if junk text followed the department.

  while (i < 15 && fgets(line, MAX_LINE_LENGTH, fp) != NULL) {
    int n = 0;
    sscanf(line, "s %d %2s %1s &n", 
        student[i].name, &student[i].age, student[i].country,
        student[i].dept, &n);
    if (n == 0 || line[n]) {
      printf("Error: Invalid data <%s> in file 'student.txt'\n", line);
      return 1;
    }
    ...
  }

It is even reasonable to use a width with "%d", like "=" to limit the age range to -99 to 999. This would help catch some pathologic values and prevent overflow.

Input data is evil. Do not trust it until vetted.


Some insights on U. S. names

CodePudding user response:

Thank you for your suggestions. But I'm sorry that the way you suggested didn't solve it. Resizing "country[3]" to "country[10]" solved the problem, but I'm still not sure why.

  • Related