I want to write and read some statics from file to structs.
write from structs to file is work of but write data from file isn't ok.
I will be grateful to help me what is the problem.
#include <stdio.h>
#include <stdlib.h>
const char* MemberFormatIn="(%[^,], %[^,], %[^,], %d)";
const char* MemberFormatOut="(%s, %s, %s, %d)\n";
typedef struct member {
char name[20],
lastName[20],
address[20];
int age;
}p1,p2;
void inputMemberList(struct member p[],int count) {
printf("name:");
scanf("%s",p[count].name);
printf("lastName:");
scanf("%s",p[count].lastName);
printf("address:");
scanf("%s",p[count].address);
printf("age:");
scanf("%d",&p[count].age);
}
void printMemberList(struct member p[],int count) {
system("cls");
for(int i=0; i<count; i++) {
printf("\aMember %d:\n", i+1);
printf("Name is: %s", p[i].name);
printf("\nLast name is: %s", p[i].lastName);
printf("\nAddress is: %s", p[i].address);
printf("\nAge is: %d", p[i].age);
printf("\n\n");
}
}
void saveMember(struct member p[],int count) {
FILE* fp = fopen("member.txt","a+");
if (fp == NULL) {
printf("File can't open.");
exit(1);
}
fprintf(fp, MemberFormatOut, p[count].name, p[count].lastName, p[count].address, p[count].age);
fclose(fp);
}
void fileToStructure(struct member p[],int count) {
FILE* fp = fopen("member.txt","a+");
if (fp == NULL) {
printf("File can't open.");
exit(1);
}
fseek(fp, 0, SEEK_SET);
for (int i=0; i<count+1; i++) {
fscanf(fp, MemberFormatIn, p[i].name, p[i].lastName, p[i].address, &p[i].age);
}
fclose(fp);
}
int numberOfMember() {
int count = 0;
char c;
FILE* fp = fopen("member.txt","r");
if(fp == NULL) {
printf("File can't open.");
exit(1);
}
do {
c = fgetc(fp);
if(c == '\n')
count++;
}while(c != EOF);
fclose(fp);
return count;
}
int main() {
int len = 100;
p1 p[len];
int count = numberOfMember();
inputMemberList(p,count);
saveMember(p,count);
fileToStructure(p,count);
printMemberList(p,count);
return 0;
}
at result just statics of member true and shown show but the others doesn't true.
File data(example):
(ahmad, dad, tir, 12)
(hossein, dad, tiran, 12)
(ali, dad, tir, 15)
(mohammadi, mmad, tiron, 16)
(helma, dad, tiran, 5)
(mohammad, amin, dadkhah, 5)
output(example):
Member 1:
Name is: ahmad
Last name is: dad
Address is: tir
Age is: 12
Member 2:
Name is: ├wöΩ\`
Last name is:
Address is: ↑
Age is: 0
Member 3:
Name is: Ä
Last name is: t
Address is: e
Age is: 7471221
Member 4:
Name is: r
Last name is: l
Address is: o
Age is: 6881396
Member 5:
Name is: n
Last name is: s
Address is:
Age is: 0
Member 6:
Name is:
Last name is:
Address is:
Age is: 0
In fileToStructure(), the first iteration of the loop calls fscanf() and if all is OK, the next character to be read from the file will be the newline character at the end of the first line. Unfortunately for the next iteration, the next call to fscanf() is expecting to read the ( character, but instead it reads the newline character. This will cause this call to fscanf() and all the subsequent calls to fail to match anything and return EOF.
The code needs to eat the newline character. One way to do that is to change the MemberFormatIn format string to one of the following:
const char* MemberFormatIn=" (%[^,], %[^,], %[^,], %d)"; - will discard any whitespace characters before the ( character; or
const char* MemberFormatIn="(%[^,], %[^,], %[^,], %d) "; - will discard any whitespace characters after the ) character.
The first one (discarding whitespace before the ( character) would be preferable for interactive input, but it doesn't matter too much when reading from a file.
Related
I am trying to write a program in C to take the data from the input.txt file and insert it into the record.txt file in the ascending order of the students’ ID.
Content of input.txt:
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
Content of record.txt:
1287 Nancy CS11
1865 Brown CS33
When I run the program, the following data from input.txt is supposed to be inserted into the record.file(valid data with students' ID, name and course):
2012 Bob CS21
1999 Teddy CS35
2001 Eric CS11
Then the content of record.txt file after insertion(ascending order):
1287 Nancy CS11
1865 Brown CS33
1999 Teddy CS35
2001 Eric CS11
2012 Bob CS21
Below is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
typedef struct {
int id;
char name[20];
char course[5];
} Student;
int read_records(Student s[]) {
FILE *fp;
int i=0;
fp = fopen("record.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return 0;
}
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
return i;
}
void write_records(Student s[], int n) {
FILE *fp;
fp = fopen("record.txt", "w");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
for (int i=0; i<n; i++) {
fprintf(fp, "%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
fclose(fp);
}
void insert_records(Student s[], int n) {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
int i=n;
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
write_records(s, i);
printf("Insertion is done.\n");
}
void display_records(Student s[], int n) {
for (int i=0; i<n; i++) {
printf("%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
}
int main() {
int n;
Student s[MAX];
n = read_records(s);
int opt;
while (1) {
printf("1. Insert\n");
printf("2. Display\n");
printf("3. Exit\n");
printf("Choose an option: ");
scanf("%d", &opt);
switch(opt) {
case 1: insert_records(s, n);
break;
case 2: display_records(s, n);
break;
case 3: exit(0);
}
}
}
When I run the program, the user will be asked to choose an option.
If I enter 2, it will display the content of record.txt. And it works well, as I expected.
If I enter 1, the insertion should be performed and "Insertion is done" will be printed. However, the program doesn't work as I expected. It just displays nothing.
I am confused about that, and I would like to know how to fix the program.
This loop
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
will run indefinitely1 because when fscanf encounters input it cannot convert, that input is left in the stream. The file will never exhaust.
With the file
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
the first fscanf call will convert 1 "2012" "Bob". The second call and so on will fail to convert CS21 into an integer.
Additionally, see Why is “while( !feof(file) )” always wrong?.
Also note that an unbounded %s specifier in *scanf functions is as dangerous as gets. Use a field-width specifier to limit the amount of data that can be read into your buffers. This should be the maximum allowed string length, and should be at most the size of your buffer minus one, leaving room for the null-terminating byte. (i.e., char s[256]; scanf("%255s", s);).
The solution is to never ignore the return values of the fscanf family of functions. Use these return values to determine how your program should proceed. If fscanf fails to match the expected number of conversions, you must be ready to clean up the input stream. This usually means consuming characters until the end of a line is found.
This is easier said than done, however. As seen above, the way %s skips whitespace (including newline characters) means it can overreach looking for valid characters.
The general suggestion is to avoid scanf/fscanf, and instead read entire lines with fgets and then parse those lines with sscanf (or other tools).
In practice this looks like:
#include <stdio.h>
#include <stdlib.h>
#define INPUT "input.txt"
int main(void)
{
FILE *file = fopen(INPUT, "r");
if (!file) {
perror(INPUT);
return EXIT_FAILURE;
}
struct {
int id;
char name[32];
char course[16];
} student;
char buffer[512];
while (fgets(buffer, sizeof buffer, file)) {
int cv = sscanf(buffer, "%d%31s%15s",
&student.id, student.name, student.course);
if (3 == cv) {
/* three successful conversions, do whatever with `student` */
printf("%s<%d> [%s]\n",
student.name, student.id, student.course);
}
}
fclose(file);
}
With your input.txt file this prints:
Bob<2012> [CS21]
Teddy<1999> [CS35]
Eric<2001> [CS11]
CS12<2011> [CS87]
Once you know sscanf succeeded in parsing the expected number of conversions you can move on to validating the record (e.g., "CS12" is an invalid name, per your examples, and you probably need a way to avoid duplicate entries).
In your code you should only increment i after making sure all these steps are followed.
After you have your merged list of records, you should use qsort to sort the array with a comparison function such as
int student_sort(const void *va, const void *vb)
{
const Student *a = va, *b = vb;
return (a->id > b->id) - (a->id < b->id);
}
before writing the records out.
1. i will eventually overflow, invoking Undefined Behaviour. The outcome of the program after this point cannot be generally reasoned about.
This program should ask you to add member (people) to a struct and print them on a file but after the first for loop just stop working and jump over the name part. I just found that thing that allow you to add space to a string, tried it but no success...
I tried to remove it and it work without any problem so the [^\n] make something go wrong.
What is wrong ?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Staff {
char Surname[100];
char Name[100];
int age;
char spec[100];
int id;
} person;
void write();
void leggi();
void trova();
int main() {
write();
}
void write() {
int i = 0;
int n = 1;
int r;
FILE *fp;
fopen_s(&fp, "index.txt", "w+");
if (fp == NULL) {
printf("Failed to open file\n");
exit(1);
}
fprintf(fp, "%d\n", i);
for (i = 0; i < n; i++) {
printf("Surame:\n");
scanf_s("%[^\n]s", person.Surname, 100);
fprintf(fp, "%s\t\t", person.Surname);
//loop just get over the name part
printf("Name:\n"); //after the first loop
scanf_s("%s", person.Name, 100);
fprintf(fp, "%s\t", person.Name);
printf("Age:\n");
scanf_s("%d", &person.age);
fprintf(fp, "%d\t", person.age);
printf("Specialization\n");
scanf_s("%s", person.spec, 100);
fprintf(fp, "%s\n", person.spec);
printf("Want to enter another? 1=yes 0=no...\n");
scanf_s("%d", &r);
if (r == 1)
n = n + 1;
}
rewind(fp);
fprintf(fp, "%d\n", i);
fclose(fp);
}
There are multiple problems in your code:
you use the so called secure functions fopen_s, scanf_s etc, but you do not check the return values to detect invalid input. You should instead use standard functions, pass the appropriate arguments and check the return values.
using scanf_s is actually non portable: the scanf_s function defined in Annex K of the C Standard requires the length argument after the pointer to have size_t type, whereas the function with the same name in the Microsoft library uses type UINT, which has a different representation on 64-bit versions of their Windows OS. A classical case of the Embrace, enhance and extinguish strategy. In Standard C, one should write: scanf_s("%s", person.Name, (size_t)100) or better:
scanf_s("%s", person.Name, sizeof person.Name)
there is no need to open the output file for update with "w+", just use "w".
you rewind the stream pointer back to the beginning of file and overwrite the number of entries at the start of the file. This works as long as you have less than 10 entries, but beyond that, the number has more digits so some characters in the file will be corrupted. You could use a format with padding such as "%6d\n" which would allow for up to 1 million records without risks.
"%[^\n]s" is not a correct scanf format: you should just write "%[^\n]" or better " %99[^\n]" to skip initial white space and limit the input to 99 characters.
Here is a modified version:
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Staff {
char Surname[100];
char Name[100];
int age;
char spec[100];
int id;
};
void write(void);
void leggi(void);
void trova(void);
int main() {
write();
}
int flush_input(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}
void write(void) {
int n = 0;
int r;
FILE *fp = fopen("index.txt", "w");
if (fp == NULL) {
fprintf("Failed to open file index.txt: %s\n",
strerror(errno));
exit(1);
}
fprintf(fp, "%6d\n", n);
for (;;) {
struct Staff person = { 0 };
printf("Surname:\n");
if (scanf(" %99[^\n]", person.Surname) != 1)
break;
flush_input();
fprintf(fp, "%s\t\t", person.Surname);
//loop just get over the name part
printf("Name:\n"); //after the first loop
scanf(" %99[^\n]", person.Name);
flush_input();
fprintf(fp, "%s\t", person.Name);
printf("Age:\n");
scanf("%d", &person.age);
flush_input();
fprintf(fp, "%d\t", person.age);
printf("Specialization\n");
scanf(" %99[^\n]", person.spec, 100);
flush_input();
fprintf(fp, "%s\n", person.spec);
n++;
printf("Want to enter another? 1=yes 0=no...\n");
if (scanf("%d", &r) != 1 || r != 1) {
flush_input();
break;
}
flush_input();
}
rewind(fp);
// update the entry count on 6 characters
fprintf(fp, "%6d\n", n);
fclose(fp);
}
Change the call of scanf below for entering strings by inserting a space in the beginning of the format string. For example instead of this call
scanf_s("%[^\n]s", person.Surname, 100);
(where the letter s must be removed from the format string) write
scanf_s(" %[^\n]", person.Surname, ( rsize_t )100);
^^^^^^^^
This allows to skip leading white space characters in the input buffer.
Pay attention to that changing the condition or the for loop the was as you are doing
for (i = 0; i < n; i++) {
//...
if (r == 1)
n = n + 1;
}
makes the code unclear. Instead of the for loop you could use do-while loop.
I'm able to write data into a file but when I read it, it prints an empty file. I tried to make 2 programs one to write to file using permission "w" and one to read using "r" but when I combined both programs and changed permission to "w+" printing the files gives lots of empty spaces.
#include <stdio.h>
#include <stdlib.h>
void main()
{
char name[20];
char roll_no[15];
char class[10];
char semester[10];
char course[20];
FILE *file_pointer;
file_pointer = fopen("StudentRecords.txt", "w+");
if (file_pointer == NULL)
{
printf("\nError Opening File StudentRecords.txt\nCreate File Manually and Try Again.");
exit(1);
}
printf("\nENTER DETAILS FOR 5 STUDENTS\n");
//TAKE 5 RECORDS FROM USERS AND SAVE THEM IN FILE-
for (int i = 1; i <= 5; i++)
{
printf("\nStudent %d", i);
fprintf(file_pointer, "Student %d", i);
printf("\nName : ");
scanf("%s", &name);
fprintf(file_pointer, "\nName : %s", name);
printf("Roll No : ");
scanf("%s", &roll_no);
fprintf(file_pointer, "\nRoll No : %s", roll_no);
printf("Class : ");
scanf("%s", &class);
fprintf(file_pointer, "\nClass : %s", class);
printf("Semester : ");
scanf("%s", &semester);
fprintf(file_pointer, "\nSemester : %s", semester);
printf("Course : ");
scanf("%s", course);
fprintf(file_pointer, "\nCourse : %s", course);
printf("\n");
fprintf(file_pointer, "\n\n");
}
//READ ENTIRE FILE WORD BY WORD
char c;
c = fgetc(file_pointer);
while (c != EOF)
{
printf("%c", c);
c = fgetc(file_pointer);
}
fclose(file_pointer);
}
When you write/read in a file you use a stream (FILE*) that stores where in the file you are (position).
When you finished writting that was the last stream position.
You need to go back to the begin of the file if you want to read it entirely.
You can use fseek for this.
int fseek(FILE *stream, long int offset, int whence);
1 SEEK_SET : Beginning of file
2 SEEK_CUR : Current position of the file pointer
3 SEEK_END : End of file
...
//READ ENTIRE FILE WORD BY WORD
char c;
fseek(file_pointer, 0, SEEK_SET);
c = fgetc(file_pointer);
while (c != EOF)
{
printf("%c", c);
c = fgetc(file_pointer);
}
For the read section do this instead. This will reset the position indicator to the beginning of the file so you can read from the beginning.
if ( fseek(file_pointer, 0L, SEEK_SET) == 0 ) {
char c;
c = fgetc(file_pointer);
while (c != EOF)
{
printf("%c", c);
c = fgetc(file_pointer);
}
}
These are 2 separate applications.
In the first one, I tried to store employee details like name, age and salary in the binary file named emp.bin.
In the second application, I tried to view the contents of the file but in place of the name, only the first character appears.
I tried printing each character separately, and it turns out that there's 3 null characters '\n' after each letter in the name that is why it is not printing after the first character.
"Write" application code:
//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>
int main()
{
FILE *fp;
char another = 'Y';
struct emp
{
char name[40];
int age;
float bs;
};
struct emp e;
fp = fopen("emp.bin", "wb");
if (fp == NULL)
{
puts("Cannot open the file.");
return 1;
}
while(another == 'Y')
{
printf("Enter the employee name, age and salary: ");
scanf("%S %d %f", e.name, &e.age, &e.bs);
while(getchar() != '\n');
fwrite(&e, sizeof(e), 1, fp);
printf("Add another record? (Y/N)");
another = getchar();
}
fclose(fp);
return 0;
}
"Read" application code:
//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>
int main()
{
FILE *fp;
struct emp
{
char name[40];
int age;
float bs;
} e;
fp = fopen("emp.bin", "rb");
if (fp == NULL)
{
puts("Cannot open the file.");
return 1;
}
while (fread(&e, sizeof(e), 1, fp) == 1)
{
printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
}
fclose(fp);
}
Here's the input and output:
How can I correct this code to make it print the whole name?
The problem is in the "writer" application, even before the actual write is performed.
When you get data from the user
scanf("%S %d %f", e.name, &e.age, &e.bs);
you use format %S (capital letter "S". Format specifiers are case sensitive!). As we can read in the printf man page
S
(Not in C99, but in SUSv2.) Synonym for ls. Don't use.
this leads us to %ls format specifier that is described in the following way
s
[...] If an l modifier is present: The const wchar_t * argument is expected to be a pointer to an array of wide characters. Wide characters from the array are converted to multibyte characters
Talking about Windows source we have:
S
Opposite-size character string, up to first white-space character (space, tab or newline). [...]
When used with scanf functions, signifies wide-character array; when used with wscanf functions, signifies single-byte-character array [...]
So, basically, you are reading characters from stdin and converting them to wide chars. In this case every character takes sizeof(wchar_t). Probably in your system this size is 4.
What you need is simply %s format specifier. And since your name array has size 40, I suggest using
scanf("%39s", e.name );
to get the name from user. In this way up to 39 characters will be written, being the 40th reserved to the string terminator '\0'.
As noted by Roberto in his answer, the problem is the %S conversion specifier, which is a typo, you should use %s.
Note however that there are other issues which might pose problems:
you should tell scanf() the maximum number of characters to read for the employee name, otherwise scanf() may write beyond the end of the destination array if input is too long.
if both programs run on separate systems with different endianness, the numbers will be incorrect on the receiving end because their bytes will be stored in the opposite order. For this reason, endianness should be specified and handled explicitly in binary files. Text format tends to be preferred for data transmission.
Here is a modified version:
//Receives records from keyboard and writes them to a file in binary mode
#include <stdio.h>
int main() {
FILE *fp;
char another = 'Y';
struct emp {
char name[40];
int age;
float bs;
} e;
int c;
fp = fopen("emp.bin", "wb");
if (fp == NULL) {
puts("Cannot open the file.");
return 1;
}
while (another == 'Y') {
printf("Enter the employee name, age and salary: ");
if (scanf("%39s %d %f", e.name, &e.age, &e.bs) != 3)
break;
while ((c = getchar()) != EOF && c != '\n')
continue;
if (fwrite(&e, sizeof(e), 1, fp) != 1)
break;
printf("Add another record? (Y/N)");
another = getchar();
}
fclose(fp);
return 0;
}
"Read" application code:
//Read records from binary file and displays them on VDU
#include <stdio.h>
#include <ctype.h>
int main() {
FILE *fp;
struct emp {
char name[40];
int age;
float bs;
} e;
fp = fopen("emp.bin", "rb");
if (fp == NULL) {
puts("Cannot open the file.");
return 1;
}
while (fread(&e, sizeof(e), 1, fp) == 1) {
printf("\n%s \t %d \t $%.2f\n", e.name, e.age, e.bs);
}
fclose(fp);
return 0;
}
student.dat file
----------------
Stu:1 abc ($) - 55 in following order (Stu: %d %s (%c) - %d)
Stu:2 pqr (^) - 82
I am trying to read this file and save highest grade details in the variable in c programming.
my code is below but is not complete!
int main(){
int num, grade;
char id, name[35];
FILE *fp = NULL;
fp = fopen("student.dat", "r");
if (fp != NULL) {
while ((fp != '\n') && (fp != EOF)) {
fscanf(fp, "%d %s %c %d", &num, name, id, &grade);
printf("Student Num: %d", num);
printf("Student Name: %s", name);
printf("Student id: %c", id);
printf("Student grade: %d", grade);
}
fclose(fp);
}else {
printf("Failed to open file\n");
}
}
In C, you have 2 primary ways to read line-oriented input and then parse into individual values (really 3, but we will ignore walking a pair of pointers down the string for now).
The preferred manner is to use a line-oriented input function such as fgets or POSIX getline to read an entire line into a buffer, and then parse the buffer with sscanf which can be done in a more flexible manner than a single call to fscanf.
Nonetheless, you appear dedicated to using fscanf here. The key to using fscanf successfully is to provide a format string that accounts for all characters in the line to be read, or to craft the format string to take advantage of properties of the individual format specifiers to accomplish the same thing (e.g. %s (as well as your numerical conversions) will skip leading whitespace giving you some control to deal with line-endings that would otherwise be left in the input-buffer (either the file or stdin and therefore be the next character available on a subsequent call to fscanf, which if not properly handled, will throw a wrench into your read routine.
Another mandatory step is to validate that all conversions specified were successfully completed during each read. You do that by checking the return value for fscanf which is the match count (a count of the number of successful conversions that took place). If you do not check, you cannot have any type of confidence that your values actually hold the data you think they do.
Putting that together, using your input file, and taking the filename to open as the first argument to the program (and reading by default on stdin if no filename is given), you could do something like the following:
#include <stdio.h>
int main (int argc, char **argv) {
int num =0, grade = 0, max = 0; /* initialize all variables */
char id = 0, name[35];
const char *fmt = " Stu:%d %s (%c) - %d"; /* given format string */
FILE *fp = NULL;
if (!(fp = argc > 1 ? fopen (argv[1], "r") : stdin)) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read each line and validate 4 successful conversions */
while (fscanf (fp, fmt, &num, name, &id, &grade) == 4) {
if (grade > max) max = grade;
printf ("Student Num: %d Name: %-12s id: %c grade: %d\n",
num, name, id, grade);
}
printf ("\n highest grade : %d\n\n", max);
if (fp != stdin) fclose (fp);
return 0;
}
Example Use/Output
$ ./bin/stdntread <dat/stdntread.dat
Student Num: 1 Name: abc id: $ grade: 55
Student Num: 2 Name: pqr id: ^ grade: 82
highest grade : 82
Look over the code, and especially the slight tweak to the format specifier, and let me know if you have any additional questions.
As user3386109 already hinted at: the format string "Stu: %d %s (%c) - %d" should do it. It actually doesn't, you need to add the newline, too.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
int num, grade, ret;
char id, name[35];
int lineno = 1;
FILE *fp = NULL;
// reset errno, just in case
errno = 0;
fp = fopen("student.dat", "r");
if (fp != NULL) {
for (;;) {
ret = fscanf(fp, "Stu: %d %s (%c) - %d\n", &num, name, &id, &grade);
if (ret != 4 && ret != EOF){
fprintf(stderr,"fscanf() returned %d instead of 4 for line %d\n",ret,lineno);
// unlikely, but cheap to check, so check
if(errno != 0){
fprintf(stderr,"With error %s\n",strerror(errno));
}
exit(EXIT_FAILURE);
}
if (ret == EOF) {
// fscanf() returns EOF for end-of-file _and_ error.
// check for error first
if(errno != 0){
fprintf(stderr,"The error %s occured while reading line %d\n",strerror(errno), lineno);
exit(EXIT_FAILURE);
}
// we are done with the file at this point and can bail out graciously
break;
}
printf("Student Num: %d, ", num);
printf("Student Name: %s, ", name);
printf("Student id: %c, ", id);
printf("Student grade: %d\n", grade);
lineno++;
}
fclose(fp);
} else {
printf("Failed to open file: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
File student.dat generated with
for i in `seq 1 1 100`;do character=$(printf \\$(printf '%03o' $((`shuf -i 40-99 -n 1`))));name=$(cat /dev/urandom | tr -dc 'a-zA-Z' | fold -w 3 | head -n 1); echo Stu:$i $name \($character\) - `shuf -i 10-99 -n 1`;done > student.dat
(Yes, that generation can be done simpler, I'm pretty sure ;-) )
First 10 lines of input (new-line is \n everywhere):
Stu:1 qim (+) - 13
Stu:2 EcF (L) - 61
Stu:3 Ko1 (Q) - 50
Stu:4 Ve7 (,) - 23
Stu:5 NiX (;) - 28
Stu:6 4O8 (C) - 73
Stu:7 00m (]) - 79
Stu:8 uiw (C) - 45
Stu:9 47k (X) - 80
Stu:10 MmJ (A) - 38
(file ends with new-line \n!)
Your while loop have incorrect condition, it'll never become false, File pointer never reaches to \n nor EOF, I had modified your code and now its working properly. Check while condition in code
int num, grade;
char id, name[35];
FILE *fp = NULL;
fp = fopen("student.dat", "r");
if (fp != NULL) {
int ret;
while((ret = fscanf(fp, "%d %s %c %d", &num, name, &id, &grade))!=EOF)
{ printf(" Student Num: %d", num);
printf(" Student Name: %s", name);
printf(" Student id: %c", id);
printf(" Student grade: %d\n", grade);
}
fclose(fp);
}else {
printf("Failed to open file\n");
}