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");
}
Related
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.
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.
I created a file and filled it with some entries. However, I want to read this file and show it on the screen. Also, after showing the entries, I want it to be deleted with my permission. But I am stuck at this point please help me.
EDIT: Code is updated but still couldn't figure it out how to do :/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char name[20], surname[20], city[30], country[30], gender[15];
int count = 0;
int main() {
FILE *f1;
f1 = fopen("C:\\FurkanArslan.txt", "r+");
while (count < 10) { // every step provides 5 new data, so 5*10 will provide 50 data in total.
printf("\n*Please enter required information: \n");
printf("Name :"); scanf("%s", name);
printf("Surname:"); scanf("%s", surname);
printf("Country:"); scanf("%s", country);
printf("City :"); scanf("%s", city);
printf("Gender :"); scanf("%s", gender);
fprintf(f1, " %s | %s | %s | %s | %s\n\n", name, surname, gender, city, country);
count++;
}
fclose(f1);
printf("\n<<<<<%d data has been successfully saved!>>>> \n", count * 5);
printf("-------------------------------------\n");
f1 = fopen("C:\\FurkanArslan.txt", "r");
char c, answer;
while ((c = fgetc(f1)) != EOF)
putchar(c); // In this part I displayed file on the screen.
printf("\n\n <<<< %d entries are displayed on the screen! >>>>", count * 5);
printf("\n\nWould you like to remove your file [Y/N] ?");
scanf(" %c", &answer);
if (answer == 'y' || answer == 'Y') {
remove("f1");
printf("\n\n***File successfully removed!");
}
return 0;
}
In order to show the content of a file you have to open it and read it letter by letter, after that, you can use the putchar function to output the current character
FILE *fp = fopen("path/to/file.txt","r");
char c;
while((c=fgetc(fp))!=EOF)
putchar(c);
fclose(fp);
after that to remove a file you need to use the remove function, which receives the name of the file as paramter.
remove("my_file.txt");
There are multiple issues in your code:
there is no need to make the variables and arrays global, just define them in the body of the main() function.
you should tell scanf() the maximum number of characters to store in the destination array with a length specifier in the format string (eg: "%19s") and check for conversion success.
the variable c used in the reading loop must have type int for proper detection of EOF. fgetc() returns a positive byte value if successful and the special negative value EOF at end of file.
you do not need to reopen the file after writing to it. Sine you opened it for update mode, you can just seek back to the beginning of the file with rewind(f1) or fseek(f1, 0L, SEEK_SET).
the file is open for read and update mode ("r+"): it will fail if the file does not exist. You should open it in write and update mode with "w+" to create or truncate it.
you should check that fopen succeeds at opening the file, otherwise you invoke undefined behavior passing a null stream pointer to fprintf.
to remove the file, remove() takes the filename as its argument. You must close the file before attempting to remove it.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char *filename = "C:\\FurkanArslan.txt";
char name[20], surname[20], city[30], country[30], gender[15];
int count = 0;
FILE *f1 = fopen(filename, "w+");
if (f1 == NULL) {
printf("Cannot open file %s.\n", filename);
return 1;
}
while (count < 10) { // every step provides 5 new data, so 5*10 will provide 50 data in total.
printf("\n*Please enter required information: \n");
printf("Name :"); if (scanf("%19s", name) != 1) break;
printf("Surname:"); if (scanf("%19s", surname) != 1) break;
printf("Country:"); if (scanf("%29s", country) != 1) break;
printf("City :"); if (scanf("%29s", city) != 1) break;
printf("Gender :"); if (scanf("%14s", gender) != 1) break;
fprintf(f1, " %s | %s | %s | %s | %s\n\n", name, surname, gender, city, country);
count++;
}
printf("\n<<<<< %d data has been successfully saved to %s! >>>>\n",
count * 5, filename);
printf("-------------------------------------\n");
rewind(f1);
int c;
while ((c = fgetc(f1)) != EOF)
putchar(c);
printf("\n\n <<<< %d entries are displayed on the screen! >>>>\n", count);
fclose(f1);
printf("\nWould you like to remove your file [Y/N] ?");
char answer;
if (scanf(" %c", &answer) == 1 && (answer == 'y' || answer == 'Y')) {
if (remove(filename)) {
printf("\n\n***Error removing file %s: %s\n",
filename, strerror(errno));
} else {
printf("\n\n***File %s successfully removed!\n", filename);
}
}
return 0;
}
In this program, I am attempting to read a name and their GPA from a file. The first name is Audrey, then a white space, and then 3.6.
The second line is Oakley, then a white space, and 3.5.
int main()
{
FILE * fPtr;
FILE * fPtr2;
char x[10] = { "Oakley " };
double y;
int z;
fPtr = fopen("data.txt", "r");
if (fPtr == NULL) // open failure
puts("File open for read failed");
else
{
while (scanf("%d", &z) != EOF)
{
fscanf(fPtr, "%s", x);
fscanf(fPtr, "%lf", &y);
fprintf(stdout, "Value read = %s\n", x);
fprintf(stdout, "GPA = %lf \n", y);
}
}
fclose(fPtr);
system("pause");
return 0;
}
So, I tried this once before and it worked. In that attempt, "x[10] = Audrey" and this was the first name in the list. It worked, and the fscanf gave me her GPA. The second time, I tried scanning for Oakley and I still get Audrey, but when I remove that line I get a really large negative number.
I used fscanf because it tokenizes around whitespace, so my theory is that if the cursor gets to the proper name then it will read the next number and that will be the GPA? Right? How do I get it to search for Oakley?
You need check scanf for any errors, which could happen because the input file does not match the format you specified. Try these changes:
char user[100];
while (scanf("%s", user) == 1) {
while (fscanf(fPtr, "%s %lf", x, &y) == 2)
{
if (strcmp(user, x) == 0) {
fprintf(stdout, "GPA for %s is %lf \n", user, y);
break;
}
}
rewind(fPtr);
}
Also, fPtr2 is is uninitialized in your code, remove the line fclose(fPtr2).
My code keeps closing before reading the file, I made a comment where it closes. Does anyone know why it wont work? I showed it to my lecturer but she couldn't figure it out and then she had to leave so I was wondering if anyone here could figure it out!
#include <stdio.h>
#include <stdlib.h>
#define RESULT_MAX = 100;
#define RESULT_MIN = 0;
int main()
{
int studentId;
char firstName[20];
char lastName[20];
int result;
FILE *fPtr;
if ((fPtr = fopen("student.txt", "w")) == NULL)
{
printf("File could not be opened\n");
//exit(0);
}
else
{
printf("Enter the Id, first name, last name and result\n");
scanf("%d %s %s %d", &studentId, firstName, lastName, &result);
while(!feof(stdin) )
{
fprintf(fPtr, "%d %s %s %d\n", studentId, firstName, lastName, result);
scanf("%d %s %s %d", &studentId, firstName, lastName, &result);
}
fclose(fPtr);
}//else end
// MY PROGRAM ENDS HERE AND WONT CONTINUE!
if ((fPtr = fopen("student.txt", "r")) == NULL)
{
printf("File could not be opened\n");
//exit(0);
}
else
{
printf("Id, first name, last name, result ");
fscanf(fPtr, "%d %s %s %d", &studentId, firstName, lastName, &result);
while(!feof(fPtr) )
{
printf("%d %s %s %d \n", studentId, firstName, lastName, result);
fscanf(fPtr, "%d %s %s %d", &studentId, firstName, lastName, &result);
}//end while
fclose( fPtr );
}//end if
}
the problem is actually the line: 'while(!feof(stdin) )'
because feof() only becomes valid when the program
tries to read past the end of the 'stdin' file.
This is something that cannot be accomplished
suggest
modifying the program to:
1) read into a local buffer[] array, in a loop,
2) using fgets() as the loop control
3) have a leading 'q' (or similar) as an indication of having read all input
4) output a prompt for every input line
5) if going to parse the fields, parse them using
strtok()/atoi() strncpy() strncpy() atoi()
or perhaps
sscanf()
6) check first char of input buffer[] for the end marker (the 'q' above)
to exit the input loop before parsing
there is a similar problem with the
loop reading the file
I.E. do not use feof() for loop control
rather use fgets()
to differentiate each line of input written to the file, modify
the fprintf(fPtr, ... ) format string to include a trailing '\n'