How to write to file user’s input and read from file - arrays

I’m a beginner at programming with C. I’m here because I need help from professional programmers. So, I have to do a simple program called “Lessons”. In this program, I need to use structure, an array of structure and use files to save all records.
For now, I have a struct
typed struct lessons{
char LessonName [30],
char TeacherName [20],
char TeacherLastName[20],
int numberOfStudents
} lessons
And array
lessons info[10]
As far I can understand I have an array called “info” which can handle 10 lessons information. Right?
And now I face up with my biggest problem. How should I “play” with all records?
Should I create a txt file and fill it up with some information or I should add the lesson’s information with code help?
Can anybody explain, give some examples of how should I put the new record to a static array when the user enters all information about the lesson?
And also, how to scan all records from (txt or bin) file and show it on console?
To make work easier I can give examples of records:
Physical education Harry Pleter 32
History Emily Shelton 12

If all string fields of the struct are a single word, you can do it like this:
for (int i = 0; i < sizeof info / sizeof info[0]; i++)
{
scanf("%29s %19s %19s %d", info[i].LessonName, info[i].TeacherName, info[i].TeacherLastName, &info[i].numberOfStudents);
}
This will work perfectly with the string "History Emily Shelton 12", but will however fail to work with the string "Physical education Harry Pleter 32", because Physical education is 2 words.
Reading more than one word per field
Reading more than 1 word per field can be done very similarly, but you will have to decide which is the character that separates the fields. In this example, I used comma ,:
for (int i = 0; i < sizeof info / sizeof info[0]; i++)
{
scanf("%29[^,] %19[^,] %19[^,] %d", info[i].LessonName, info[i].TeacherName, info[i].TeacherLastName, &info[i].numberOfStudents);
}
This will correctly parse the string: "Physical education, Harry, Pleter, 32". The commas are needed because the program needs to know when to stop reading a field and continue with the next one.

How should I “play” with all records?
How about 1 line of the file == 1 record?
Code needs 2 functions:
void lessons_write(FILE *destination, const lessons *source);
// return 1: success, EOF: end-of-file, 0: fail
int lessons_read(FILE *source, lessons *destination);
A key consideration: Input may not be formatted correctly and so calling code needs to know when something failed.
Consider comma separated values CSV.
void lessons_write(FILE *destination, const lessons *source) {
// Maybe add tests here to detect if the record is sane.
fprintf(destination, "%s, %s, %s, %d\n",
source->LessonName,
source->TeacherName,
source->TeacherLastName,
source->numberOfStudents);
}
int lessons_read(FILE *source, lessons *destination) {
// Use a buffer large enough for any reasonable input.
char buf[sizeof *destination * 2];
// Use fgets to read **1 line**
if (fgets(buf, sizeof buf, source) == NULL) {
return EOF;
}
// Use %n to detect end of scanning. It records the offset of the scan.
int n = 0;
sscanf(buf, " %29[^,], %19[^,], %19[^,], %d %n",
destination->LessonName,
destination->TeacherName,
destination->TeacherLastName,
&destination->numberOfStudents, &n);
// If scan did not complete or some junk at the end ...
if (n == 0 || buf[n] != '\0') {
return 0;
}
// Maybe add tests here to detect if the record is sane.
return 1;
}
Now armed with basics, consider more advanced issues:
Can a course name or teachers name contain a ',' or '\n' as that will foal up the reading? Perhaps form a bool Lessons_Valid(const lessons *source) test?
Can a course name or teachers name begin, contain or end with spaces? Or control characters?
What range is valid for # of students? Perhaps 0-9999.
How about long names?
A key to good record handling is the ability to detect garbage input.
give some examples of how should I put the new record to a static array when the user enters all information about the lesson?
How about some pseudo code to not take all the learning experience away?
open file to read, successful?
Set N as 10
lessons abc[N]
for up to N times
read record and return success
Was that the last (EOF)?
Was is a bad record (Error message)
else save it and increment read_count
close input
for read_count
print the record to stdout

Related

How do you read the integers from a text file of both characters and integers?

I was having trouble reading and storing the last set of values from a text file. For example, let's say that this is printed in a text file:
ID Grade
AA22 12
BB33 13
DD44 14
How do I read only the grades of the student and store it in an integer in order to carry out calculations?
#include <stdio.h>
#include <string.h>
#include <stdlb.h>
FILE *fp;
int counter;
int main () {
fp = fopen ("nameoffile.txt", "r");
int line[50];
while (fgets(line, 50, fp) != EOF) {
counter = counter + line;
}
printf("The total amount is %d", counter);
}
It was originally written, and the question asked was similar to the example given. I am really more concerned about the logic.
Here is some code to get you started. You can use this as a base on top of which to build the logic that suits you.
Many error checking must be done, but I left this to you to figure out what needs to be improved.
I suggest you to first analyze it and understand what exactly happens, because there were many fundamentals errors with your code.
int main(void)
{
FILE *fp;
fp = fopen("nameoffile.txt", "r");
char line[200];
char ids[20][20];
int grades[20];
int cnt;
cnt = 0;
while (fgets(line, sizeof(line), fp) != NULL)
{
if(cnt)
sscanf(line, "%s %d", ids[cnt - 1], &grades[cnt - 1]);
cnt++;
}
printf("ID GRADE\n");
for(int i = 0; i < cnt - 1; i++)
{
printf("%s %d\n", ids[i], grades[i]);
}
return 0;
}
It seems that you know how to code but having trouble breaking down the project. Breaking down the project becomes the logic. It's helpful to breakdown the project and then google each point you don't understand. Cause clearly your project is information overload to you. Just try it next time.
And sure this is a lot of stuff to do but using this method you'll save a lot of time in the long run. ✔︎
Breakdown
• Read file
• Identify the "Grade" column.
• Read only those values under the "Grade column".
• Make sure the value's characters are all isdigit()
• Convert the values to integers:atoi() then Add those values to an array for whatever use you want to make of them later. to carry out calculations as you said.
Research Breakdown
• Read file
=> C Read Text File ... Now you have access to each line
• Identify the "Grade" column
=> multiple spaces to only one => by doing this step, its now easy to identify what your Columns "ID Grade ... ..." are delimited by.
=> Find a substring of a string in C ... Now you know you're currently on the line of your HEADERS ... You'll use this headers to figure out what column your grades are under.
=> By using the strstr() example the great internet provided you with, you now have at your disposal the index number of the starting character of the word "Grade".
=> Lucky for you, you already made the column delimiter a single space, so now you loop over the string until you reach that index number.
=> But while doing so, you need to count the amount of spaces you encounter. Because when your loop reaches the index number, you would have the column number of your grades - "Grade", which is actually nothing but the number of spaces.
• Make sure the value's characters are all digits
isdigit()
=> loop to the column where your grades value lies... you can do this because you know the column number and the column delimiter.
=> when you reach that point in the string, start checking if each character after that isdigit(). by "each character after that" i mean, all characters until you reach a space.
=> if you encountered a character thats something other a digit then throw a little syntax error or something cooler than that. your program!
=> now that you know the grades value is all digits, you want to atoi() that bad boy. but how ? something like this... ... this example code is how to get that value out of the whole string. Now you need to take that value in atoi(value).
=> then you'll add the return value of atoi(value) to your values
Good luck

Reading strings with spaces from a file

I'm working on a project and I just encountered a really annoying problem. I have a file which stores all the messages that my account received. A message is a data structure defined this way:
typedef struct _message{
char dest[16];
char text[512];
}message;
dest is a string that cannot contain spaces, unlike the other fields.
Strings are acquired using the fgets() function, so dest and text can have "dynamic" length (from 1 character up to length-1 legit characters). Note that I manually remove the newline character after every string is retrieved from stdin.
The "inbox" file uses the following syntax to store messages:
dest
text
So, for example, if I have a message from Marco which says "Hello, how are you?" and another message from Tarma which says "Are you going to the gym today?", my inbox-file would look like this:
Marco
Hello, how are you?
Tarma
Are you going to the gym today?
I would like to read the username from the file and store it in string s1 and then do the same thing for the message and store it in string s2 (and then repeat the operation until EOF), but since text field admits spaces I can't really use fscanf().
I tried using fgets(), but as I said before the size of every string is dynamic. For example if I use fgets(my_file, 16, username) it would end up reading unwanted characters. I just need to read the first string until \n is reached and then read the second string until the next \n is reached, this time including spaces.
Any idea on how can I solve this problem?
#include <stdio.h>
int main(void){
char username[16];
char text[512];
int ch, i;
FILE *my_file = fopen("inbox.txt", "r");
while(1==fscanf(my_file, "%15s%*c", username)){
i=0;
while (i < sizeof(text)-1 && EOF!=(ch=fgetc(my_file))){
if(ch == '\n' && i && text[i-1] == '\n')
break;
text[i++] = ch;
}
text[i] = 0;
printf("user:%s\n", username);
printf("text:\n%s\n", text);
}
fclose(my_file);
return 0;
}
As the length of each string is dynamic then, if I were you, I would read the file first for finding each string's size and then create a dynamic array of strings' length values.
Suppose your file is:
A long time ago
in a galaxy far,
far away....
So the first line length is 15, the second line length is 16 and the third line length is 12.
Then create a dynamic array for storing these values.
Then, while reading strings, pass as the 2nd argument to fgets the corresponding element of the array. Like fgets (string , arrStringLength[i++] , f);.
But in this way you'll have to read your file twice, of course.
You can use fgets() easily enough as long as you're careful. This code seems to work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAX_MESSAGES = 20 };
typedef struct Message
{
char dest[16];
char text[512];
} Message;
static int read_message(FILE *fp, Message *msg)
{
char line[sizeof(msg->text) + 1];
msg->dest[0] = '\0';
msg->text[0] = '\0';
while (fgets(line, sizeof(line), fp) != 0)
{
//printf("Data: %zu <<%s>>\n", strlen(line), line);
if (line[0] == '\n')
continue;
size_t len = strlen(line);
line[--len] = '\0';
if (msg->dest[0] == '\0')
{
if (len < sizeof(msg->dest))
{
memmove(msg->dest, line, len + 1);
//printf("Name: <<%s>>\n", msg->dest);
}
else
{
fprintf(stderr, "Error: name (%s) too long (%zu vs %zu)\n",
line, len, sizeof(msg->dest)-1);
exit(EXIT_FAILURE);
}
}
else
{
if (len < sizeof(msg->text))
{
memmove(msg->text, line, len + 1);
//printf("Text: <<%s>>\n", msg->dest);
return 0;
}
else
{
fprintf(stderr, "Error: text for %s too long (%zu vs %zu)\n",
msg->dest, len, sizeof(msg->dest)-1);
exit(EXIT_FAILURE);
}
}
}
return EOF;
}
int main(void)
{
Message mbox[MAX_MESSAGES];
int n_msgs;
for (n_msgs = 0; n_msgs < MAX_MESSAGES; n_msgs++)
{
if (read_message(stdin, &mbox[n_msgs]) == EOF)
break;
}
printf("Inbox (%d messages):\n\n", n_msgs);
for (int i = 0; i < n_msgs; i++)
printf("%d: %s\n %s\n\n", i + 1, mbox[i].dest, mbox[i].text);
return 0;
}
The reading code will handle (multiple) empty lines before the first name, between a name and the text, and after the last name. It is slightly unusual in they way it decides whether to store the line just read in the dest or text parts of the message. It uses memmove() because it knows exactly how much data to move, and the data is null terminated. You could replace it with strcpy() if you prefer, but it should be slower (the probably not measurably slower) because strcpy() has to test each byte as it copies, but memmove() does not. I use memmove() because it is always correct; memcpy() could be used here but it only works when you guarantee no overlap. Better safe than sorry; there are plenty of software bugs without risking extras. You can decide whether the error exit is appropriate — it is fine for test code, but not necessarily a good idea in production code. You can decide how to handle '0 messages' vs '1 message' vs '2 messages' etc.
You can easily revise the code to use dynamic memory allocation for the array of messages. It would be easy to read the message into a simple Message variable in main(), and arrange to copy into the dynamic array when you get a complete message. The alternative is to 'risk' over-allocating the array, though that is unlikely to be a major problem (you would not grow the array one entry at a time anyway to avoid quadratic behaviour when the memory has to be moved during each allocation).
If there were multiple fields to be processed for each message (say, date received and date read too), then you'd need to reorganize the code some more, probably with another function.
Note that the code avoids the reserved namespace. A name such as _message is reserved for 'the implementation'. Code such as this is not part of the implementation (of the C compiler and its support system), so you should not create names that start with an underscore. (That over-simplifies the constraint, but only slightly, and is a lot easier to understand than the more nuanced version.)
The code is careful not to write any magic number more than once.
Sample output:
Inbox (2 messages):
1: Marco
How are you?
2: Tarma
Are you going to the gym today?

How to assign a string value

Can I do something like the code below to get a persons name into the firstname string?
printf("First Name? ");
scanf("%s", &firstname[11]);
yup. This stores the input in firstname, starting at index 11.
No. You should not do it this way. It is unsafe, in exactly the same way gets is unsafe.
You should instead do something like this. (I am assuming that you really do want to write to firstname starting at character position 11, which is what &firstname[11] does. If firstname is 11 bytes long and you want to write starting at position 0, you would simply use firstname and remove the various occurrences of 11 + below.)
char inbuf[80];
size_t n;
fputs("First Name? ", stdout);
fgets(inbuf, sizeof inbuf, stdin);
n = strlen(inbuf);
if (n == 0) {
fputs("No name entered\n", stderr);
exit(1);
}
if (inbuf[n-1] != '\n') {
fputs("Name too long\n", stderr);
exit(1);
}
inbuf[--n] = '\0';
if (n == 0) {
fputs("No name entered\n", stderr);
exit(1);
}
if (11 + n >= sizeof firstname) {
fputs("Name too long\n", stderr);
exit(1);
}
memcpy(&firstname[11], inbuf, n);
firstname[11 + n] = '\0';
If you reaction is that that looks like a giant pain in the behind, all I can say is, welcome to C programming. All of that is in fact necessary for robustness in the face of arbitrarily malformed input.
You should also reflect upon Falsehoods Programmers Believe About Names and then redesign your database accordingly. (Most importantly in this context: people do not necessarily divide their names into "first" and "last" and "middle" components; many people have first names that require more than 10 bytes to represent.)
EDIT: Bugs in example code should now all be corrected. Serve me right for doing memory arithmetic in my head without testing it.
The short answer to your question is, yes you can.
The long answer (and questions that go with it)...
What's the declaration of firstname?
What's in the first 11 places of filename?
Are you trying to limit the the limit the length of the user input to 11 characters and that was the syntax you thought would work?
I suspect that you are trying to limit the firstname to be at most 11 characters. The way to solve this is:
char firstname[12];
scanf("%11s", firstname);
However, as Zach pointed out, this will be problematic if you have a first name like "Mary Kay". scanf will stop at the first white space and you will end up with a first name that contains just "Mary". The better approach is to use fgets.
fgets(firstname, 12, stdin);
That should work if there is nothing else in a line.
If you expect to read the data from a file and there are other fields in a line, then you will have to deal with that additional complexity. Say you have first name, last name, and age in a line. Your input file could look something like:
John, Deer, 59
Mary Kay, Smith, 42
If your input file contains data like above, you can't use fgets to pick out the first name using fgets(firstname, 12, infile);. You will have to use fgets to read the entire line, then parse the line to extract all the relevant data.

How would I compare a string (entered by the user) to the first word of a line in a file?

I am really struggling to understand how character arrays work in C. This seems like something that should be really simple, but I do not know what function to use, or how to use it.
I want the user to enter a string, and I want to iterate through a text file, comparing this string to the first word of each line in the file.
By "word" here, I mean substring that consists of characters that aren't blanks.
Help is greatly appreciated!
Edit:
To be more clear, I want to take a single input and search for it in a database of the form of a text file. I know that if it is in the database, it will be the first word of a line, since that is how to database is formatted. I suppose I COULD iterate through every single word of the database, but this seems less efficient.
After finding the input in the database, I need to access the two words that follow it (on the same line) to achieve the program's ultimate goal (which is computational in nature)
Here is some code that will do what you are asking. I think it will help you understand how string functions work a little better. Note - I did not make many assumptions about how well conditioned the input and text file are, so there is a fair bit of code for removing whitespace from the input, and for checking that the match is truly "the first word", and not "the first part of the first word". So this code will not match the input "hello" to the line "helloworld 123 234" but it will match to "hello world 123 234". Note also that it is currently case sensitive.
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[100]; // declare space for the input string
FILE *fp; // pointer to the text file
char fileBuf[256]; // space to keep a line from the file
int ii, ll;
printf("give a word to check:\n");
fgets(buf, 100, stdin); // fgets prevents you reading in a string longer than buffer
printf("you entered: %s\n", buf); // check we read correctly
// see (for debug) if there are any odd characters:
printf("In hex, that is ");
ll = strlen(buf);
for(ii = 0; ii < ll; ii++) printf("%2X ", buf[ii]);
printf("\n");
// probably see a carriage return - depends on OS. Get rid of it!
// note I could have used the result that ii is strlen(but) but
// that makes the code harder to understand
for(ii = strlen(buf) - 1; ii >=0; ii--) {
if (isspace(buf[ii])) buf[ii]='\0';
}
// open the file:
if((fp=fopen("myFile.txt", "r"))==NULL) {
printf("cannot open file!\n");
return 0;
}
while( fgets(fileBuf, 256, fp) ) { // read in one line at a time until eof
printf("line read: %s", fileBuf); // show we read it correctly
// find whitespace: we need to keep only the first word.
ii = 0;
while(!isspace(fileBuf[ii]) && ii < 255) ii++;
// now compare input string with first word from input file:
if (strlen(buf)==ii && strstr(fileBuf, buf) == fileBuf) {
printf("found a matching line: %s\n", fileBuf);
break;
}
}
// when you get here, fileBuf will contain the line you are interested in
// the second and third word of the line are what you are really after.
}
Your recent update states that the file is really a database, in which you are looking for a word. This is very important.
If you have enough memory to hold the whole database, you should do just that (read the whole database and arrange it for efficient searching), so you should probably not ask about searching in a file.
Good database designs involve data structures like trie and hash table. But for a start, you could use the most basic improvement of the database - holding the words in alphabetical order (use the somewhat tricky qsort function to achieve that).
struct Database
{
size_t count;
struct Entry // not sure about C syntax here; I usually code in C++; sorry
{
char *word;
char *explanation;
} *entries;
};
char *find_explanation_of_word(struct Database* db, char *word)
{
for (size_t i = 0; i < db->count; i++)
{
int result = strcmp(db->entries[i].word, word);
if (result == 0)
return db->entries[i].explanation;
else if (result > 0)
break; // if the database is sorted, this means word is not found
}
return NULL; // not found
}
If your database is too big to hold in memory, you should use a trie that holds just the beginnings of the words in the database; for each beginning of a word, have a file offset at which to start scanning the file.
char* find_explanation_in_file(FILE *f, long offset, char *word)
{
fseek(f, offset, SEEK_SET);
char line[100]; // 100 should be greater than max line in file
while (line, sizeof(line), f)
{
char *word_in_file = strtok(line, " ");
char *explanation = strtok(NULL, "");
int result = strcmp(word_in_file, word);
if (result == 0)
return explanation;
else if (result > 0)
break;
}
return NULL; // not found
}
I think what you need is fseek().
1) Pre-process the database file as follows. Find out the positions of all the '\n' (carriage returns), and store them in array, say a, so that you know that ith line starts at a[i]th character from the beginning of the file.
2) fseek() is a library function in stdio.h, and works as given here. So, when you need to process an input string, just start from the start of the file, and check the first word, only at the stored positions in the array a. To do that:
fseek(inFile , a[i] , SEEK_SET);
and then
fscanf(inFile, "%s %s %s", yourFirstWordHere, secondWord, thirdWord);
for checking the ith line.
Or, more efficiently, you could use:
fseek ( inFile , a[i]-a[i-1] , SEEK_CURR )
Explanation: What fseek() does is, it sets the read/write position indicator associated with the file at the desired position. So, if you know at which point you need to read or write, you can just go there and read directly or write directly. This way, you won't need to read whole lines just to get first three words.

reading multiple variable types from single line in file C

Alright I've been at this all day and can't for the life of me get this down, maybe you chaps can help. I have a file that reads as follows
1301,105515018,"Boatswain","Michael R.",ABC, 123,="R01"
1301,103993269,"Castille","Michael Jr",ABC, 123,="R03"
1301,103993267,"Castille","Janice",ABC, 123,="R03"
1301,104727546,"Bonczek","Claude",ABC, 123,="R01"
1301,104731479,"Cruz","Akeem Mike",ABC, 123,="R01"
1301,105415888,"Digiacomo","Stephen",ABC, 123,="R02"
1301,106034479,"Annitto Grassis","Susan",ABC, 123,="R04"
1301,106034459,"Als","Christian",ABC, 123,="R01"
And here is my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME 15
#define MAX_SUBSEC 3
#define N 128
//void printArr(struct *students);
struct student{
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char subjectname[MAX_SUBSEC];
int catalog;
char section[MAX_SUBSEC];
}students[10];
int main(){
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char sub[MAX_SUBSEC];
int cat;
char sec[MAX_SUBSEC];
char fname[N];
FILE *inputf;
printf("Enter the name of the text file: ");
scanf("%123s",fname);
strcat(fname,".txt");
inputf = fopen(fname,"r");
if (inputf == NULL){
printf("I couldn't open the file for reading.\n");
exit(0);
}
//TROUBLE HERE!
fscanf(inputf, "%d,%d,%[^,]s", &students[0].term, &students[0].id,students[0].lastname);
printf("%d\n", students[0].term);
printf("%d\n", students[0].id);
printf("%s\n", students[0].lastname);
/*for (j = 1 ; j <= 10-1 ; j++){
for(k = 0 ; k <= 10-2 ; k++){
if(students[k] > students[k+1]){
temp = students[k];
students[k] = students[k+1];
students[k+1] = temp;
}
}
}*/
fclose(inputf);
system("pause");
return 0;
}
void printArr(int a[], int tally){
int i;
for(i = 0 ; i < tally ; i++){
printf("%d ", a[i]);
}
printf("\n");
}
My objective is to take each one of those values in the text file and input it to where it belongs in the struct and subsequently the struct array, but I can't get passed the first 2 ints.
Getting the lastname string, because it is a max of 15 characters, it spills over into the first name string right after it and takes what remaining characters it needs in order to fill up the lastname char array. Obviously I do not want this. As you can see I have tried strtok but it doesnt do anything, not sure what I have to do though as I have never used it before. Also have tried just including all the variables into fscanf statement, but I either get the same output, or it becomes a mess. As it is, I am extremely lost, how do I get these values into the variables they belong?!
EDIT: updated my code, I have gotten a little farther but not much. I can now print out just the last name but can not more farther from there, I cant get to the firstname string or any of the variables beyond it.
What you have there is a CSV file with quoted strings, and so I would recommend you use a CSV parser (or roll your own) rather than trying to do it all with scanf (since scanf cannot deal with quotes, e.g. commas within quoted strings). A quick Google search turns up libcsv.c which you may be able to use in your project.
With the fscanf format string "%d,%d,\"%[^\"]\",\"%[^\"]\",%[^,],%d,=\"%[^\"]\"" we can read a whole line's data. Besides, you have to define
char lastname[MAX_NAME+1];
char firstname[MAX_NAME+1];
char subjectname[MAX_SUBSEC+1];
int catalog;
char section[MAX_SUBSEC+1];
— the +1 to account for the terminating '\0' character.
I have a question for you... If you want to know how to use a diamond cutter, do you try it and see, or do you consult the manual? The problem here isn't the result of your choice, but your choice itself. Believe it or not, I have answered these questions so often that I'm tired of repeating myself. The answer is all in the manual.
Read the POSIX 2004 scanf manual — or the POSIX 2008/2013 version — and the answer this question and you'll have some idea of what you're not doing that you should be. Even fscanf code should use assert as a debugging aid to ensure the number of items read was correct.
%[^,]s It seems as though there's a mistake here. Perhaps you meant %[^,]. The %[ format specifier is a different format specifier to the %s format specifier, hence in the presumably mistaken code there are two directives: %[^,] and s. The s directive tells scanf to read an 's' and discard it.
1.There is a syntax error in
while(result != NULL){
printf(".....);
......
}
}//error
fscanf(inputf, "%s", lastname); can't read a line ,fscanf will stop when it comes across an space
fscanf reads one line at a time, and you can easily capture the contents of each line because your file is formatted pretty nicely, especially due to the comma separation (really useful if none of your separated values contain a comma).
You can pass fscanf a format like you're doing with "%d" to capture an int, "%s" to capture a string (ends at white space, be weary of this when for example trying to find a name like "Annitto Grassis, which would require 2 %s's), etc, from the currently read line of the file. You can be more advanced and use regex patterns to define the contents you want captured as chars, such as "Boatswain", a sequence comprised chars from the sets {A-Z}, {a-z}, and the {"}. You'll want to scan the file until you reach the end (signified by EOF in C) so you can do such and capture the contents of the line and appropriately assign the values to variables like so:
while( fscanf(inputf, "%d,%d,%[\"A-Za-z ],%[\"A-Za-z .]", &term, &id, lastname, firstname) != EOF) {
.... //do something with term, id, lastname, firstname - put them in a student struct
}
For more about regex, Mastering Regex by Jeff Friedl is a good book for learning about the topic.

Resources