Rückblick – Dateiparser
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 58 von 107.
Aufgabe
EinfachErstellen wir einen Konfigurationsdatei-Parser, der alle Fehlerbehandlungstechniken aus diesem Kapitel zusammenführt. Dein Parser behandelt mehrere Fehlerszenarien mit benutzerdefinierten Fehlertypen, Sentinel-Fehlern, Fehler-Wrapping und ordnungsgemäßer Bereinigung mithilfe von defer.
Du organisierst deinen Code über drei Dateien:
errors.go: Definiere die Fehlerinfrastruktur für den Parser.Erstelle zwei Sentinel-Fehler:
ErrEmptyContentmit der Meldung"file content is empty"ErrInvalidFormatmit der Meldung"invalid configuration format"
Erstelle einen benutzerdefinierten Fehlertyp
ParseErrormit den FeldernLine(int) undMessage(string). Seine MethodeError()soll Folgendes zurückgeben:parse error at line [Line]: [Message]parser.go: Erstelle den Konfigurationsparser mit mehrstufiger Fehlerbehandlung.Erstelle eine Struktur
ConfigParsermit einem booleschen Feldparsed, um zu verfolgen, ob das Parsen erfolgreich abgeschlossen wurde.Implementiere diese Methoden:
NewConfigParser() *ConfigParser– erstellt einen neuen Parser, bei demparsedauffalsegesetzt istParseLine(lineNum int, line string) error– validiert eine einzelne Zeile. Wenn die Zeile kein=-Zeichen enthält, gib einen*ParseErrormit der Zeilennummer und der Meldung"missing key-value separator"zurück. Andernfalls gibnilzurück.Parse(content string) error– die Hauptmethode zum Parsen, die:ErrEmptyContentzurückgibt, wenn der Inhalt leer istErrInvalidFormatzurückgibt, wenn der Inhalt nicht mindestens ein Zeilenumbruchzeichen enthält- den Inhalt anhand von Zeilenumbrüchen aufteilt und für jede Zeile
ParseLineaufruft (die Zeilennummern beginnen bei 1) - den Fehler mit
ParseLineumschließt und zurückgibt, wenn"parsing configuration: %w"einen Fehler zurückgibt parsedbei Erfolg auftruesetzt undnilzurückgibt
Status() string– gibt"ready"zurück, wennparsedden Wert true hat, andernfalls"not parsed"
main.go: Lies den Konfigurationsinhalt ein und demonstriere eine umfassende Fehlerprüfung.Lies zwei Eingabezeilen ein, die zusammen den Konfigurationsinhalt bilden (verbinde sie mit einem Zeilenumbruch). Verwende
defer, um unabhängig von Erfolg oder Fehler am Ende immer den Status des Parsers auszugeben.Untersuche nach dem Versuch zu parsen jeden Fehler mit
errors.Is()underrors.As(), um abhängig von der Fehlerursache eine spezifische Rückmeldung zu geben.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Erste Zeile des Konfigurationsinhalts
- Zeile 2: Zweite Zeile des Konfigurationsinhalts
Behandle Fehler mit spezifischen Meldungen:
- Wenn
errors.Is()ErrEmptyContentfindet: GibError: No configuration providedaus - Wenn
errors.Is()ErrInvalidFormatfindet: GibError: Configuration must have multiple linesaus - Wenn
errors.As()einen*ParseErrorfindet: Gib zuerst den vollständig umschlossenen Fehler aus und anschließend in einer neuen ZeileFix line [Line]: [Message] - Wenn das Parsen erfolgreich ist: Gib
Configuration parsed successfullyaus
Nach der Verarbeitung des Ergebnisses (Erfolg oder Fehler) soll der aufgeschobene Status in einer neuen Zeile ausgegeben werden: Parser status: [status]
Wenn beispielsweise host=localhost und port=8080 angegeben werden, sollte deine Ausgabe wie folgt aussehen:
Configuration parsed successfully
Parser status: readyUnd bei host=localhost und invalid line sollte deine Ausgabe wie folgt aussehen:
parsing configuration: parse error at line 2: missing key-value separator
Fix line 2: missing key-value separator
Parser status: not parsedProbier es selbst
package main
import (
"bufio"
"errors"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Erste Zeile der Konfiguration lesen
line1, _ := reader.ReadString('\n')
line1 = line1[:len(line1)-1] // Abschließenden Zeilenumbruch entfernen
// Zweite Zeile der Konfiguration lesen
line2, _ := reader.ReadString('\n')
if len(line2) > 0 && line2[len(line2)-1] == '\n' {
line2 = line2[:len(line2)-1]
}
// Die beiden Zeilen mit einem Zeilenumbruch verbinden, um den Konfigurationsinhalt zu bilden
content := line1 + "\n" + line2
// Einen neuen Parser erstellen
parser := NewConfigParser()
// TODO: defer verwenden, um am Ende immer den Status des Parsers auszugeben
// Format: "Parser status: [status]"
// TODO: Versuchen, den Inhalt zu parsen
// TODO: Fehler mit errors.Is() und errors.As() behandeln
// - Wenn errors.Is() ErrEmptyContent findet: "Error: No configuration provided" ausgeben
// - Wenn errors.Is() ErrInvalidFormat findet: "Error: Configuration must have multiple lines" ausgeben
// - Wenn errors.As() einen *ParseError findet: den vollständigen gewrappten Fehler ausgeben,
// dann in einer neuen Zeile "Fix line [Line]: [Message]" ausgeben
// - Wenn das Parsen erfolgreich ist: "Configuration parsed successfully" ausgeben
_ = errors.Is // Hinweis: errors.Is für Sentinel-Fehler verwenden
_ = errors.As // Hinweis: errors.As für benutzerdefinierte Fehlertypen verwenden
_ = fmt.Println
}
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler