2015-03-28 11 views
6

Ho un file flat che contiene 339276 righe di testo per una dimensione di 62.1 MB. Sto tentando di leggere tutte le righe, analizzarle in base a determinate condizioni che ho e quindi inserirle in un database.Come leggere un file flat grande in Golang

Inizialmente, ho tentato di utilizzare un ciclo bufio.Scan() e bufio.Text() per ottenere la riga ma stavo esaurendo lo spazio del buffer. Sono passato a usare bufio.ReadLine/ReadString/ReadByte (ho provato ciascuno) e ho avuto lo stesso problema con ciascuno. Non avevo abbastanza spazio nel buffer.

Ho provato a utilizzare la lettura e l'impostazione della dimensione del buffer, ma come il documento dice che in realtà è un const che può essere reso più piccolo ma mai più grande di 64 * 1024 byte. Ho quindi provato a utilizzare File.ReadAt in cui ho impostato il postilion di partenza e spostato mentre ho portato in ogni sezione senza alcun risultato. Ho guardato i seguenti esempi e spiegazioni (non un elenco esaustivo):

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

Come faccio a leggere in un intero file (sia riga per riga o il tutto in una sola volta) in una fetta quindi posso andare a fare le cose alle linee?

Ecco po 'di codice che ho provato:

    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]) 

Qui è un'altra cosa ho provato:

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 
       } 
     } 

Sono in perdita. O non capisco esattamente come funziona il buffer o non capisco qualcos'altro. Grazie per aver letto.

+3

Non leggere tutto in una volta . Steam it. Usa 'bufio.Scanner' (dato che sembra che indichi che è basato sulla linea), elabora la linea, inserisci nel tuo db, * poi dimentica quella linea *. –

+0

Grazie per la risposta. Come faccio a dimenticare quella linea? Nei miei tentativi di utilizzare bufio.Scanner quando raggiungo la riga 63700 (approssimativamente) nel mio file, interrompo la lettura in nuove righe. La mia comprensione è che è perché ho colpito il MaxScanTokenSize (http://golang.org/pkg/bufio/#pkg-constants) dello scanner.Mi piacerebbe leggere la riga, analizzarla e buttarla via, ma non so come fare a buttarla via così lo scanner continua a muoversi attraverso l'intero file. – rvrtex

+0

@DaveC Hm ... Buffer al vapore. – fuz

risposta

5

bufio.Scan() e bufio.Text() in un ciclo funziona perfettamente per me su un file con dimensioni molto più grandi, così mi si supponga di avere linee di superamento della capacità del buffer. Poi

  • verificare la linea termina
  • e quale versione si utilizza Go path, err :=r.ReadLine("\n") // 0x0A separator = newline? Sembra func (b *bufio.Reader) ReadLine() (line []byte, isPrefix bool, err error) ha valore di ritorno isPrefix specificamente per il vostro caso d'uso http://golang.org/pkg/bufio/#Reader.ReadLine
+1

Questo è il modo corretto per farlo. Dopo alcuni ri-factoring per dimenticare ogni riga di input come suggerito da @DaveC e usando '.Scan()' e '.Text()' Ho eseguito di nuovo lo stesso ed ho avuto lo stesso problema. Poi sono andato a guardare il file su cui stavo effettivamente eseguendo il mio programma e ho scoperto che il problema era il file. Il programma stava facendo esattamente quello che avrebbe dovuto fare e avevo file danneggiati sul mio server. Lezione appresa, a volte non è una cattiva programmazione, ma un input negativo. Grazie per il tuo aiuto, con esso ho reso il mio programma molto più efficiente. – rvrtex

3

Non è chiaro che sia necessario leggere tutte le righe prima di analizzarle e inserirle in un database. Cerca di evitarlo.

Si dispone di un file di piccole dimensioni: "un file flat che contiene 339276 righe di testo per una dimensione di 62.1 MB." Ad esempio,

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

Mi sembra questa variante di readLines è più breve e più veloce di quanto suggerito 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 
} 
Problemi correlati