568 Stimmen

Lesen einer Datei Zeile für Zeile in Go

Ich kann nicht finden file.ReadLine Funktion in Go.

Wie kann man eine Datei Zeile für Zeile lesen?

9 Stimmen

Ab Go1.1 ist bufio.Scanner die beste Möglichkeit, dies zu tun.

957voto

Stefan Arentz Punkte 32936

In Go 1.1 und neuer ist der einfachste Weg, dies zu tun, mit einer bufio.Scanner . Hier ist ein einfaches Beispiel, das Zeilen aus einer Datei liest:

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("/path/to/file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // optionally, resize scanner's capacity for lines over 64K, see next example
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

Dies ist der sauberste Weg, um aus einer Reader Zeile für Zeile.

Es gibt eine Einschränkung: Bei Zeilen, die länger als 65536 Zeichen sind, macht der Scanner einen Fehler. Wenn Sie wissen, dass Ihre Zeilenlänge größer als 64K ist, verwenden Sie die Buffer() Methode, um die Kapazität des Scanners zu erhöhen:

...
scanner := bufio.NewScanner(file)

const maxCapacity int = longLineLen  // your required line length
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)

for scanner.Scan() {
...

47 Stimmen

Und da der Auftraggeber darum bat, eine Datei zu überprüfen, wäre es trivial, zunächst file, _ := os.Open("/path/to/file.csv") und scannen Sie dann den Dateihandle: scanner := bufio.NewScanner(file)

26 Stimmen

Das Problem ist, dass Scanner.Scan() auf eine Puffergröße von 4096 []Byte pro Zeile beschränkt ist. Sie erhalten bufio.ErrTooLong Fehler, der bufio.Scanner: token too long wenn die Schlange zu lang ist. In diesem Fall müssen Sie bufio.ReaderLine() oder ReadString() verwenden.

12 Stimmen

Nur meine $0.02 - dies ist die korrekteste Antwort auf der Seite :)

197voto

HINWEIS: Die akzeptierte Antwort war in frühen Versionen von Go richtig. Siehe die höchstbewertete Antwort enthält den neueren idiomatischen Weg, dies zu erreichen.

Es gibt eine Funktion ReadLine im Paket bufio .

Bitte beachten Sie, dass die Funktion eine unvollständige Zeile zurückgibt, wenn die Zeile nicht in den Lesepuffer passt. Wenn Sie in Ihrem Programm immer eine ganze Zeile mit einem einzigen Funktionsaufruf lesen wollen, müssen Sie die ReadLine Funktion in Ihre eigene Funktion, die die ReadLine in einer for-Schleife.

bufio.ReadString('\n') nicht vollständig gleichwertig ist mit ReadLine denn ReadString ist nicht in der Lage, den Fall zu behandeln, dass die letzte Zeile einer Datei nicht mit dem Zeilenumbruchzeichen endet.

49 Stimmen

Aus den Unterlagen: "ReadLine ist ein primitives Zeilenleseprogramm auf niedriger Ebene. Die meisten Aufrufer sollten ReadBytes(') verwenden. \n ') oder ReadString(' \n ') oder verwenden Sie einen Scanner."

15 Stimmen

@mdwhatcott Warum spielt es eine Rolle, dass es sich um ein "Low-Level-Line-Reading-Primitiv" handelt? Wie kommt man zu dem Schluss, dass "die meisten Aufrufer ReadBytes(') verwenden sollten? \n ') oder ReadString(' \n ') oder verwenden Sie einen Scanner."?

16 Stimmen

@CharlieParker - Ich bin mir nicht sicher, ich zitiere nur aus den Unterlagen, um den Kontext zu verdeutlichen.

64voto

Malcolm Punkte 2296

EDIT: Ab go1.1 ist die idiomatische Lösung die Verwendung von bufio.Scanner

Ich habe eine Methode entwickelt, mit der man jede Zeile aus einer Datei lesen kann. Die Funktion Readln(*bufio.Reader) gibt eine Zeile zurück (ohne \n ) aus der zugrunde liegenden bufio.Reader-Struktur.

// Readln returns a single line (without the ending \n)
// from the input buffered reader.
// An error is returned iff there is an error with the
// buffered reader.
func Readln(r *bufio.Reader) (string, error) {
  var (isPrefix bool = true
       err error = nil
       line, ln []byte
      )
  for isPrefix && err == nil {
      line, isPrefix, err = r.ReadLine()
      ln = append(ln, line...)
  }
  return string(ln),err
}

Sie können Readln verwenden, um jede Zeile einer Datei zu lesen. Der folgende Code liest jede Zeile in einer Datei und gibt jede Zeile auf stdout aus.

f, err := os.Open(fi)
if err != nil {
    fmt.Printf("error opening file: %v\n",err)
    os.Exit(1)
}
r := bufio.NewReader(f)
s, e := Readln(r)
for e == nil {
    fmt.Println(s)
    s,e = Readln(r)
}

Zum Wohl!

23 Stimmen

Ich habe diese Antwort geschrieben, bevor Go 1.1 herauskam. Go 1.1 hat ein Scanner-Paket in der stdlib., die die gleiche Funktionalität wie meine Antwort bietet. Ich würde empfehlen, Scanner anstelle meiner Antwort zu verwenden, da Scanner in der stdlib. enthalten ist. Viel Spaß beim Hacken! :-)

49voto

zouying Punkte 1179

Es gibt zwei gängige Methoden, um eine Datei Zeile für Zeile zu lesen.

  1. bufio.Scanner verwenden
  2. ReadString/ReadBytes/... in bufio.Reader verwenden

In meinem Testfall, ~250MB, ~2.500.000 Zeilen bufio.Scanner(Zeitaufwand: 0.395491384s) ist schneller als bufio.Reader.ReadString(Zeitaufwand: 0.446867622s).

Quellcode: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line

Datei lesen mit bufio.Scanner,

func scanFile() {
    f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
    if err != nil {
        log.Fatalf("open file error: %v", err)
        return
    }
    defer f.Close()

    sc := bufio.NewScanner(f)
    for sc.Scan() {
        _ = sc.Text()  // GET the line string
    }
    if err := sc.Err(); err != nil {
        log.Fatalf("scan file error: %v", err)
        return
    }
}

Datei lesen mit bufio.Reader,

func readFileLines() {
    f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
    if err != nil {
        log.Fatalf("open file error: %v", err)
        return
    }
    defer f.Close()

    rd := bufio.NewReader(f)
    for {
        line, err := rd.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }

            log.Fatalf("read file line error: %v", err)
            return
        }
        _ = line  // GET the line string
    }
}

2 Stimmen

Beachten Sie, dass diese bufio.Reader Beispiel liest die letzte Zeile einer Datei nicht, wenn sie nicht mit einem Zeilenumbruch endet. ReadString gibt sowohl die letzte Zeile als auch io.EOF in diesem Fall.

0 Stimmen

Der Code, der bufio.Reader verwendet, verliert die letzte Zeile der Datei. Wenn err== io.EOF kann er nicht direkt abbrechen, da die letzte Zeile der Datei in der Zeitleiste steht.

22voto

Kokizzu Punkte 22415

Beispiel aus diesem .

func readLine(path string) {
  inFile, err := os.Open(path)
  if err != nil {
     fmt.Println(err.Error() + `: ` + path)
     return
  }
  defer inFile.Close()

  scanner := bufio.NewScanner(inFile)
  for scanner.Scan() {
    fmt.Println(scanner.Text()) // the line
  }
}

aber dies führt zu einem Fehler, wenn eine Zeile größer als der Puffer des Scanners ist.

Wenn das passiert ist, benutze ich Folgendes reader := bufio.NewReader(inFile) meinen eigenen Puffer zu erstellen und zu verketten, entweder mit ch, err := reader.ReadByte() ou len, err := reader.Read(myBuffer)

Eine andere Methode, die ich verwende (ersetzen Sie os.Stdin mit Datei wie oben), diese konkaten, wenn Zeilen lang sind (isPrefix) und ignoriert leere Zeilen:

func readLines() []string {
  r := bufio.NewReader(os.Stdin)
  bytes := []byte{}
  lines := []string{}
  for {
    line, isPrefix, err := r.ReadLine()
    if err != nil {
      break
    }
    bytes = append(bytes, line...)
    if !isPrefix {
      str := strings.TrimSpace(string(bytes))
      if len(str) > 0 {
        lines = append(lines, str)
        bytes = []byte{}
      }
    }
  }
  if len(bytes) > 0 {
    lines = append(lines, string(bytes))
  }
  return lines
}

0 Stimmen

Können Sie erklären, warum -1 ?

0 Stimmen

Ich denke, diese Lösung ist ein wenig zu kompliziert, Sie nicht?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X