Zusammenfassung
Diese Lektion schließt die siebte Einheit des Kurses ab, die dem Text und den Dateien gewidmet ist. Sie schreibt mit dem Gelernten das Programm der Einführung neu, das ohne Warnung Daten verlor und Namen mit Akzenten falsch maß; sie rekonstruiert die Kette der Begriffe, die von der Zeichenkette zum Format einer Datei führt; sie trennt, was davon in jeder Sprache gilt, von dem, was C++ eigen ist; sie sammelt das undefinierte Verhalten der Einheit, mit dem, was der Sanitizer anhält, und dem, was niemand sieht; sie wiederholt in ihrem Abschlusstest die Fertigkeiten des Eingangstests; und sie endet mit sechs Herausforderungen, die die ganze Einheit verbinden.
Lernziele
- Echten Text mit seiner Kodierung verarbeiten: die Größe einer Zeichenkette in Bytes vorhersagen, ihre Buchstaben zählen und die Operationen vermeiden, die UTF-8 zerbrechen.
- Eingaben und Dateien robust lesen, dabei die ungültige Eingabe vom Ende der Daten unterscheiden und jede Eingabe als vollständigen Text validieren.
- Ergebnisdateien in einem spezifizierten Format erzeugen und jede ungültige Zeile mit ihrer Nummer melden.
- Das undefinierte Verhalten der Einheit erkennen (die hängende View, den Index außerhalb einer Zeichenkette) und es von den definierten Fehlschlägen unterscheiden, die ebenfalls unbemerkt bleiben.
- Im Gelernten der Einheit trennen, was in jeder Sprache gilt, von dem, was C++ eigen ist.
Was die Einheit erreicht hat
Die Einheit hatte sich vorgenommen zu lehren, wie man Text als Daten behandelt und wie ein Programm Dateien liest und schreibt, ohne an einer fehlerhaften Eingabe zu zerbrechen. Das Ziel ist erreicht, gestützt nur auf das, was die Einführung als bekannt vorausgesetzt hat: das Zeichen als Code aus Einheit 2, die Bits aus Einheit 3, die Vektoren und Datensätze aus Einheit 6 und die Funktionen mit Vertrag aus Einheit 5. Das Programm der Notenliste, das mit >> las, Ana Pérez und Luis verlor und „Íñigo“ für länger erklärte als „Carmen“, wird jetzt so geschrieben: Es liest die Zeilen einer Datei, trennt die Note am letzten Leerzeichen ab, validiert sie mit der Funktion parseInt aus der Lektion Richtig einlesen, meldet die ungültige Zeile über den Fehlerstream, misst die Namen in Zeichen und legt die Liste in einer anderen Datei ab. Damit das Beispiel für sich steht, schreibt das Programm zuerst die Datendatei, die in der Praxis schon existieren würde.
#include <charconv>#include <cstddef>#include <fstream>#include <iostream>#include <print>#include <string>#include <string_view>// Prüft, ob ein Text genau eine ganze Zahl ist.// Vorbedingung: keine.// Nachbedingung: die der Lektion „Richtig einlesen“.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;}// Zahl der Zeichen eines UTF-8-Textes.// Vorbedingung: text ist gültiges UTF-8.// Nachbedingung: gibt die Zahl der Zeichen (nicht der Bytes) von text zurück.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Íñigo 70\nAna Pérez 58\nLuis sechs\nTomás 61"); } std::ifstream in{"grades.txt"}; std::ofstream report{"report.txt"}; if (!in || !report) { std::println(std::cerr, "Die Dateien konnten nicht geöffnet werden"); 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, "Zeile {} ungültig: „{}“", 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("Längster Name: {} ({} Zeichen)", longest, letterCount(longest));}Zeile 4 ungültig: „Luis sechs“Längster Name: Ana Pérez (9 Zeichen)Der Bildschirm zeigt nur die Fehlermeldung und das Ergebnis; die Liste steht in der Datei, die der Befehl cat (in PowerShell Get-Content) nach der Ausführung des Programms anzeigt:
$ cat report.txtCarmen 65Íñigo 70Ana Pérez 58Tomás 61Jeder Teil antwortet auf einen Mangel der Einführung. Das zeilenweise Lesen und rfind erlauben Namen mit Leerzeichen; npos und parseInt weisen die Zeile von Luis zurück, melden sie mit ihrer Nummer und lassen Tomás durch; letterCount zählt Zeichen, und „Ana Pérez“, mit neun Zeichen und zehn Bytes, gewinnt zu Recht; und report.txt ist vollständig, wenn main endet und sein Stream geschlossen wird.
Zeichenketten
Die Lektion Zeichenketten stellte std::string als Folge von char mit Textoperationen vor. Sie lehrte, mit find zu suchen, das eine Position oder npos liefert, diesen Wert immer explizit zu vergleichen und mit substr zu extrahieren, das eine gültige Position verlangt; sie stellte std::string_view vor, sicher als Parameter und gefährlich, wenn sie den beobachteten Text überlebt, und, als Gegenstück, die C-Strings.
Text ist nicht ASCII
Die Lektion Text ist nicht ASCII trennte das Zeichen, seinen Codepunkt und seine Bytes und erklärte, wie UTF-8 die Bits auf ein bis vier Bytes verteilt. Daraus leitete sie ab, dass size Bytes zählt, dass man Zeichen mit (c & 0xC0) != 0x80 zählt, dass Umkehren, Umwandeln in Großbuchstaben oder Schneiden Byte für Byte den Text zerbricht, dass Suchen und Verketten ihn nicht zerbrechen, und was die Standardbibliothek nicht leistet.
Richtig einlesen
Die Lektion Richtig einlesen behandelte die Eingabe als nicht vertrauenswürdige Grenze. Sie lehrte, Zeilen mit std::getline zu lesen, mit fail() und eof() die ungültige Eingabe, die Zahl, die nicht passt, und das Ende der Daten zu unterscheiden, sich mit clear und ignore zu erholen und eine Zahl als Text mit std::from_chars zu validieren, wobei verlangt wird, dass sie vollständig verbraucht wird; sie schloss mit parseInt und readInt, Grenzfunktionen mit Vertrag.
Textdateien
Die Lektion Textdateien zeigte, dass eine Datei ein Stream ist, mit dem Zustand von std::cin. Sie machte die Prüfung des Öffnens zur Regel; sie bewies, warum while (!in.eof()) einen Wert zu viel verarbeitet; sie lehrte, mit std::println(out, …) zu schreiben, ersetzend oder anhängend, und Meldungen und Ergebnisse mit std::cerr zu trennen; und sie führte alles zu einem spezifizierten und validierten Zeilenformat.
Inhalt wird geladen…
Abgeschlossen
Melde dich an, um deinen Fortschritt zu speichern