Strings and array of structures - c

***UPDATED WITH NEW CODE
I'm trying to load a file data with an unknown amount of lines of data.
The data is organized as last, first:score1:score2:score3:score5:lettergrade
I'm getting the file to (sort of) load into an array and print.
When the data prints, it doesn't print the first name, just the first. It's printing the same integer for evert data element except for the last one, that comes out at 0 for all. Also, it's not flushing the data that doesn't return 8 elements.
PLEASE help!
Thanks in advance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FFLUSH while(fgetsc(fp) != '\n')
#define MAX_STUDENTS 45
typedef struct aStudent
{
char name[26];
int id[7];
int test1[4];
int test2[4];
int proj1[4];
int proj2[4];
int proj3[4];
char grade[4];
} aStudent;
int main(void)
{
char file_name[FILENAME_MAX];
FILE* fp;
int i;
int rc;
int dc;
aStudent studentArray[MAX_STUDENTS];
printf("Enter File Name: ");
rc = scanf("%s", &file_name);
if (rc == 0)
{
printf("\n\nError: No file name entered.");
exit(0);
}
fp = fopen(file_name, "r");
if (fp == NULL)
{
printf("Error: Could not open file %s for read", file_name);
exit(0);
}
for(int i=0; i< 45; i++)
{
dc = (fscanf(fp, "%s[^:]%d[^:]%d[^:]%d[^:]%d[^:]%d[^:]%d[^:]%f[^:]%c[^:\n]", studentArray[i].name, studentArray[i].id, studentArray[i].test1, studentArray[i].test2, studentArray[i].proj1, studentArray[i].proj2, studentArray[i].proj3, studentArray[i].grade));
if (dc != 8)
{
FFLUSH;
}
printf("%s %d %d %d %d %d %d %.2f %c", studentArray[i].name, studentArray[i].id, studentArray[i].test1, studentArray[i].test2, studentArray[i].proj1, studentArray[i].proj2, studentArray[i].proj3, studentArray[i].grade);
}
return 0;
}

STUDENT is a type. You cannot fgets into a type. There is no array STUDENT. Try this;
typedef struct aStudent
{
char name[26];
int id[7];
int test1[4];
int test2[4];
int proj1[4];
int proj2[4];
int proj3[4];
int ave[4];
char grade[4];
} aStudent;
and;
aStudent studentArray[MAX_STUDENTS];
and replace gGets with fscanf.

It appears you have your struct set up incorrectly to handle the data you want to read from your file. Your data was:
name:score1:score2:score3:score5:lettergrade
A struct that makes sense (given the multiple scores) would be:
typedef struct {
char name[MAXNM];
int id;
int scores[NGRDS];
float ave;
char grade;
} student;
(you have never accounted for where the id comes from and I'll leave it to use to split name into first, last, as your original question specified name and your recent edits added first, last)
What is clear is you want to read your datafile into a struct and compute the ave (average) from the grades read (which should be a floating-point type instead of int). Given your file format, reading each line of data with the scanf family functions is probably a bit more sensible than reading/tokenizing each line with fgets/strtok.
Your mixing of fgets and fscanf syntax will not work and exemplifies the difficulty in trying to shoehorn mixed data into a scanf type format string. You must take care in crafting the string to handle whitepace and any trailing newlines left in the input buffer. For your set of data, the following will work as well as anything else:
char *fmt = " %[^:]:%d:%d:%d:%d:%c";
Another way to account for the trailing newline is to use the assignment suppression operator to read and discard the trailing newline. When used, the discarded conversion is not added to the returned match count. e.g.
char *fmt = "%[^:]:%d:%d:%d:%d:%c%*c";
There is no need for macros to flush the input buffer if you craft your format string correctly.
Putting the pieces together, and leaving your belated change from name to last, first to you, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* constants for:
NGRDS - max number of grades per student.
MAXNM - max length of student name.
MAXSTD - max number of students in array.
MAXFN - max filename length.
*/
enum { NGRDS = 4, MAXNM = 26, MAXSTD = 45, MAXFN = 128 };
typedef struct {
char name[MAXNM];
int id;
int scores[NGRDS];
float ave;
char grade;
} student;
int main (void) {
char filename[MAXFN]; /* initialize variables */
char *fmt = " %[^:]:%d:%d:%d:%d:%c";
FILE* fp = NULL;
size_t i = 0, nstudents = 0;
student students[MAXSTD] = {{ {""}, 0, {0}, 0.0, 0 }};
printf ("\nEnter File Name: ");
if (scanf("%s", filename) != 1) { /* validate filename read */
fprintf (stderr, "error: invalid file name entered.\n");
return 1;
}
if (!(fp = fopen (filename, "r"))) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'\n.", filename);
return 1;
}
/* read each line in file */
while (fscanf (fp, fmt, students[i].name, &students[i].scores[0],
&students[i].scores[1],&students[i].scores[2],
&students[i].scores[3], &students[i].grade) == 6) {
int j; /* compute sum/average */
for (j = 0; j < NGRDS; j++) students[i].ave += students[i].scores[j];
students[i].ave /= (float)NGRDS;
if (++i == MAXSTD) { /* check against max */
fprintf (stderr, "warning: students array full.\n");
break;
}
}
fclose (fp); /* close file */
nstudents = i; /* save number of students */
for (i = 0; i < nstudents; i++) { /* output student data */
size_t j;
printf ("\n Student[%2zu]\n name : %s\n scores :",
i, students[i].name);
for (j = 0; j < NGRDS; j++) printf (" %d", students[i].scores[j]);
printf ("\n average: %.2f\n grade : %c\n",
students[i].ave, students[i].grade);
}
return 0;
}
Example Data
$cat dat/studentgrades.txt
Joe Cool:80:77:63:88:c
Nancy Smart:96:93:97:99:a
Dwane Dumb:61:60:67:50:f
Al Smith:75:77:78:73:c
Example Use/Output
$ ./bin/studentstruct
Enter File Name: dat/studentgrades.txt
Student[ 0]
name : Joe Cool
scores : 80 77 63 88
average: 77.00
grade : c
Student[ 1]
name : Nancy Smart
scores : 96 93 97 99
average: 96.25
grade : a
Student[ 2]
name : Dwane Dumb
scores : 61 60 67 50
average: 59.50
grade : f
Student[ 3]
name : Al Smith
scores : 75 77 78 73
average: 75.75
grade : c
(note: your letter grade should be computed based on the average, not simply read from the file -- what if the grades change? Also note that your input file format is fixed and inflexible. If the number of fields or their format changes in any way, you must account for the change in your fmt format string.)
Look it over and let me know if you have any questions.

Related

Using qsort and strcmp prior to my sctrucs going into txt files

My code pulls data from one text file and then totals the points and enters it into a separate text file, so I would like the program to organize the teams and scores by total points prior to frpintf into the text file. So far the program pulls and runs the data and totals it out and fprintf just fine, should I use qsort to sort and print into the text file, and where in my code do I place it. Here is the text it is reading.
Indians 54 45 5
Twins 45 53 7
Tigers 43 59 8
White_Sox 35 64 9
Royals 30 69 3
Also I know there are no ties in the MLB lol just throwing in an additional variable.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main(void)
{
struct records {
char filename;
char team[50];
int wins;
int tie;
int loss;
int points;
};
struct records roster;
FILE *ifp = NULL;
FILE *afd = NULL;
const int argv;
char filename2[64] = {0};
char filename[64] = {0};
int points;
int points2;
int total;
printf("\nPlease enter the the .txt file you would like to open: ");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");
if (ifp == NULL)
{
printf("Could not open");
printf("\nPlease enter the the .txt file you would like to open:");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");
}
printf("\nReading the file %s \n", filename);
while(fscanf(ifp, "%s %d %d%d" , roster.team, &roster.wins, &roster.loss,
&roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d\n", roster.team, roster.wins,
roster.loss, roster.tie);
printf("\nWins are worth 2 points ties are worth 1 point and losses are
worth \nzero in overall league standings.");
printf("\nHere are the overall ALCentral Standings!\n");
ifp = fopen(filename, "r");
fopen(filename, "r"); while(fscanf(ifp, "%s %d %d %d", roster.team,
&roster.wins, &roster.loss, &roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d Total league points:%d\n",
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins * 2 +
roster.tie * 1), total);
printf("closing %s", filename);
fclose(ifp);
printf("\nPlease enter the the .txt file you would like to write to: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2, "a+");
if (afd == NULL)
{
printf("Could not open"); printf("\nPlease enter the the .txt file you
would like to open: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2,"a+");
}
ifp = fopen(filename,"r");
fopen(filename, "r");
points = roster.wins * 2;
points2 = roster.tie * 1;
total = points + points2;
while(fscanf(ifp, "%s %d %d %d", roster.team, &roster.wins, &roster.loss,
&roster.tie) != EOF)
fprintf(afd, "%s Wins:%d Losses:%d ties:%d total league points:%d\n",
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins *2 +
roster.tie *1 ), total, filename2);
printf("\nYour stats have been recorded to the selected
file!\nClosing all open files! \nThanks for using the STAT-Master!
Have a great day!
fclose(afd);
fclose(ifp);
return 0;
}
Your logic is quite jumbled. If I understand what you are attempting, you want to read each teams wins, loss and tie record from your data file, and then compute the points that are then used to sort the teams into descending (or perhaps ascending) order and then output the "the overall ALCentral Standings" to either stdout or a file, or both.
Before starting let's talk about your definition of struct records inside of main(). While legal, you generally will want your struct declaration to be at file scope so that the struct type is available to any functions you write to help process the data in your code. Here, you must move the declaration outside of main() because the qsort compare function will need to know what the type struct records is. The compare function cannot be declared and defined in main() because C does not allow nested function declarations. So move your struct outside of main(), e.g.
#define MAXR 16 /* if you need a constant, #define one (or more) */
#define MAXC 64
#define MAXB 1024
typedef struct records {
char team[MAXC];
int wins,
loss,
tie,
points;
} record_t;
(note: using a typedef can make things more convenient)
To begin with, your multiple attempts to read the filename is awkward and never insures you actually have a file open. It just makes a couple of attempts and then effectively gives up validation and just blindly assumes there is a valid file stream to read and write to and presses ahead. This is a recipe for invoking Undefined Behavior.
When ever you do anything in C, and especially when you are dealing with user input or file operations, you must validate every step. You must know whether or not the file is open for reading before you attempt to read. (no matter how many times you attempt to open it) You must validate what you have read before you begin using the values in your code. You must validate the state of your input stream before you attempt your next read, and handle any characters that remain.
With that in mind you can prompt for the filename and attempt to open the filename provided in a continual loop until you validate the file is actually open, e.g.
/** helper function to empty stdin between inputs */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
...
FILE *fp = NULL; /* file pointer */
do { /* loop until valid filename given and file open */
printf("\nenter input filename: ");
if (scanf (" %63[^\n]", filename) != 1) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove '\n' and any add'l chars in input buffer */
fp = fopen (filename, "r"); /* open filename */
if (fp == NULL) /* validate file open for reading */
perror ("fopen-input");
} while (fp == NULL);
printf("Opened file name %s\n\n",filename);
(note: rather than using scanf for user input, fgets provides a better approach to user input and far fewer pitfalls that scanf, example below with roster)
Next, your understanding of how to collect the data in memory in order to sort it, is not evident from your post. When you want to sort a collection of team information, you must read all team information into memory before you can pass the information to qsort for sorting.
You have a struct that holds all of information for each team, you simply need to read each line (record) from your input file into an array or struct which can then easily be passed to qsort for sorting.
To use qsort you first must define a compare function that is used to qsort to order your data. The compare function has a specific declaration:
int compare (const void *a, const void *b);
Where each const void * parameter will be a pointer to one of the elements in your array of records. (in C a void pointer, e.g. void* is a generic pointer that can be assigned from, or to, any other pointer type without a cast -- that is why the compare function's declaration uses that type for the parameters) Your job in writing a compare function is simply to provide a type to the pointers (either by assignment or by explicit cast, your choice) and then a comparison between the values that you wish to sort by. For example, your compare on the points member of each struct could simply be:
/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
const record_t *ta = a;
const record_t *tb = b;
return (ta->points < tb->points) - (ta->points > tb->points);
}
(where the (a < b) - (a > b) simply evaluates to -1, 0, 1 depending on the inequalites, e.g. if a = 4 and b = 3, you have (0) - (1) which equals -1 meaning a sorts before b)
All that remains is reading each teams data into your array and computing points and then passing your array to qsort.
To read your data into an array, read each line into a buffer, parse the data in the buffer into the individual values, validate that each of the individual values where parsed correctly, compute the points value for each team, increment your array index and then go read the next line. You could do something like the following using fgets to read each line of data:
record_t roster[MAXR] = {{ .team = "" }}; /* array to hold teams */
...
/* read up to MAXR team records from file */
while (ndx < MAXR && fgets (buf, MAXB, fp)) {
/* parse data from buffer filled, saving return */
int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team,
&roster[ndx].wins, &roster[ndx].loss,
&roster[ndx].tie);
if (rtn < 4) /* if less than 4 conversions, get next line */
continue;
/* compute points (2 * wins + ties) */
roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
ndx++; /* increment index */
}
fclose (fp); /* close file */
(note: you can add a strlen() check of buf to determine if additional characters remain in the line unread -- that is left to you).
With all you data in your array of structs roster, all that remains is passing roster to qsort and then outputting your sorted data.
The following sorts the data and outputs the results to stdout. Writing the output to a file is left to you, but hint, you don't need both ifp and afp and you don't need filename and filename2 or points2, you are only dealing with one file/filename at a time. The sort and output is simple:
/* sort roster based on points (descending order) */
qsort (roster, ndx, sizeof *roster, compare);
/* outpout results */
printf ("Here are the overall ALCentral Standings!\n\n"
"Team Wins Loss Tie Points\n\n");
for (int i = 0; i < ndx; i++)
printf ("%-12s %4d %4d %4d %4d\n", roster[i].team,
roster[i].wins, roster[i].loss, roster[i].tie,
roster[i].points);
Putting it altogether in an example, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXR 16 /* if you need a constant, #define one (or more) */
#define MAXC 64
#define MAXB 1024
typedef struct records {
char team[MAXC];
int wins,
loss,
tie,
points;
} record_t;
/** helper function to empty stdin between inputs */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
const record_t *ta = a;
const record_t *tb = b;
return (ta->points < tb->points) - (ta->points > tb->points);
}
int main (void) {
int ndx = 0; /* index for roster array */
char buf[MAXB] = "", /* buffer for fgetrs */
filename[MAXC] = ""; /* filename to read */
record_t roster[MAXR] = {{ .team = "" }}; /* array to hold teams */
FILE *fp = NULL; /* file pointer */
do { /* loop until valid filename given and file open */
printf("\nenter input filename: ");
if (scanf (" %63[^\n]", filename) != 1) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
empty_stdin(); /* remove '\n' and any add'l chars in input buffer */
fp = fopen (filename, "r"); /* open filename */
if (fp == NULL) /* validate file open for reading */
perror ("fopen-input");
} while (fp == NULL);
printf("Opened file name %s\n\n",filename);
/* read up to MAXR team records from file */
while (ndx < MAXR && fgets (buf, MAXB, fp)) {
/* parse data from buffer filled, saving return */
int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team,
&roster[ndx].wins, &roster[ndx].loss,
&roster[ndx].tie);
if (rtn < 4) /* if less than 4 conversions, get next line */
continue;
/* compute points (2 * wins + ties) */
roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
ndx++; /* increment index */
}
fclose (fp); /* close file */
/* sort roster based on points (descending order) */
qsort (roster, ndx, sizeof *roster, compare);
/* outpout results */
printf ("Here are the overall ALCentral Standings!\n\n"
"Team Wins Loss Tie Points\n\n");
for (int i = 0; i < ndx; i++)
printf ("%-12s %4d %4d %4d %4d\n", roster[i].team,
roster[i].wins, roster[i].loss, roster[i].tie,
roster[i].points);
return 0;
}
Example Input File
The file is provided as given, with blank lines in between records.
$ cat dat/teams.txt
Indians 54 45 5
Twins 45 53 7
Tigers 43 59 8
White_Sox 35 64 9
Royals 30 69 3
Example Use/Output
$ ./bin/qsortstrcmp
enter input filename: dat/teams.txt
Opened file name dat/teams.txt
Here are the overall ALCentral Standings!
Team Wins Loss Tie Points
Indians 54 45 5 113
Twins 45 53 7 97
Tigers 43 59 8 94
White_Sox 35 64 9 79
Royals 30 69 3 63
Look things over and let me know if you have further questions.

Reading tab delimited ints into a struct in C [duplicate]

I'm currently working on a program that simulates various CPU scheduling methods. Currently I have the program asking for input:
printf("Enter type of CPU scheduling algorithm (SJF, RR, PR_noPREMP, PR_withPREMP): ");
scanf("%s", typeOf);
printf("Enter number of processes: ");
scanf("%d", &numPro);
struct processStruct structs[numPro];
int burstTimes[numPro];
for (i = 0; i < numPro; i++) {
printf("Enter process number: ");
scanf("%d", &structs[i].pNum);
printf("Enter arrival time: ");
scanf("%d", &structs[i].arTime);
printf("Enter CPU burst time: ");
scanf("%d", &structs[i].cpuBur);
printf("Enter priority: ");
scanf("%d", &structs[i].prio);
}
In addition to the two variables typeOf (an int) and numPro (a char array) I am also using a data structure.
Here is the data structure that is holding the various parameters:
struct processStruct {
int pNum;
int arTime;
int cpuBur;
int prio;
int waitTim;
};
Instead of manual input I could like to use a text file with the same information as input for the program. The text file would look something like this:
SJF
4
1 0 6 1
2 0 8 1
3 0 7 1
4 0 3 1
First line is the name of the scheduling algorithm.
Second line is the number of processes.
The following lines consists of information for each process. So 1 0 6 1 = Process = 1, 0 = Arrival Time, 6 = CPU burst time, 1 = Priority
I unfortunately have little experience using text file input with C. Does anyone have ideas about how I could read in the data from the text file into the variables and data structure?
Thank you
Edit: One of the issues I am having is that the data is not the same for each line. If it was just the rows of 4 numbers then it would be relatively easy. I need the program to read the first line into a char array (string), the second into the numPro variable then the subsequent lines into multiple instances of the data structure (one for each process).
The file can be read fairly simply with fscanf() because everything except the first line identifier is a number. But you do need to check the validity of what is read from the file. I have just used exit(1) on error for illustration, it could be more sophisticated than that (for example an error message).
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
struct processStruct {
int pNum;
int arTime;
int cpuBur;
int prio;
int waitTim;
};
struct processStruct structs[MAX];
int main(int argc, char** args)
{
FILE *fil;
char typeOf[4];
int numPro, i;
if ((fil = fopen("myfile.txt", "rt")) == NULL)
exit(1);
if(fscanf(fil, "%4s", typeOf) != 1)
exit(1);
if(fscanf(fil, "%d", &numPro) != 1)
exit(1);
if(numPro > MAX)
exit(1);
for(i=0; i<numPro; i++) {
if(fscanf(fil, "%d%d%d%d", &structs[i].pNum, &structs[i].arTime,
&structs[i].cpuBur, &structs[i].prio) != 4)
exit(1);
}
fclose(fil);
// test the result
printf("Type: %s\n", typeOf);
printf("Num: %d\n", numPro);
for(i=0; i<numPro; i++) {
printf("%d %d %d %d\n", structs[i].pNum, structs[i].arTime,
structs[i].cpuBur, structs[i].prio);
}
return 0;
}
Program output:
Type: SJF
Num: 4
1 0 6 1
2 0 8 1
3 0 7 1
4 0 3 1
You will want to use fscanf and fprintf instead of scanf and printf to do I/O from/to files instead of standard input/output.
These are widely documented. You will use FILE * variables and fopen & fclose. You can even use stdin, stdout, and stderr as handles for the console.
The best way to read lines in a file with C is to use the getline() function. It is much more reliable than scanf() and can allocate the needed memory automagically.
Here is the code I suggest:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv)
{
FILE *inputfile;
if (argc < 2)
{
fprintf (stderr, "error: missing file name in the arguments\n");
exit (EXIT_FAILURE);
}
inputfile = fopen (argv[1], "r");
if (!inputfile)
{
perror ("myprogram");
exit (EXIT_FAILURE);
}
/* Getting first line */
char *line = NULL;
size_t line_size = 0;
if (!getline(&line, &line_size, inputfile))
{
fprintf (stderr, "error: failed to read first line\n");
exit (EXIT_FAILURE);
}
/* Getting the size of the matrix */
unsigned int size; /* Size of the matrix */
if (sscanf (line, "%zd", &size) != 1)
{
fprintf (stderr, "error: first line has wrong format\n");
exit (EXIT_FAILURE);
}
/* Getting the content of the matrix */
int matrix[size][size];
for (unsigned int i = 0; i < size; i++)
{
if (!getline (&line, &line_size, inputfile))
{
fprintf (stderr, "error: input file has wrong format\n");
exit (EXIT_FAILURE);
}
/* Copying the items of the line to the matrix */
char *ptr = line;
for (unsigned j = 0; j < size; j++)
{
matrix[i][j] = atoi(strtok(ptr, " "));
ptr = NULL;
}
}
free(line);
return EXIT_SUCCESS;
}
Beware, I didn't tried this code... So, if something is not working properly, I'll fix it.

Reading in a text file in C, separate lines into multiple variables

I'm currently working on a program that simulates various CPU scheduling methods. Currently I have the program asking for input:
printf("Enter type of CPU scheduling algorithm (SJF, RR, PR_noPREMP, PR_withPREMP): ");
scanf("%s", typeOf);
printf("Enter number of processes: ");
scanf("%d", &numPro);
struct processStruct structs[numPro];
int burstTimes[numPro];
for (i = 0; i < numPro; i++) {
printf("Enter process number: ");
scanf("%d", &structs[i].pNum);
printf("Enter arrival time: ");
scanf("%d", &structs[i].arTime);
printf("Enter CPU burst time: ");
scanf("%d", &structs[i].cpuBur);
printf("Enter priority: ");
scanf("%d", &structs[i].prio);
}
In addition to the two variables typeOf (an int) and numPro (a char array) I am also using a data structure.
Here is the data structure that is holding the various parameters:
struct processStruct {
int pNum;
int arTime;
int cpuBur;
int prio;
int waitTim;
};
Instead of manual input I could like to use a text file with the same information as input for the program. The text file would look something like this:
SJF
4
1 0 6 1
2 0 8 1
3 0 7 1
4 0 3 1
First line is the name of the scheduling algorithm.
Second line is the number of processes.
The following lines consists of information for each process. So 1 0 6 1 = Process = 1, 0 = Arrival Time, 6 = CPU burst time, 1 = Priority
I unfortunately have little experience using text file input with C. Does anyone have ideas about how I could read in the data from the text file into the variables and data structure?
Thank you
Edit: One of the issues I am having is that the data is not the same for each line. If it was just the rows of 4 numbers then it would be relatively easy. I need the program to read the first line into a char array (string), the second into the numPro variable then the subsequent lines into multiple instances of the data structure (one for each process).
The file can be read fairly simply with fscanf() because everything except the first line identifier is a number. But you do need to check the validity of what is read from the file. I have just used exit(1) on error for illustration, it could be more sophisticated than that (for example an error message).
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
struct processStruct {
int pNum;
int arTime;
int cpuBur;
int prio;
int waitTim;
};
struct processStruct structs[MAX];
int main(int argc, char** args)
{
FILE *fil;
char typeOf[4];
int numPro, i;
if ((fil = fopen("myfile.txt", "rt")) == NULL)
exit(1);
if(fscanf(fil, "%4s", typeOf) != 1)
exit(1);
if(fscanf(fil, "%d", &numPro) != 1)
exit(1);
if(numPro > MAX)
exit(1);
for(i=0; i<numPro; i++) {
if(fscanf(fil, "%d%d%d%d", &structs[i].pNum, &structs[i].arTime,
&structs[i].cpuBur, &structs[i].prio) != 4)
exit(1);
}
fclose(fil);
// test the result
printf("Type: %s\n", typeOf);
printf("Num: %d\n", numPro);
for(i=0; i<numPro; i++) {
printf("%d %d %d %d\n", structs[i].pNum, structs[i].arTime,
structs[i].cpuBur, structs[i].prio);
}
return 0;
}
Program output:
Type: SJF
Num: 4
1 0 6 1
2 0 8 1
3 0 7 1
4 0 3 1
You will want to use fscanf and fprintf instead of scanf and printf to do I/O from/to files instead of standard input/output.
These are widely documented. You will use FILE * variables and fopen & fclose. You can even use stdin, stdout, and stderr as handles for the console.
The best way to read lines in a file with C is to use the getline() function. It is much more reliable than scanf() and can allocate the needed memory automagically.
Here is the code I suggest:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv)
{
FILE *inputfile;
if (argc < 2)
{
fprintf (stderr, "error: missing file name in the arguments\n");
exit (EXIT_FAILURE);
}
inputfile = fopen (argv[1], "r");
if (!inputfile)
{
perror ("myprogram");
exit (EXIT_FAILURE);
}
/* Getting first line */
char *line = NULL;
size_t line_size = 0;
if (!getline(&line, &line_size, inputfile))
{
fprintf (stderr, "error: failed to read first line\n");
exit (EXIT_FAILURE);
}
/* Getting the size of the matrix */
unsigned int size; /* Size of the matrix */
if (sscanf (line, "%zd", &size) != 1)
{
fprintf (stderr, "error: first line has wrong format\n");
exit (EXIT_FAILURE);
}
/* Getting the content of the matrix */
int matrix[size][size];
for (unsigned int i = 0; i < size; i++)
{
if (!getline (&line, &line_size, inputfile))
{
fprintf (stderr, "error: input file has wrong format\n");
exit (EXIT_FAILURE);
}
/* Copying the items of the line to the matrix */
char *ptr = line;
for (unsigned j = 0; j < size; j++)
{
matrix[i][j] = atoi(strtok(ptr, " "));
ptr = NULL;
}
}
free(line);
return EXIT_SUCCESS;
}
Beware, I didn't tried this code... So, if something is not working properly, I'll fix it.

Create a student record using structures and pointers in C language

I am having issues with trying to build a student record. The struct has four variables which are id, name, age, and GPA. I am supposed to read in a file and dynamically allocate memory for a new student struct which is suppose to hold a data set (student record). Then finally I'm supposed to print the total number of students that were successfully created from the file, average age (as float or double), and average GPA (as float or double). As you can see I am very lost and would appreciate any help in coding this project. Will be forever grateful! Here is what I have so far:
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
int id;
char name[255];
int age;
float gpa;
}student;
int makeStudent(FILE *fpin, student *students)
{
int num_Students = 0;
int rowNum = 0;
students = (student *)malloc(sizeof(student) * 50);
if((fscanf(fpin, "%d", students->id) < 0))
{
printf("Cannot create student record from file row # %d: id is invalid", rowNum);
exit(EXIT_FAILURE);
}
if((fscanf(fpin, "%s", students->name) != 1))
{
printf("Cannot create student record from file row # %d: name is invalid", rowNum);
exit(EXIT_FAILURE);
}
if((fscanf(fpin, "%d", students->age) < 0))
{
printf("Cannot create student record from file row # %d: age is invalid", rowNum);
exit(EXIT_FAILURE);
}
fscanf(fpin, "%d", students->gpa);
if(students->gpa < 0.0 || students->gpa > 4.0)
{
printf("Cannot create student record from file row # %d: GPA is invalid", rowNum);
exit(EXIT_FAILURE);
}
num_Students++;
rowNum++;
return 1;
}
int main()
{
FILE *fpin;
char name[255];
int size = 0;
fpin = fopen("student.txt", "r");
student studentRecord[255];
while(fgets(name, 255, fpin) != NULL)
printf("%s", name);
/*while(1)
if(!makeStudent(fpin, &studentRecord[size]))
break;
++size;
if(fpin == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
*/
fclose(fpin);
return 0;
}
FILE DATA BELOW:
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
In a comment, I said:
Note that you probably need to pass a student **students (a double pointer, rather than a single pointer) so that code outside this function can access the array of structures allocated inside the function. As it stands, you modify the local variable students, but that doesn't change the value in the function that calls this one.
And Carla Ramirez asked:
so using a double pointer in makeStudent function will allow me to access that array inside main method for example? How would the syntax look in C language to access that array in makeStudent?
The simplest way is to rename the parameter (I chose p_students) and make a local variable student *students in place of the parameter. Then write the code as:
int makeStudent(FILE *fpin, student **p_students)
{
int num_Students = 0;
int rowNum = 0;
student *students = (student *)malloc(sizeof(student) * 50);
if (students == 0)
return 0;
*p_students = students;
and the rest of the code can use students just as before.
If you can't or won't do that, then:
int makeStudent(FILE *fpin, student **students)
{
int num_Students = 0;
int rowNum = 0;
*students = (student *)malloc(sizeof(student) * 50);
if (*students == 0)
return 0;
and in the rest of the code, reference *student (or, more frequently, (*student)) where you previously had just student. However, that's make a notational rod for your own back; it is silly to make the code harder to understand than it needs to be.
Note, though, that the makeStudent() code would need to loop through up to 50 students.
In the main() program, you'd write:
student *students = 0;
int n_students = makeStudent(fpin, &students);
if (n_students > 0)
{
…print them…
free(students); // Free them (let them go home at the end of the day/term/year?)
}
Alternatively
Since your function is written to process one student, you could do the memory allocation in the main() program and not try any memory allocation in makeStudent(). Then main() might look like:
student students[50];
for (i = 0; makeStudent(fpin, &students[i]) != 0; i++)
{
…process a student…
}
And the code in makeStudent() might be just:
int makeStudent(FILE *fpin, student *students)
{
if ((fscanf(fpin, "%d", students->id) < 0))
{
printf("Cannot create student record from file row # %d: id is invalid", rowNum);
exit(EXIT_FAILURE);
}
…etcetera…
…except that the rowNum would need to be passed in (it is i in my revised main())…
Below is an example adapted to your problem. I have removed the validation checks so the logic is more clear. In the code where you see at //(1) or //(2) that corresponds to the validation check at the end of the file that can be replaced when you a comfortable with the logic. Note: that are many ways to read the data and many ways to allocate and keep track of your structure index. Here an arbitrary 200 max student count was chosen. For an unlimited amount, you can either implement a reallocation scheme or create a linked-list from your structure. That would complicate the example.
Here I have used a comma separate datafile with your setup. That can be easily changed. I have chosen to read the data file line-by-line with getline() instead of fscanf. You can do it either way, although, line-oriented input with getline() is generally much more flexible. The example data file is (put together before your edit):
$ cat dat/students.txt
2158,John Q. Student,11,88.42
2159,Jane Q. Student,12,89.31
2160,Jill Q. Student,13,90.83
2161,Jack Q. Student,13,82.1
2162,Jene Q. Student,10,89.18
The code below is fairly basic, but it does employ one trick to allow iteration over all students without having to maintain or pass a count to any function used. The code allocates 200 pointers to struct using calloc instead of malloc and then later allocates space for each struct as lines are read. Allocating with calloc sets all pointers to an explicit NULL allowing you to iterate over the pointers that have been assigned and stopping when your reach the first pointer that is NULL. (you will see this before all students are printed at the end). Go through the code, build it, run it, and come back with questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* maximum number students */
#define MAXS 200
typedef struct student {
int id;
char *name;
int age;
float gpa;
} student;
int main (int argc, char **argv) {
if (argc < 2) {
printf ("filename.csv please...\n");
return 1;
}
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* characters read by getline () */
size_t n = 0; /* number of bytes to allocate */
char *p = NULL; /* pointer to use parsing line */
char *sp = NULL; /* 2nd pointer to use parsing line */
student **students = NULL; /* ptr to array of stuct student */
int cnt = 0; /* counter for number allocated */
int field = 0; /* couter for field in line */
int it = 0;
FILE *fp;
fp = fopen (argv[1], "r"); /* open file , read only */
if (!fp) {
fprintf (stderr, "failed to open file for reading\n");
return 1;
}
students = calloc (MAXS, sizeof (*students)); /* allocate 200 ptrs set to NULL */
// (1)
while ((read = getline (&line, &n, fp)) != -1) { /* read each line in input file */
sp = p = line; /* set start ptr and ptr to beginning of line */
field = 0; /* set/reset field to 0 */
// (2)
students[cnt] = malloc (sizeof (**students)); /* alloc a stuct with malloc */
// (3)
/* parse id, name, age, gpa */
while (*p) /* for each character in line */
{
if (*p == ',') /* if ',' end of field found */
{
*p = 0; /* set as null-term char (temp) */
if (field == 0) students[cnt]->id = atoi (sp);
if (field == 1) students[cnt]->name = strdup (sp); /* strdup allocates for you */
if (field == 2) students[cnt]->age = atoi (sp);
*p = ','; /* replace with original ',' */
sp = p + 1; /* set new start ptr start pos */
field++; /* update field count */
}
p++; /* increment pointer p */
}
students[cnt]->gpa = strtof (sp, NULL); /* read gpa (sp already set to begin) */
cnt++; /* increment students count */
}
/* iterate over all students and print */
printf ("\nThe students in the class are:\n\n");
while (students[it])
{
printf (" %d %s %d %6.2f\n",
students[it]->id, students[it]->name, students[it]->age, students[it]->gpa);
it++;
}
int total = it;
printf ("\nTotal number of students: %d\n", total);
/* compute average age and gpa*/
float avg_age = 0;
float avg_gpa = 0;
for (it = 0; it < total; it++)
{
avg_age += (float)students[it]->age;
avg_gpa += students[it]->gpa;
}
avg_age /= (float)total;
avg_gpa /= (float)total;
printf ("Average Age of students : %.2f\n", avg_age);
printf ("Average GPA of students : %.2f\n\n", avg_gpa);
// free memory
return 0;
}
build:
gcc -Wall -Wextra -o student structstudent.c
Note: always compile with at least -Wall -Wextra warning enabled to help point out problems in your code.
output:
./student students.txt
The students in the class are:
2158 John Q. Student 11 88.42
2159 Jane Q. Student 12 89.31
2160 Jill Q. Student 13 90.83
2161 Jack Q. Student 13 82.10
2162 Jene Q. Student 10 89.18
Total number of students: 5
Average Age of students : 11.80
Average GPA of students : 87.97
validations removed:
// Validation checks:
// (1)
// if (!students) {
// fprintf (stderr, "calloc failed to allocate students\n");
// return 1;
// }
// (2)
// if (cnt == MAXS) { /* test cnt less than MAXS */
// fprintf (stderr, "Error: MAXS reached\n");
// break;
// /* you will realloc students here in real life */
// }
// (3)
// if (!students[cnt]) {
// fprintf (stderr, "malloc failed to allocate students\n");
// return 1;
// }
Edit to read given format:
You can replace the code above that reads a csv file in order to read the data file in the format you have given. Just replace everything from the common start and common end lines in its entirety. In addition add int linecnt = 0; in the variable declaration section:
while ((read = getline (&line, &n, fp)) != -1) { /* read each line in input file */
if ((linecnt % 4) == 0) /* after every 4th read */
students[cnt] = malloc (sizeof (**students)); /* alloc a stuct with malloc */
switch (linecnt) /* parse id, name, age, gpa */
{
case 0 : students[cnt]->id = atoi (line);
linecnt++; /* increment linecnt */
break;
case 1 : line[read -1] = 0; /* strip newline char */
students[cnt]->name = strdup (line);
linecnt++;
break;
case 2 : students[cnt]->age = atoi (line);
linecnt++;
break;
case 3 : students[cnt]->gpa = strtof (line, NULL);
linecnt = 0; /* reset linecnt to 0 */
cnt++; /* increment students count */
break;
}
}
/* iterate over all students and print */
printf ("\nThe students in the class are:\n\n");
while (students[it])
{
printf (" %3d %16s %d %6.2f\n",
students[it]->id, students[it]->name, students[it]->age, students[it]->gpa);
it++;
}
Build, Run and New Output:
gcc -Wall -Wextra -o students2 students2.c
$ ./students2 students_2.txt
The students in the class are:
1 Bob Smith 24 3.50
2 Jill Williams 23 3.60
3 Tom Jones 32 2.40
4 Julie Jackson 21 3.10
5 Al Brown 23 3.35
6 Juan Garcia 22 3.40
-7 Melissa Davis 20 3.20
8 Jack Black 44 1.10
Total number of students: 8
Average Age of students : 26.12
Average GPA of students : 2.96

fscanf, structs, and using the struct c programming

/*Project 1
Student records
1. Read the file with the records
2. store them
3. sort them
4. output them
ex. input and output (SORTED by student ID
2040003 AAAA BBBBBBBBB ComputerScience 3.45
2040002 AAA CCC ElectricalEngineering 3.01
2040005 AAAAAAAAAAAAAAAAA BBB ComputerScience 3.60
2040002,AAA,CCC,ElectricalEngineering,3.01
2040003,AAAA,BBBBBBBBB,ComputerScience,3.45
2040005,AAAAAAAAAAAAAAAAA,BBB,ComputerScience,3.60
char* name = malloc(256*sizeof(char));
*/
int main()
{
typedef struct StudentRecords
{
int StudentID; //must be of size 7 between 1000000 and 9999999
char *Firstname; //= MALLOC(256*sizeof(char)); // must be any length and allocate memory dynamically.
char *Lastname; //= MALLOC(256*sizeof(char));
char *Department; //= MALLOC(256*sizeof(char));
float GPA; // must be between 0 and 4
} STUDENTRECORDS;
/*
First job is read the file
*/
//set variables
int i=0;
char filecontent, file_name[100];
FILE *fp;
STUDENTRECORDS StudentRecords[300];
STUDENTRECORDS a[300];
int size =0;
printf("Enter directory of file\n"); // instructs user to enter directory of file
gets(file_name); //prompt use
fp = fopen(file_name,"r"); //opens the file "r" is read mode for fopen()
// here is a check to see if fp is empty and throw an error if so
if (fp == NULL)
{
perror("Could not open file\n");
exit(EXIT_FAILURE);
}
printf("The contents of %s file are :\n", file_name); // just prints the file name (file_name) you are prompted for
// here is where the printing of contents actually occurs
while ((filecontent = fgetc(fp)) != EOF) // I think EOF is end of feed here, not 100%
{
printf("%c",filecontent);
}
//I thought this line was to figure out how many lines are in the text, but it isnt working.
while (!feof(fp))
{
read(StudentRecords, i, fp);
i++;
}
//because the while statement isnt working, Ive elected to setting size to 3 in order to continue coding.
size = i = 3;
printf("Size = %d\n", size);
//I thought this essentially put the files contents into
for (i = 0; i < size; ++i)
fscanf(fp, "%d %s %s %s %f\n", &StudentRecords[i].StudentID, &StudentRecords[i].Firstname, &StudentRecords[i].Lastname, &StudentRecords[i].Department, &StudentRecords[i].GPA);
for (i = 0; i < size; ++i)
printf("%s", StudentRecords[i]);
//printf("%d %s %s %s %f/n", &StudentRecords[i].StudentID, &StudentRecords[i].Firstname, &StudentRecords[i].Lastname, &StudentRecords[i].Department, &StudentRecords[i].GPA);
for (i = 0; i < size; ++i)
fscanf(fp, "%d %s %s %s %f\n", &a[i].StudentID, &a[i].Firstname, &a[i].Lastname, &a[i].Department, &a[i].GPA);
for (i = 0; i < size; ++i)
printf("%s", a[i]);
//printf("%d %s %s %s %f/n", &a[i].StudentID, &a[i].Firstname, &a[i].Lastname, &a[i].Department, &a[i].GPA);
// fclose() must follow an fopen()
fclose(fp);
//printf("%g", &StudentRecords);
// return code
return 0;
}
How do I add information into a struct and print it or use it? This is what
i have so far. I've tried many different things and to no avail. I think the problem is with my initializing my struct for use. I can't get it right. I've tried searching for a solution, but each one is different and don't explain much.
Thanks for any suggestions.
Please find example code for reading content from file and storing it in structure also for this example have only take 5 student data entry(you can change as you wish) And on which criteria you want to do sorting? So i leave sorting on you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ENTRY 5
typedef struct StudentRecords
{
int StudentID; //must be of size 7 between 1000000 and 9999999
char *Firstname; //= MALLOC(256*sizeof(char)); // must be any length and allocate memory dynamically.
char *Lastname; //= MALLOC(256*sizeof(char));
char *Department; //= MALLOC(256*sizeof(char));
float GPA; // must be between 0 and 4
} STUDENTRECORDS;
int main()
{
/*
First job is read the file
*/
//set variables
int i=0;
char filecontent, file_name[100];
FILE *fp;
STUDENTRECORDS StudentRecords[MAX_ENTRY];
for(i=0;i<MAX_ENTRY;i++)
{
StudentRecords[i].Firstname = malloc(sizeof(char)*256);
StudentRecords[i].Lastname = malloc(sizeof(char)*256);
StudentRecords[i].Department = malloc(sizeof(char)*256);
}
printf("Enter directory of file\n"); // instructs user to enter directory of file
gets(file_name); //prompt use
fp = fopen(file_name,"r"); //opens the file "r" is read mode for fopen()
// here is a check to see if fp is empty and throw an error if so
if (fp == NULL)
{
perror("Could not open file\n");
//exit(EXIT_FAILURE);
}
i=0;
while(EOF!=fscanf(fp, "%d %s %s %s %f\n", &StudentRecords[i].StudentID, StudentRecords[i].Firstname, StudentRecords[i].Lastname, StudentRecords[i].Department, &StudentRecords[i].GPA))
{
printf("%d %s %s %s %f\n", StudentRecords[i].StudentID, StudentRecords[i].Firstname, StudentRecords[i].Lastname, StudentRecords[i].Department, StudentRecords[i].GPA);
i++;
}
// fclose() must follow an fopen()
fclose(fp);
return 0;
}
Note: never forgot to free string which allocated by malloc after it's use.
A few comments, and some suggestions:
Was it your lecturer who said to use a linked-list? If so.. you really should do that otherwise you'll lose marks for failing to meet the spec.
EOF is 'End Of File'. feof() tells you if the stream pointer passed to it has hit EOF already.
Your while loop to print the contents is inefficient. Rather than reading every. single. character. in. the. file., you should read the entire file (or at least, large chunks thereof, let's not assume infinite memory), fclose() the stream and then operate on the read-in file.
Also, this sort of exercise lends itself very well to fixed-size record
Omitting error handling, variable declarations, structures and
using some pseudocode:
stat("/path/to/file", &statbuf)
inputfd = fopen("/path/to/file", r);
/* assuming that we only have a small file... */
contents = calloc(statbuf.st_size * sizeof(char));
/* read it all in, in one big chunk */
fread(contents, statbuf.st_size, 1, inputfd);
fclose(inputfd);
/* Now you can operate on it however you like */
bytesleft = statbuf.st_size;
/*
* this might need to go at the top of your block, depends on if you
* have enabled C99
*/
char eachline[MAXLINELENGTH];
int n = 0;
while (bytesleft > 0) {
add_new_list_element(mylist);
bzero(eachline, MAXLINELENTH);
memccpy(&eachline, contents[n], '\n', MAXLINELENGTH);
bytesleft -= sizeof(eachline);
nread = sscanf(start, "USE YOUR FORMAT STRING HERE", [variable list]);
if (nread < 0)
/* handle EOF, remember to make use of errno */
}
call_my_sort_function(mylist);
for (; thiselement != NULL; thiselement = thiselement->next)
print_salient_field_values(thiselement);

Resources