Как читать в большом плоском файле в Golang
у меня есть плоский файл, в котором есть строка текста 339276 для размера 62.1 MB. Я пытаюсь прочитать все строки, проанализировать их на основе некоторых условий, которые у меня есть, а затем вставить их в базу данных.
первоначально я пытался использовать bufio.Scan () loop и bufio.Text (), чтобы получить строку, но у меня заканчивалось буферное пространство. Я переключился на использование bufio.ReadLine/ReadString / ReadByte (я пробовал каждый) и имел ту же проблему с каждым. У меня не было достаточного буферного пространства.
Я попытался использовать read и установить размер буфера, но, как говорится в документе, это на самом деле const, который можно сделать меньше, но никогда не больше, чем 64*1024 байта. Затем я попытался использовать файл.ReadAt, где я установил начальный postilion и переместил его, поскольку я привел каждый раздел безрезультатно. Я просмотрел следующие примеры и объяснения (не исчерпывающий список):
чтение текстового файла в строковый массив (и запись) как читать последние строки из большого файла с Go каждые 10 секунд чтение файла строка за строкой в go
Как я могу прочитать весь файл (строка за строкой или все сразу) в срез, чтобы я мог делать что-то с линиями?
вот код, который я пробовал:
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])
вот еще что я пробовал:
var fileSize int64 = fileInfo.Size()
fmt.Printf("File Size: %dt", 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: %sn", currentErr)
fmt.Printf("fullFile Size: %sn", 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
}
}
Я в недоумении. Либо я не совсем понимаю, как работает буфер, либо я чего-то не понимаю еще. Спасибо за чтение.
3 ответов
bufio.Scan()
и bufio.Text()
в цикле отлично работает для меня на файлах с гораздо большим размером, поэтому я полагаю, что у вас есть строки, превышающие буферную емкость. Тогда
- проверить конец строки
- и какую версию Go вы используете
path, err :=r.ReadLine("\n") // 0x0A separator = newline
? Похоже наfunc (b *bufio.Reader) ReadLine() (line []byte, isPrefix bool, err error)
возвращаетisPrefix
специально для вашего варианта использования http://golang.org/pkg/bufio/#Reader.ReadLine
неясно, что необходимо прочитать все строки перед их разбором и вставкой в базу данных. Постарайся этого избежать.
у вас есть небольшой файл: "плоский файл, который имеет 339276 строк текста в нем для размера 62.1 MB.- Например,
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
}
}
мне кажется этот вариант readLines
короче и быстрее, чем предложил 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
}