1

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
Mantas
  • 11
  • 2
  • 2
    `Physical education Harry Pleter 32` - how are you going to tokenize it? IOW, how do you know when, say, LessonName ends and TeacherName begins? – Arkadiusz Drabczyk Dec 12 '20 at 22:19
  • Ohoh, if you are using that data persistanty, you might want to read up on [database normalisation](https://en.m.wikipedia.org/wiki/Database_normalization) before you go adding tables and fields. – Neil Dec 13 '20 at 06:21
  • LessonName is only 30 characters and then TeacherName begins. I really don't know if it possible to scan right and then show it on the console. – Mantas Dec 13 '20 at 16:35

2 Answers2

1

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.

DarkAtom
  • 2,589
  • 1
  • 11
  • 27
  • This is good advice; in particular, prefer text files as much as possible. Deserialisalion can be made to respect spaces with [fscanf](https://pubs.opengroup.org/onlinepubs/000095399/functions/fscanf.html), too, but it's not the default. – Neil Dec 13 '20 at 06:50
  • I don't understand what you are talking about. `fscanf` works exactly like `scanf`, but reads the data from any file, rather than always `stdin`. When reading strings, both `scanf` and `fscanf` (and all functions part of the family) stop at a whitespace character. – DarkAtom Dec 13 '20 at 09:42
  • 1
    So maybe there are solution if I need two words to read? Maybe I can use comma to separate LessonName and TeacherName... – Mantas Dec 13 '20 at 16:33
  • 1
    I meant the `%[^,]` in the format string. Eventally, when reading variable lenght data or data that might overflow, you need more error checking then `scanf` is designed for; _cf_ the disscussion on [how do you allow spaces](https://stackoverflow.com/questions/1247989/how-do-you-allow-spaces-to-be-entered-using-scanf). – Neil Dec 13 '20 at 19:03
  • 1
    @Mantas I have added a way to read more words – DarkAtom Dec 13 '20 at 19:09
  • 1
    @Neil I think this falls out of the scope of this question. Good error checking is probably more advanced than what the OP is striving for here. – DarkAtom Dec 13 '20 at 19:09
  • @Mantas If you want to re-use your code, a reconised encoding format such as [csv](https://en.m.wikipedia.org/wiki/Comma-separated_values) (more than one word per field uses this) or [tsv](https://en.m.wikipedia.org/wiki/Tab-separated_values) is good practice. – Neil Dec 13 '20 at 20:50
0

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



  
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256