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.