Ασκήσεις Αρχείων σε γλώσσα C

Εκφωνήσεις με Επισημάνσεις & Συμβουλές

FILE I/O

📚 Σύνοψη Συναρτήσεων Αρχείων

ΣυνάρτησηΧρήση
fopen(filename, mode)Ανοίγει αρχείο — επιστρέφει FILE* ή NULL
fclose(fp)Κλείνει το αρχείο (ΠΑΝΤΑ στο τέλος)
fprintf(fp, ...)Γράφει formatted text (όπως printf)
fscanf(fp, ...)Διαβάζει formatted text (όπως scanf)
fgetc(fp)Διαβάζει έναν χαρακτήρα
fputc(ch, fp)Γράφει έναν χαρακτήρα
fgets(buf, size, fp)Διαβάζει μία γραμμή
EOFΕιδική τιμή για "τέλος αρχείου"

Modes: "r" read · "w" write (σβήνει το υπάρχον!) · "a" append · "r+" read+write

📄 Άσκηση 1 — Εγγραφή με φίλτρο και sentinel

Εκφώνηση

Γράψτε πρόγραμμα σε C το οποίο:

  • Δημιουργεί αρχείο με όνομα askisi1.txt.
  • Ζητά από τον χρήστη να εισάγει αριθμούς (έναν-έναν).
  • Αποθηκεύει στο αρχείο μόνο τους αριθμούς που βρίσκονται στο διάστημα [10, 20].
  • Η εισαγωγή τερματίζει όταν ο χρήστης δώσει -1 (sentinel value).

💡 Επισημάνσεις

  • Άνοιγμα για εγγραφή: fopen("askisi1.txt", "w"). Το "w" σβήνει υπάρχον αρχείο.
  • Έλεγχος NULL: ΠΑΝΤΑ μετά το fopen. if (fp == NULL) return 1;
  • Priming read: Διαβάζουμε την πρώτη τιμή ΠΡΙΝ το while. Μέσα στο loop, κάνουμε την επόμενη ανάγνωση στο τέλος. Έτσι το -1 δεν γράφεται ποτέ.
  • Σωστός έλεγχος εύρους: if (num >= 10 && num <= 20) — διπλός έλεγχος με &&.
  • fprintf σαν printf: fprintf(fp, "%d\n", num); — το \n είναι απαραίτητο για νέα γραμμή.
  • Κλείσιμο: fclose(fp); πάντα στο τέλος — αλλιώς μπορεί να μην γραφτούν τα δεδομένα (buffering).

⚠️ Συνήθη Λάθη

  • Χρήση do-while χωρίς priming → το -1 γράφεται στο αρχείο.
  • Ξεχασμένο \n → όλοι οι αριθμοί σε μία γραμμή.
  • system("notepad ...") μόνο σε Windows — σε Linux/Mac χρήση xdg-open ή cat.

📄 Άσκηση 2 — Άθροισμα αριθμών από αρχείο

Εκφώνηση

Γράψτε πρόγραμμα σε C το οποίο:

  • Διαβάζει ένα αρχείο με όνομα numbers.txt.
  • Το αρχείο περιέχει αριθμούς (έναν σε κάθε γραμμή ή χωρισμένους με κενά).
  • Υπολογίζει το άθροισμα όλων των αριθμών.
  • Εμφανίζει το αποτέλεσμα στην οθόνη.

💡 Επισημάνσεις

  • Άνοιγμα για ανάγνωση: fopen("numbers.txt", "r"). Αν το αρχείο δεν υπάρχει, επιστρέφει NULL.
  • Αρχικοποίηση αθροίσματος: float sum = 0; — ΠΑΝΤΑ, αλλιώς garbage value.
  • Ιδίωμα ανάγνωσης: while (fscanf(file, "%f", &number) == 1). Η fscanf επιστρέφει πλήθος επιτυχημένων αναγνώσεων — 1 όταν διαβάσει επιτυχώς έναν αριθμό.
  • fscanf αγνοεί whitespace: νέες γραμμές, κενά, tabs — όλα αγνοούνται αυτόματα με %f ή %d.
  • Εμφάνιση με δεκαδικά: printf("Άθροισμα: %.2f\n", sum);%.2f για 2 δεκαδικά.

⚠️ Συνήθη Λάθη

  • Ξεχασμένη αρχικοποίηση sum = 0.
  • Έλεγχος != EOF αντί == 1 — μπορεί να κάνει infinite loop αν διαβάσει σκουπίδια.
  • Λάθος format: %d για ακεραίους, %f για float, %lf για double.

📄 Άσκηση 3 — Μέτρημα φωνηέντων

Εκφώνηση

Γράψτε πρόγραμμα σε C το οποίο:

  • Διαβάζει ένα αρχείο κειμένου (π.χ. text.txt).
  • Μετρά πόσα φωνήεντα υπάρχουν (a, e, i, o, u).
  • Εμφανίζει το πλήθος στην οθόνη.

💡 Επισημάνσεις

  • Ανάγνωση χαρακτήρα-χαρακτήρα: fgetc(file) επιστρέφει τον επόμενο χαρακτήρα ή EOF στο τέλος.
  • Κρίσιμο detail: Η μεταβλητή ch πρέπει να είναι int, ΟΧΙ char! Το EOF είναι -1 και με unsigned char δεν μπορεί να αναπαρασταθεί σωστά.
  • Σωστό ιδίωμα: while ((ch = fgetc(file)) != EOF) — προσοχή στις παρενθέσεις γύρω από την ανάθεση.
  • Έλεγχος φωνήεντος: μία γραμμή με πολλαπλά ||: if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
  • Αν θέλουμε και κεφαλαία: πρόσθεσε 'A', 'E', ... ή χρησιμοποίησε tolower(ch) από το <ctype.h>.
  • Αρχικοποίηση: int count = 0;

⚠️ Συνήθη Λάθη

  • char ch αντί int ch → bug που εμφανίζεται σε συγκεκριμένα platforms.
  • Ξεχασμένες παρενθέσεις: while (ch = fgetc(file) != EOF) — λάθος λόγω προτεραιότητας (η != έχει προτεραιότητα πάνω από την =).
  • Μέτρημα μόνο πεζών χωρίς ενημέρωση του χρήστη.

📄 Άσκηση 4 — Αποθήκευση ονομάτων (με fgets)

Εκφώνηση

Γράψτε πρόγραμμα σε C το οποίο:

  • Ζητάει από τον χρήστη 5 ονόματα φοιτητών.
  • Αποθηκεύει τα ονόματα στο αρχείο students.txt.
  • Τα ονόματα μπορεί να περιέχουν κενά (π.χ. "Γιώργος Παπαδόπουλος").

💡 Επισημάνσεις — Γιατί fgets αντί scanf;

  • Πρόβλημα με scanf: το scanf("%s", name) σταματά στο πρώτο κενό. Αν δώσεις "Γιώργος Παπαδόπουλος", αποθηκεύει μόνο "Γιώργος".
  • Λύση με fgets: fgets(name, 100, stdin) διαβάζει ολόκληρη τη γραμμή μέχρι το \n (και συμπεριλαμβάνει το \n).
  • Μέγεθος buffer: char name[100]; για να χωράνε και τα μεγάλα ονόματα.
  • 3 ορίσματα fgets: fgets(buffer, μέγιστο μέγεθος, stdin) — το stdin είναι η standard input (πληκτρολόγιο).
  • Εγγραφή χωρίς \n: fprintf(file, "%s", name); — ΟΧΙ "%s\n" γιατί η fgets έχει ήδη συμπεριλάβει το \n στο string.

⚠️ Συνήθη Λάθη & Παγίδες

  • Διπλό newline: αν γράψεις fprintf(file, "%s\n", name) ενώ το name έχει ήδη \n → κενές γραμμές στο αρχείο.
  • Ανάμιξη scanf + fgets: Αν υπάρχει πριν scanf("%d", ...), αφήνει το \n στο buffer και η fgets διαβάζει κενό string! Λύση: getchar() ή scanf(" %[^\n]", ...).
  • Αφαίρεση newline: αν θες το όνομα χωρίς το τελικό \n: name[strcspn(name, "\n")] = '\0'; (χρειάζεται <string.h>).
  • Μικρό buffer: char name[10] είναι επικίνδυνο για μεγάλα ονόματα.

📄 Άσκηση 5 — Αποθήκευση ονομάτων (με scanf)

Εκφώνηση

Γράψτε πρόγραμμα σε C το οποίο:

  • Ζητά από τον χρήστη να εισάγει 5 ονόματα φοιτητών.
  • Αποθηκεύει τα ονόματα σε αρχείο με όνομα students.txt.
  • Κάθε όνομα να αποθηκεύεται σε νέα γραμμή.

💡 Επισημάνσεις

  • Buffer για string: char name[50]; — αρκετά μεγάλο buffer για το μεγαλύτερο πιθανό όνομα.
  • scanf με string: scanf("%s", name)ΟΧΙ &name! Ο πίνακας είναι ήδη διεύθυνση.
  • Προσοχή σε κενά: το %s διαβάζει μέχρι το πρώτο κενό. Για πλήρη γραμμή (π.χ. "Γιώργος Παπαδόπουλος") χρησιμοποίησε fgets(name, 50, stdin).
  • Νέα γραμμή: fprintf(file, "%s\n", name); — το \n βάζει κάθε όνομα σε δική του γραμμή.
  • Loop: for (int i = 0; i < 5; i++) { ... } — απλό counter loop για τις 5 εισαγωγές.

⚠️ Συνήθη Λάθη

  • scanf("%s", &name) — λάθος, το name είναι ήδη pointer.
  • Buffer overflow: scanf("%s", name) χωρίς όριο. Ασφαλές: scanf("%49s", name).
  • Άνοιγμα με "a" (append) αντί "w" → συσσώρευση ονομάτων σε κάθε εκτέλεση.
  • Ξεχασμένο \n → όλα τα ονόματα κολλημένα.

📄 Άσκηση 6 — Αντιγραφή αρχείου χαρακτήρα-χαρακτήρα

Εκφώνηση

Γράψτε πρόγραμμα σε C το οποίο:

  • Ανοίγει ένα αρχείο πηγής (π.χ. input.txt).
  • Δημιουργεί/ανοίγει ένα δεύτερο αρχείο (π.χ. output.txt).
  • Αντιγράφει το περιεχόμενο του πρώτου αρχείου στο δεύτερο.
  • Η αντιγραφή να γίνεται με χρήση των fgetc() και fputc().

💡 Επισημάνσεις

  • Δύο file pointers: FILE *source, *destination;
  • Δύο έλεγχοι NULL: μετά από κάθε fopen. Αν αποτύχει το δεύτερο, πρέπει να κλείσεις το πρώτο πριν κάνεις return (resource cleanup).
  • Ιδίωμα αντιγραφής:
while ((ch = fgetc(source)) != EOF) { fputc(ch, destination); }
  • fputc: Πρώτο όρισμα ο χαρακτήρας, δεύτερο το FILE* (αντίστροφα από το fprintf).
  • Κλείσιμο και των δύο: fclose(source); fclose(destination);
  • Τύπος ch: πάντα int ch — το EOF απαιτεί int.

⚠️ Συνήθη Λάθη

  • Παράλειψη κλεισίματος του πρώτου αρχείου στο error path του δεύτερου.
  • fputc(destination, ch) — αντίστροφη σειρά ορισμάτων.
  • char ch αντί int ch.
  • Αν το πηγαίο είναι binary (π.χ. εικόνα), χρήση "rb" και "wb".

✅ Γενικές Συμβουλές για όλες τις ασκήσεις

🎯 Χρυσοί Κανόνες Αρχείων

  • 1. Πάντα NULL check μετά από fopen.
  • 2. Πάντα fclose στο τέλος — αλλιώς κινδυνεύεις με απώλεια δεδομένων.
  • 3. Πάντα αρχικοποίηση μετρητών/αθροισμάτων (count = 0, sum = 0).
  • 4. Χρήση int για τον χαρακτήρα όταν διαβάζεις με fgetc.
  • 5. Πρόσεχε το mode: "w" σβήνει! Χρησιμοποίησε "a" αν θες append.
  • 6. fscanf(...) == 1 για ακριβή έλεγχο επιτυχίας ανάγνωσης.
  • 7. Μην ξεχνάς το \n στο fprintf όταν θες νέες γραμμές.