Read and modify txt file (line by line) - c

So there was this light project in one of my past classes where the user would read a text file (lets call it "studentstuff.txt", see below)
*studentstuff.txt*
1
Bob Smith
24
3.5
2
Jill Williams
23
3.6
3
Tom Jones
32
2.4
4
Julie Jackson
21
3.1
5
Al Brown
23
3.35
6
Juan Garcia
22
3.4
-7
Melissa Davis
20
3.2
8
Jack Black
44
1.1
and the output would print out: 1) # of students 2) Average age 3) Average gpa. In this assignment we had a struct:
typedef struct{
int id;
char name[255];
int age;
float gpa;
}student;
According to the program, the "studentstuff.txt" will be read and sorted according to the struct and then after some little math and functions spits out the:
'#' of students:
Average age:
Average gpa:
The problem is I have the idea in my head, but I cant seem to put it into code. Could anyone help me out on this?

as with any programming problem the first action (after deciding the inputs and outputs) is the breaking down of the problem into simple discrete steps.
Such a set of steps for the OPs problem would look similar to:
open the input file
if any errors:
output user message to stderr
exit program, indicating error occurred
else
begin: loop:
input the info for one student
if any errors, except EOF:
output user message to stderr
cleanup by closing the input file
exit program, indicating an error occurred
else
update number of students
update total age
update total gpa
endif
goto top of loop
end loop:
endif
calculate the average age
calculate the average gpa
display number of students
display average student age
display average student gpa
cleanup by closing the input file
return to caller, indicating success
Because the calculations will produce fractions, to avoid problems, suggest the struct be defined as:
struct studentStruct
{
float id;
char name[255];
float age;
float gpa;
};
typedef struct studentStruct student;
Notice the separation of the struct definition from the typedef statement. It does not make any difference here, but will when working with a debugger (which needs the struct tag name to properly display all fields in the struct) and when working with large projects to help avoid confusion.

Related

Unknown test case error in C program

This a question I have to submit for an assignment, hence it has to be evaluated online. My program is running correctly for 6 out of 7 test cases. Only 3 test cases are provided and they are as shown:
Sports or Economy Car
Help Mr.Kamath to check whether his vehicle is an economy car or not. The program should display “There is a gas hog” if the mileage is less than or equal to 15 Km and the program should display “It is an economy car” if the mileage is not less than 30 Km. Otherwise display "Fuel Economy". Write a C program to get the values of a Car variables from the user. Create a structure called Car. Order of the input values entered should be according to the structure variables
struct Car{
float startKm;
float endKm;
float litres;
};
Test Case
Input 1
30
50
5
Output 1
There is a gas hog
Input 2
40.5
80.5
1.5
Output 2
Fuel Economy
Input 3
30
0
5
Output 3
You have entered 0
My code:
#include<stdio.h>
struct Car
{
float startKm;
float endKm;
float litres;
};
int main()
{
struct Car c;
float m;
scanf("%f",&c.startKm);
scanf("%f",&c.endKm);
scanf("%f",&c.litres);
m=(c.endKm-c.startKm)/c.litres;
if(c.startKm<=0||c.endKm<=0||c.litres<=0)
{
printf("You have entered 0"); return 0;
}
else if(m<=15)
{
printf("There is a gas hog");
}
else if(m>=30)
{
printf("It is an economy car");
}
else
{
printf("Fuel Economy");
}
return 0;
}
These are the test cases(unknown):
These is my evaluation output:
PS: I am facing similar problems in many such programs with several test cases.
I also asked a similar question Cannot identify error with code, failing a test case
It would be helpful if someone suggests how to approach such unknown test cases.
If I'm not mistaken you should be able to start from 0 km. I went ahead and tried my own test case:
Starting km 0, Ending km 25, Liters used 1
if(c.startKm < 0 || c.endKm <= c.startKm || c.litres <= 0)

Trouble with populating student's marks

This is in response to a similar threat I posted the other day with reading a file into the requisite data structure with the file data like so, I can't remember who said it but yes there's four subjects. (I wanted to post an overall reply to all responses but could only comment on each post made):
131782 Mathematics 59
075160 Mathematics 92
580313 Physics 63
073241 Mathematics 32
487476 Mathematics 73
075160 Physics 98
472832 English 44
...
I'm using fscanf() now to parse the data and this is a much better approach. I made another thread yesterday about removing duplicate strings. I've scrapped that idea now and just used qsort on the student IDs and created a for loop that skips every four elements and rings the unique student IDs into the structure. I did a printf() command earlier and they're successfully stored. Now I've got the IDs stored I'm now ready to search for that ID and populate their marks and I "think" it's almost there except for a slight problem inside the update_student() function.
If you look at my code, or even compile it, it's not liking the line that's supposed to populate the mark for the student, student_data[idx].marks[buffer_subjects]=marks. But buffer_subjects is a string but if you look at my defines those strings are constants which is the whole idea when it gets to this stage.
How can I fix this?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define STUDENTS 20
#define COMPUTING 0
#define ENGLISH 1
#define MATHEMATICS 2
#define PHYSICS 3
#define SUBJECTS 4
#define ROWS 80
#define SIZE 100
int string_compare(void const *x, void const *y)
{
return strcmp(*(char**)x, *(char**)y);
}
struct student
{
char student_ID[SIZE];
int marks[SUBJECTS];
};struct student student_data[STUDENTS];
int find_student(char buffer_IDs[])
{
int j;
for(j=0;j<STUDENTS;j++)
if(strcmp(student_data[j].student_ID,buffer_IDs)==0)
return j;
}
void update_student(char buffer_IDs[], char buffer_subjects[], int marks[])
{
int idx = find_student(buffer_IDs);
student_data[idx].marks[buffer_subjects] = marks;
}
int main(void)
{
FILE *input;
int i,j, data_items;
char buffer_IDs[ROWS][SIZE];
char buffer_subject[ROWS][SIZE];
int marks[ROWS][SIZE];
char *string_ptrs[ROWS];
if((input=fopen("C:\\marks\\marks.txt","r"))==NULL)
perror("File open failed!");
else
{
for(i=0;i<ROWS;i++)
{
while((data_items=fscanf(input, "%s %s %d", buffer_IDs[i], buffer_subject[i], marks[i])!=3));
printf("%s %s %d\n", buffer_IDs[i], buffer_subject[i], *marks[i]);
string_ptrs[i]=buffer_IDs[i];
}
putchar('\n');
qsort(string_ptrs, sizeof(string_ptrs)/sizeof(char*), sizeof(char*), string_compare);
for(i=0;i<ROWS;i=i+4)
{
j=0;
strcpy(student_data[j].student_ID,string_ptrs[i]);
printf("%s\n",student_data[j].student_ID);
j++;
}
for(i=0;i<ROWS;i++)
update_student(buffer_IDs[i], buffer_subject[i], marks[i]);
}
return 0;
}
> Blockquote
There are numerous failures in this block of code. I would seriously recommend building it up block by block, and using a debugger to confirm that the data in each stage is as expected, and not proceeding till each of the lower level blocks work (and do not produce compile errors).
Running it through GDB with some sample data suggests problems even at the point of reading in and parsing data from the file on disk.
We can assist with individual issues as they arise from this approach.

C Programming: Read file and search in a second file based on the data found

I have two files, file1.txt and file2.txt:
file1.txt:
1234:James Smith:100:110
1111:Steve Jones:150:130
4321:Bob Wilson:110:140
file2.txt
100;Area 1;0.00
110;Area 2;3.00
120;Area 3;4.75
130;Area 4;5.95
140;Area 5;10.00
150;Area 6;12.00
What I would like to do is read file1.txt line by line and record some of the information to a file3.txt (output file) such as field 1 (number) and field 2 (name). To test that I could retrieve these, I used:
while (fscanf(file1, "%[^:]:%[^:]:%d:%d", number, name, &on, &off) == 4)
{
printf("Name contains: %s", name);
}
As expected, this retrieved all of the "name" (field 2) values and displayed them on screen.
I would now like to use the data stored in "on" and "off" to retrieve additional information from file2.txt. As you can see above, file1.txt fields 3 and 4 match up with field 1 in file2.txt. What I'm looking at getting in the final output file is:
1234 James Smith Area 1 to Area 2 3.00
1111 Steve Jones Area 6 to Area 4 6.05
4321 Bob Wilson Area 2 to Area 5 7.00
I'm not sure how to accomplish reading file1.txt line by line and performing the "check" with file2.txt at the same time and outputting. I also require a simple subtraction calculation in there as well.
Any help would be greatly appreciated. I don't have much experience with fscanf, fgets, reading files, etc.
This is an example of the struct definitions required-:
#define MAX_LINES 1000
struct rec1
{
int n;
char name[30];
int area1;
int area2;
};
struct rec1 rec1_list[MAX_LINES];
struct rec2
{
int area;
char desc[20];
int rating;
};
struct rec2 rec2_list[MAX_LINES];

C: How to properly output struct from .txt file?

First time I have asked a question here. First here's the code:
void displayCompanyInfo(STAFF e[], int *s)
{
FILE *payRoll;
int i = 0;
int rosterNumber [100];
int count = 1;
if ((payRoll = fopen("payRoll.txt", "r")) == NULL)
{
perror ("error");
exit (0);
}
cls;
while (fgets(e[i].name, sizeof(e[i].name), payRoll) != NULL)
{
printf ("Record %i: ", count++);
printf("%s\n", e[i].name);
}
fclose(payRoll);
printf("\n\n");
pause;
cls;
} // end of display
Basically this code works. However when the text file displays it reads like this:
Record 1: Name: blah
Record 2: Age: 23
Record 3: Hourly Rate: 34
Instead I want it to read it as follows:
Record 1: Name: Blah
Age: 23
Hourly Rate: 34
Record 2: Name: Blah2
Age: 24
Hourly Rate: 35
And so on...
Any idea on how I can get this to work. I didn't post whole program because I didn't want to over do my thread. But if you need it let me know.
To this case you have to work with binary file handling.With text file it is not possible.
You have to read,write chunk of data in terms of bytes and handle it accordingly to retrieve you structure.
Say
struct e{
char name[20],
int age,
int hourly_rate
};
This structure will require
20(name) + 4(age) + 4(hourly_rate) bytes.Then you should write 28 bytes at a time in binary file and retrieve 28 bytes accordingly,which is not possible in case of text file.Because text file considers every thing as character,say age=23 it considers age field as 2 bytes and if age=3,it consider it as 1 byte.But binary file considers both thing as 4 bytes which is actual size of integer.
So the solution to your problem is binary file handling.
The problem is that the loop considers each line as a record. Instead, a record should be 3 lines. So read 3 things in the loop - add the 2 missing right before the printf.

C Primer 5th - Task 14-6

A text file holds information about a softball team. Each line has data arranged as follows:
4 Jessie Joybat 5 2 1 1
The first item is the player's number, conveniently in the range 0–18. The second item is the player's first name, and the third is the player's last name. Each name is a single word. The next item is the player's official times at bat, followed by the number of hits, walks, and runs batted in (RBIs). The file may contain data for more than one game, so the same player may have more than one line of data, and there may be data for other players between those lines. Write a program that stores the data into an array of structures. The structure should have members to represent the first and last names, the at bats, hits, walks, and RBIs (runs batted in), and the batting average (to be calculated later). You can use the player number as an array index. The program should read to end-of-file, and it should keep cumulative totals for each player.
The world of baseball statistics is an involved one. For example, a walk or reaching base on an error doesn't count as an at-bat but could possibly produce an RBI. But all this program has to do is read and process the data file, as described next, without worrying about how realistic the data is.
The simplest way for the program to proceed is to initialize the structure contents to zeros, read the file data into temporary variables, and then add them to the contents of the corresponding structure. After the program has finished reading the file, it should then calculate the batting average for each player and store it in the corresponding structure member. The batting average is calculated by dividing the cumulative number of hits for a player by the cumulative number of at-bats; it should be a floating-point calculation. The program should then display the cumulative data for each player along with a line showing the combined statistics for the entire team.
team.txt (text file I'm working with):
4 Jessie Joybat 5 2 1 1
4 Jessie Joybat 7 3 5 3
7 Jack Donner 6 3 1 2
11 Martin Garder 4 3 2 1
15 Jaime Curtis 7 4 1 2
2 Curtis Michel 3 2 2 3
9 Gillan Morthim 9 6 6 7
12 Brett Tyler 8 7 4 3
8 Hans Gunner 7 7 2 3
14 Jessie James 11 2 3 4
12 Brett Tyler 4 3 1 3
Since I'm a beginner in C, either I misinterpreted the task from what was asked originally or it's unfairly complex (I believe the former is the case). I'm so lost that I can't think of the way how could I fill in by the criteria of index (player number) every piece of data, keep track of whether he has more than one game, calculate and fetch bat average and then print.
What I have so far is:
#define LGT 30
struct profile {
int pl_num;
char name[LGT];
char lname[LGT];
int atbat[LGT/3];
int hits[LGT/3];
int walks[LGT/3];
int runs[LGT/3];
float batavg;
};
//It's wrong obviously but it's a starting point
int main(void)
{
FILE *flx;
int i,jc,flow=0;
struct profile stat[LGT]={{0}};
if((flx=fopen("team.txt","r"))==NULL) {
fprintf(stderr,"Can't read file team!\n");
exit(1);
}
for( jc = 0; jc < 11; jc++) {
fscanf(flx,"%d",&i);
stat[i].pl_num=i;
fscanf(flx,"%s",&stat[i].name);
fscanf(flx,"%s",&stat[i].lname);
fscanf(flx,"%d",&stat[i].atbat[flow]);
fscanf(flx,"%d",&stat[i].hits[flow]);
fscanf(flx,"%d",&stat[i].walks[flow]);
fscanf(flx,"%d",&stat[i].runs[flow]);
flow++;
}
}
Advice 1: don't declare arrays like atbat[LGT/3].
Advice 2: Instead of multiple fscanf you could read the whole line in a shot.
Advice 3: Since the number of players is limited and the player number has a good range (0-18), using that player number as an index into the struct array is a good idea.
Advice 4: Since you need cumulative data for each player (no need to store his history points), then you don't need arrays of integers, just an integer to represent the total.
So:
#include <stdio.h>
#define PLAYERS_NO 19
typedef struct
{
char name[20+1];
char lastName[25+1];
int atbat;
int hits;
int walks;
int runs;
float batavg;
} Profile;
int main(int argc, char** argv)
{
Profile stats[PLAYERS_NO];
int i;
FILE* dataFile;
int playerNo;
Profile tmpProfile;
int games = 0;
for(i=0; i<PLAYERS_NO; ++i)
{
stats[i].name[0] = '\0';
stats[i].lastName[0] = '\0';
stats[i].atbat = 0;
stats[i].hits = 0;
stats[i].walks = 0;
stats[i].runs = 0;
}
dataFile = fopen("team.txt", "r");
if ( dataFile == NULL )
{
fprintf(stderr, "Can't read file team!\n");
exit(1);
}
for(i=0; i<PLAYERS_NO && !feof(dataFile); ++i, ++games)
{
fscanf(dataFile, "%d", &playerNo);
if ( playerNo <0 || playerNo > PLAYERS_NO )
{
fprintf(stderr, "Player number out of range\n");
continue;
}
fscanf(dataFile, "%s %s %d %d %d %d",
&tmpProfile.name,
&tmpProfile.lastName,
&tmpProfile.atbat,
&tmpProfile.hits,
&tmpProfile.walks,
&tmpProfile.runs);
printf("READ: %d %s %s %d %d %d %d\n",
playerNo,
tmpProfile.name,
tmpProfile.lastName,
tmpProfile.atbat,
tmpProfile.hits,
tmpProfile.walks,
tmpProfile.runs);
strcpy(stats[playerNo].name, tmpProfile.name);
strcpy(stats[playerNo].lastName, tmpProfile.lastName);
stats[playerNo].atbat += tmpProfile.atbat;
stats[playerNo].hits += tmpProfile.hits;
stats[playerNo].walks += tmpProfile.walks;
stats[playerNo].runs += tmpProfile.runs;
}
/* exercise: compute the average */
fclose(dataFile);
for(i=0; i<PLAYERS_NO; ++i)
{
if ( stats[i].name[0] == '\0' )
continue;
printf("%d %s %s %d %d %d %d\n",
i,
stats[i].name,
stats[i].lastName,
stats[i].atbat,
stats[i].hits,
stats[i].walks,
stats[i].runs);
}
return 0;
}
The first rule of programming: Divide and conquer.
So you need to identify individual operations. One such operation is "load one row of input", another is "look up a player". If you have some of those operations (more will come up as you go), you can start building your program:
while( more_input ) {
row = load_one_row()
player = find_player( row.name )
if( !player ) {
player = create_player( row.name )
add_player( player )
}
... do something with row and player ...
}
when you have that, you can start to write all the functions.
An important point here is to write test cases. Start with a simple input and test the code to read a row. Do you get the correct results?
If so, test the code to find/create players.
The test cases make sure that you can forget about code that already works.
Use a framework like Check for this.
If I were doing this, I'd start with a structure that only held one "set" of data, then create an array of those structs:
struct profile {
char name[NAMELEN];
char lname[NAMELEN];
int atbat;
int hits;
int walks;
int runs;
float batavg;
};
Since you're using the player's number as the index into an array, you don't need to store it into the structure too.
I think that will simplify the problem a little bit. You don't need to store multiple data items for a single player -- when you get a duplicate, you just ignore some of the new data (like the names, which should be identical) and sum up the others (e.g., at-bats, hits).

Resources