Περίληψη
Αυτό το μάθημα κλείνει την έβδομη ενότητα του κύκλου μαθημάτων, αφιερωμένη στο κείμενο και στα αρχεία. Ξαναγράφει με όσα μάθαμε το πρόγραμμα της εισαγωγής, που έχανε δεδομένα χωρίς προειδοποίηση και μετρούσε λάθος τα ονόματα γραμμένα με γράμματα εκτός ASCII· ανασυνθέτει την αλυσίδα των εννοιών που πηγαίνει από τη συμβολοσειρά στη μορφή ενός αρχείου· χωρίζει όσα από αυτά ισχύουν σε κάθε γλώσσα από όσα είναι ίδια της C++· συγκεντρώνει την απροσδιόριστη συμπεριφορά της ενότητας, με όσα σταματά ο ανιχνευτής και όσα δεν βλέπει κανείς· επαναλαμβάνει στην τελική αξιολόγηση τις δεξιότητες της αρχικής· και τελειώνει με έξι προκλήσεις που συνδυάζουν ολόκληρη την ενότητα.
Μαθησιακοί στόχοι
- Να επεξεργάζεστε πραγματικό κείμενο με την κωδικοποίησή του: να προβλέπετε το μέγεθος σε byte μιας συμβολοσειράς, να μετράτε τα γράμματά της και να αποφεύγετε τις πράξεις που χαλούν το UTF-8.
- Να διαβάζετε εισόδους και αρχεία με ανθεκτικό τρόπο, διακρίνοντας το άκυρο δεδομένο από το τέλος των δεδομένων και επικυρώνοντας κάθε δεδομένο ως ολόκληρο κείμενο.
- Να παράγετε αρχεία αποτελεσμάτων με καθορισμένη μορφή, αναφέροντας κάθε άκυρη γραμμή με τον αριθμό της.
- Να αναγνωρίζετε την απροσδιόριστη συμπεριφορά της ενότητας (την ξεκρέμαστη όψη, τον δείκτη έξω από μια συμβολοσειρά) και να τη διακρίνετε από τις ορισμένες αστοχίες που επίσης περνούν σιωπηλά.
- Να χωρίζετε, σε όσα μάθατε στην ενότητα, όσα ισχύουν σε κάθε γλώσσα από όσα είναι ίδια της C++.
Όσα πέτυχε η ενότητα
Η ενότητα έβαλε στόχο να διδάξει τον χειρισμό του κειμένου ως δεδομένου και να κάνει ένα πρόγραμμα να διαβάζει και να γράφει αρχεία χωρίς να καταρρέει μπροστά σε μια κακοσχηματισμένη είσοδο. Ο στόχος επιτεύχθηκε στηριγμένος μόνο σε όσα η εισαγωγή δήλωσε γνωστά: τον χαρακτήρα ως κωδικό της ενότητας 2, τα bit της ενότητας 3, τα διανύσματα και τις εγγραφές της ενότητας 6 και τις συναρτήσεις με σύμβαση της ενότητας 5. Το πρόγραμμα της λίστας βαθμών, που διάβαζε με >>, έχανε την Άννα Ζήση και τον Πέτρο και δήλωνε τον «Νίκος» μακρύτερο από την «Carmen», γράφεται τώρα έτσι: διαβάζει τις γραμμές ενός αρχείου, χωρίζει τον βαθμό στο τελευταίο κενό, τον επικυρώνει με τη συνάρτηση parseInt του μαθήματος Σωστή ανάγνωση της εισόδου, αναφέρει την άκυρη γραμμή στη ροή σφαλμάτων, μετρά τα ονόματα σε χαρακτήρες και αφήνει τη λίστα σε άλλο αρχείο. Για να είναι το παράδειγμα αυτοτελές, το πρόγραμμα γράφει πρώτα το αρχείο δεδομένων, που στην πράξη θα υπήρχε ήδη.
#include <charconv>#include <cstddef>#include <fstream>#include <iostream>#include <print>#include <string>#include <string_view>// Κρίνει αν ένα κείμενο είναι ακριβώς ένας ακέραιος.// Προσυνθήκη: καμία.// Μετασυνθήκη: εκείνη του μαθήματος «Σωστή ανάγνωση της εισόδου».bool parseInt(std::string_view text, int& value){ int result{}; const auto last{text.data() + text.size()}; const auto [stop, error]{std::from_chars(text.data(), last, result)}; if (error != std::errc{} || stop != last) { return false; } value = result; return true;}// Αριθμός χαρακτήρων ενός κειμένου UTF-8.// Προσυνθήκη: το text είναι έγκυρο UTF-8.// Μετασυνθήκη: επιστρέφει τον αριθμό των χαρακτήρων (όχι των byte) του text.std::size_t letterCount(std::string_view text){ std::size_t count{0}; for (const char c : text) { if ((c & 0xC0) != 0x80) { ++count; } } return count;}int main(){ { std::ofstream out{"grades.txt"}; std::println(out, "Carmen 65\nΝίκος 70\nΆννα Ζήση 58\nΠέτρος έξι\nΘωμάς 61"); } std::ifstream in{"grades.txt"}; std::ofstream report{"report.txt"}; if (!in || !report) { std::println(std::cerr, "Δεν ήταν δυνατό να ανοίξουν τα αρχεία"); return 1; } std::string line{}; std::string longest{}; int lineNumber{0}; while (std::getline(in, line)) { ++lineNumber; const std::size_t space{line.rfind(' ')}; int grade{}; if (space == std::string::npos || !parseInt(std::string_view{line}.substr(space + 1), grade)) { std::println(std::cerr, "Η γραμμή {} είναι άκυρη: «{}»", lineNumber, line); continue; } const std::string name{line.substr(0, space)}; std::println(report, "{:<10}{:>3}", name, grade); if (letterCount(name) > letterCount(longest)) { longest = name; } } std::println("Μακρύτερο όνομα: {} ({} χαρακτήρες)", longest, letterCount(longest));}Η γραμμή 4 είναι άκυρη: «Πέτρος έξι»Μακρύτερο όνομα: Άννα Ζήση (9 χαρακτήρες)Η οθόνη δείχνει μόνο το μήνυμα σφάλματος και το συμπέρασμα· η λίστα βρίσκεται στο αρχείο, που η εντολή cat (στο PowerShell, Get-Content) το δείχνει μετά την εκτέλεση του προγράμματος:
$ cat report.txtCarmen 65Νίκος 70Άννα Ζήση 58Θωμάς 61Κάθε μέρος απαντά σε ένα ελάττωμα της εισαγωγής. Η ανάγνωση ανά γραμμή και η rfind επιτρέπουν ονόματα με κενά· το npos και η parseInt απορρίπτουν τη γραμμή του Πέτρου, την αναφέρουν με τον αριθμό της και αφήνουν να συνεχίσει ο Θωμάς· η letterCount μετρά χαρακτήρες, και η «Άννα Ζήση», με εννέα χαρακτήρες και δεκαεπτά byte, κερδίζει δίκαια· και το report.txt είναι πλήρες όταν τελειώνει η main, τη στιγμή που κλείνει η ροή του.
Συμβολοσειρές
Το μάθημα Συμβολοσειρές παρουσίασε το std::string ως ακολουθία από char με πράξεις κειμένου. Δίδαξε την αναζήτηση με τη find, που επιστρέφει μια θέση ή το npos, τη σύγκριση αυτής της τιμής πάντα ρητά, και την εξαγωγή με τη substr, που απαιτεί έγκυρη θέση· παρουσίασε το std::string_view, ασφαλές ως παράμετρος και επικίνδυνο αν επιζήσει του κειμένου που παρατηρεί, και τις συμβολοσειρές της C, για αντιπαραβολή.
Το κείμενο δεν είναι ASCII
Το μάθημα Το κείμενο δεν είναι ASCII διέκρινε τον χαρακτήρα, το σημείο κώδικά του και τα byte του, και εξήγησε πώς το UTF-8 μοιράζει τα bit σε ένα έως τέσσερα byte. Από αυτό συνήγαγε ότι η size μετρά byte, ότι οι χαρακτήρες μετριούνται με (c & 0xC0) != 0x80, ότι η αντιστροφή, η μετατροπή σε κεφαλαία ή το κόψιμο byte προς byte χαλούν το κείμενο, ότι η αναζήτηση και η συνένωση δεν το χαλούν, και τι δεν κάνει η πρότυπη βιβλιοθήκη.
Σωστή ανάγνωση της εισόδου
Το μάθημα Σωστή ανάγνωση της εισόδου αντιμετώπισε την είσοδο ως μη αξιόπιστο σύνορο. Δίδαξε την ανάγνωση γραμμών με τη std::getline, τη διάκριση με τις fail() και eof() του άκυρου δεδομένου, του αριθμού που δεν χωράει και του τέλους των δεδομένων, την ανάκαμψη με τις clear και ignore, και την επικύρωση ενός αριθμού ως κειμένου με τη std::from_chars, με την απαίτηση να καταναλωθεί ολόκληρο· έκλεισε με τις parseInt και readInt, συναρτήσεις του συνόρου με σύμβαση.
Αρχεία κειμένου
Το μάθημα Αρχεία κειμένου έδειξε ότι ένα αρχείο είναι ροή, με την κατάσταση του std::cin. Έκανε κανόνα τον έλεγχο του ανοίγματος· απέδειξε γιατί το while (!in.eof()) επεξεργάζεται ένα δεδομένο παραπάνω· δίδαξε τη γραφή με std::println(out, …), με αντικατάσταση ή με προσάρτηση, και τον διαχωρισμό μηνυμάτων και αποτελεσμάτων με το std::cerr· και τα οδήγησε όλα σε μια καθορισμένη και επικυρωμένη μορφή γραμμής.
Φόρτωση του περιεχομένου…
Ολοκληρώθηκε
Συνδεθείτε για να αποθηκεύσετε την πρόοδό σας