Datei zeilenweise in Go lesen: Vom Anfänger zum Profi im Jahr 2025
\n\nHaben Sie sich jemals gefragt, wie man riesige Logdateien effizient liest oder CSV-Daten in Go verarbeitet? Wenn ja, sind Sie hier genau richtig. Das Thema zeilenweises Lesen von Dateien ist eines der beliebtesten auf Stack Overflow, und das nicht ohne Grund. Go bietet uns leistungsstarke Werkzeuge, aber den richtigen Ansatz zu wählen, ist manchmal nicht einfach.
\n\nIn diesem Artikel zeigen wir nicht nur, wie man Zeilen aus einer Datei liest. Wir analysieren die Best Practices für 2025: vom schnellen bufio.Scanner bis hin zu benutzerdefinierten gepufferten Lösungen. Sie erfahren, wie Sie Fallstricke vermeiden (z. B. Zeilen länger als 64 KB), Fehler richtig behandeln und die Arbeit mit großen Datenmengen beschleunigen.
Bereit? Los geht's!
\n\nWarum ist zeilenweises Lesen nicht einfach "nehmen und lesen"?
\n\nIn Go gibt es mehrere Möglichkeiten, Dateien zu lesen, und jede hat ihren eigenen Zweck. Manche verwenden ioutil.ReadFile (übrigens seit Go 1.16 veraltet), andere os.File mit einem Puffer. Aber für die zeilenweise Verarbeitung ist der König bufio.Scanner.
Warum? Weil er:
\n- \n
- Speichereffizient ist – lädt nicht die gesamte Datei in den RAM \n
- Einfach zu bedienen ist – minimaler Code \n
- Fehler behandeln kann – jeden Schritt überprüft \n
Es gibt jedoch einen Haken: Der Standardpuffer des Scanner beträgt 64 KB. Wenn Ihre Zeile länger ist, erhalten Sie einen Fehler. Für solche Fälle sind andere Ansätze erforderlich.
\n\nBasisbeispiel: bufio.Scanner
\n\nBeginnen wir mit der gebräuchlichsten Methode. Hier ist ein minimaler funktionierender Code:
\n\npackage main\n\nimport (\n "bufio"\n "fmt"\n "log"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("data.txt")\n if err != nil {\n log.Fatal(err)\n }\n defer file.Close()\n\n scanner := bufio.NewScanner(file)\n for scanner.Scan() {\n line := scanner.Text()\n fmt.Println(line)\n }\n\n if err := scanner.Err(); err != nil {\n log.Fatal(err)\n }\n}\n\n\nWas ist hier wichtig?
\n- \n
defer file.Close()– schließt die Datei auch bei einer Panik \n scanner.Text()– gibt die Zeile ohne Zeilenumbruchzeichen (\\n) zurück \n scanner.Err()– überprüft, ob während des Lesens Fehler aufgetreten sind \n
Dieser Code ist ideal für 90 % der Aufgaben: Konfigurationen, Logs, kleine CSV-Dateien.
\n\nProblem mit langen Zeilen: Puffer vergrößern
\n\nStellen Sie sich vor: Sie lesen eine Datei mit JSON-Zeilen von je 100 KB. Der Scanner wird mit dem Fehler bufio.Scanner: token too long abbrechen. Die Lösung ist, den Puffer zu vergrößern:
scanner := bufio.NewScanner(file)\nbuf := make([]byte, 0, 1024*1024) // 1 MB\nscanner.Buffer(buf, 10*1024*1024) // maximal 10 MB pro Zeile\n\nfor scanner.Scan() {\n // ... Verarbeitung\n}\n\n\nWichtig: Der zweite Parameter ist die maximale Zeilengröße. Wenn die Zeile größer ist, gibt es erneut einen Fehler. Wählen Sie mit Reserve, aber nicht riesig – Speicher ist nicht unbegrenzt.
\n\nWann Scanner nicht geeignet ist: Alternativen
\n\nTrotz seiner Beliebtheit ist bufio.Scanner nicht universell. Hier sind zwei Fälle, in denen Sie über einen anderen Ansatz nachdenken sollten:
1. Riesige Dateien (Gigabyte)
\nWenn die Datei größer als der verfügbare Speicher ist (selten, aber möglich), ist Scanner immer noch gut. Wenn Sie jedoch binäre Daten lesen oder Zeilen mit benutzerdefinierten Trennzeichen verarbeiten müssen, verwenden Sie besser bufio.Reader:
reader := bufio.NewReader(file)\nfor {\n line, err := reader.ReadString('\\n')\n if err != nil {\n break\n }\n // line enthält \\n am Ende\n line = strings.TrimRight(line, "\\n")\n fmt.Println(line)\n}\n\n\nVorteil: Sie kontrollieren jeden Lesevorgang. Nachteil: Mehr Code und höheres Fehlerrisiko.
\n\n2. Verarbeitung ohne Laden in den Speicher
\nManchmal müssen Sie nur Zeilen zählen oder ein Muster finden. Dafür eignet sich bufio.Scanner