2015-03-28 21 views
6

Mam płaski plik z 339276 wierszami tekstu o wielkości 62,1 MB. Próbuję przeczytać we wszystkich liniach, analizować je w oparciu o pewne warunki, które mam, a następnie wstawić je do bazy danych.Jak mogę odczytać duży płaski plik w Golang

Początkowo próbowałam użyć pętli bufio.Scan() i bufio.Text(), aby uzyskać linię, ale kończyło mi się miejsce w buforze. Przełączyłem się na używanie bufio.ReadLine/ReadString/ReadByte (próbowałem każdego) i miałem taki sam problem z każdym. Nie miałem wystarczającej ilości miejsca w buforze.

Próbowałem używać odczytu i ustawiania rozmiaru bufora, ale jak mówi dokument, w rzeczywistości stała, która może być mniejsza, ale nigdy większa niż 64 * 1024 bajty. Następnie spróbowałem użyć File.ReadAt, gdzie ustawiłem początkowy postilion i przeniosłem go, ponieważ przyniosłem każdą sekcję bezskutecznie. Mam spojrzał na następujących przykładów i wyjaśnień (lista nie jest wyczerpująca):

Read text file into string array (and write) How to Read last lines from a big file with Go every 10 secs reading file line by line in go

Jak czytać całego pliku (albo linia po linii lub całego rzeczy na raz) do plasterek, więc mogę iść do rzeczy?

Oto kod, który próbowałem:

    file, err := os.Open(feedFolder + value) 
       handleError(err) 
       defer file.Close() 
       //    fileInfo, _ := file.Stat() 
       var linesInFile []string 

      r := bufio.NewReader(file) 
      for { 
        path, err := r.ReadLine("\n") // 0x0A separator = newline 

        linesInFile = append(linesInFile, path) 
        if err == io.EOF { 
          fmt.Printf("End Of File: %s", err) 
          break 
        } else if err != nil { 
          handleError(err) // if you return error 
        } 
      } 
      fmt.Println("Last Line: ", linesInFile[len(linesInFile)-1]) 

Oto coś innego Próbowałem:

var fileSize int64 = fileInfo.Size() 
    fmt.Printf("File Size: %d\t", fileSize) 
    var bufferSize int64 = 1024 * 60 
    bytes := make([]byte, bufferSize) 
    var fullFile []byte 
    var start int64 = 0 
    var interationCounter int64 = 1 
    var currentErr error = nil 
     for currentErr != io.EOF { 
      _, currentErr = file.ReadAt(bytes, st) 
      fullFile = append(fullFile, bytes...) 
      start = (bufferSize * interationCounter) + 1 
      interationCounter++ 
      } 
    fmt.Printf("Err: %s\n", currentErr) 
    fmt.Printf("fullFile Size: %s\n", len(fullFile)) 
    fmt.Printf("Start: %d", start) 

    var currentLine []string 


    for _, value := range fullFile { 
     if string(value) != "\n" { 
      currentLine = append(currentLine, string(value)) 
     } else { 
     singleLine := strings.Join(currentLine, "") 
     linesInFile = append(linesInFile, singleLine) 
     currentLine = nil 
       } 
     } 

Jestem na straty. Albo nie rozumiem dokładnie, jak działa bufor, albo nie rozumiem czegoś innego. Dziękuje za przeczytanie.

+3

Nie czytać to wszystko na raz . Gotuj na parze. Użyj 'bufio.Scanner' (ponieważ wydajesz się wskazywać na linię), przetwórz linię, wstaw do bazy, * potem zapomnij o tej linii *. –

+0

Dziękuję za odpowiedź. Jak mogę zapomnieć o tej linii? W moich próbach użycia bufio.Scanner, gdy w moim pliku trafiam wiersz 63700 (z grubsza), przestaję czytać w nowych liniach. Rozumiem, że to dlatego, że uderzyłem w skaner MaxScanTokenSize (http://golang.org/pkg/bufio/#pkg-constants).Chciałbym przeczytać wiersz, przeanalizować go i wyrzucić, ale nie wiem jak to zrobić, aby skaner przesuwał się przez cały plik. – rvrtex

+0

@DaveC Hm ... Bufory na parze. – fuz

Odpowiedz

5

bufio.Scan() i bufio.Text() w pętli doskonale działa na mnie na wiele plików o większym rozmiarze, więc przypuszczam, że masz linie przekroczyła pojemność buforową. Następnie

  • sprawdzenie linii kończącej
  • i który Go wersja użyć path, err :=r.ReadLine("\n") // 0x0A separator = newline? Wygląda func (b *bufio.Reader) ReadLine() (line []byte, isPrefix bool, err error) ma wartość zwracaną isPrefix specjalnie dla przypadku użycia http://golang.org/pkg/bufio/#Reader.ReadLine
+1

To jest właściwy sposób, aby to zrobić. Po kilku ponownych rozważaniach, aby zapomnieć o każdym wprowadzonym wierszu, zgodnie z sugestią @DaveC i używając '.Scan()' i '.Text()' uruchomiłem go ponownie i miałem ten sam problem. Potem poszedłem i spojrzałem na plik, w którym faktycznie uruchomiłem program i stwierdziłem, że plik jest problemem. Program robił dokładnie to, co powinien, a ja miałem złe pliki po stronie serwera. Lekcja, której się nauczyłem, czasami nie jest to złe programowanie, ale złe wejście. Dzięki za pomoc, dzięki niej mój program działał znacznie wydajniej. – rvrtex

3

Nie jest jasne, czy należy przeczytać we wszystkich liniach przed analizowaniem i wstawieniem ich do bazy danych. Staraj się tego unikać.

Masz mały plik: "płaski plik z 339276 wierszami tekstu o wielkości 62,1 MB." Na przykład,

package main 

import (
    "bytes" 
    "fmt" 
    "io" 
    "io/ioutil" 
) 

func readLines(filename string) ([]string, error) { 
    var lines []string 
    file, err := ioutil.ReadFile(filename) 
    if err != nil { 
     return lines, err 
    } 
    buf := bytes.NewBuffer(file) 
    for { 
     line, err := buf.ReadString('\n') 
     if len(line) == 0 { 
      if err != nil { 
       if err == io.EOF { 
        break 
       } 
       return lines, err 
      } 
     } 
     lines = append(lines, line) 
     if err != nil && err != io.EOF { 
      return lines, err 
     } 
    } 
    return lines, nil 
} 

func main() { 
    // a flat file that has 339276 lines of text in it for a size of 62.1 MB 
    filename := "flat.file" 
    lines, err := readLines(filename) 
    fmt.Println(len(lines)) 
    if err != nil { 
     fmt.Println(err) 
     return 
    } 
} 
0

Wydaje mi się ten wariant readLines jest krótsza i szybsza niż sugerowane peterSO

func readLines(filename string) (map[int]string, error) { 
    lines := make(map[int]string) 

    data, err := ioutil.ReadFile(filename) 
    if err != nil { 
     return nil, err 
    } 

    for n, line := range strings.Split(string(data), "\n") { 
     lines[n] = line 
    } 

    return lines, nil 
}