12 ответов:
существует функция ReadLine в пакете
bufio.обратите внимание, что если строка не помещается в буфер чтения, функция возвращает неполные линии. Если вы хотите всегда читать всю строку в своей программе одним вызовом функции, вам нужно будет инкапсулировать
в Go 1.1 и новее самый простой способ сделать это с помощью
bufio.Scanner. Вот простой пример, который читает строки из файла: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) for scanner.Scan() { fmt.Println(scanner.Text()) } if err := scanner.Err(); err != nil { log.Fatal(err) } }Это самый чистый способ чтения из
Readerстрока за строкой.есть один нюанс: сканер не очень хорошо справляется со строками длиной более 65536 символов. Если это проблема для вас, то вы, вероятно, должны свернуть свой собственный на вершине
Reader.Read().
изменить: в go1.1, идиоматические решением является использование bufio.Сканер
Я написал способ легко читать каждую строку из файла. Readln (*bufio.Функция Reader) возвращает строку (sans \n) из базового bufio.Читательская структура.
// 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 }вы можете использовать Readln для чтения каждой строки из файла. Следующий код считывает каждую строку в файле и выводит каждую строку в stdout.
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) }Ура!
использование:
reader.ReadString('\n')
- если вы не возражаете, что строка может быть очень длинной (т. е. использовать много оперативной памяти). Он держит
\nВ конце строки, возвращаемой.reader.ReadLine()
- если вы заботитесь об ограничении потребления оперативной памяти и не возражаете против дополнительной работы по обработке случая, когда строка больше размера буфера считывателя.
Я проверил различные решения, предлагаемые путем написания программы для тестирования сценариев, которые идентифицируются как проблемы в других ответах:
- файл со строкой 4 МБ.
- файл, который не заканчивается разрывом строки.
я обнаружил, что:
- The
Scannerрешение не обрабатывает длинные строки.- The
ReadLineрешение является сложным для реализации.- The
ReadStringрешение является самым простым и работает долго русло.вот код, который демонстрирует каждое решение, его можно запустить с помощью
go run main.go:package main import ( "bufio" "bytes" "fmt" "io" "os" ) func readFileWithReadString(fn string) (err error) { fmt.Println("readFileWithReadString") file, err := os.Open(fn) defer file.Close() if err != nil { return err } // Start reading from the file with a reader. reader := bufio.NewReader(file) var line string for { line, err = reader.ReadString('\n') fmt.Printf(" > Read %d characters\n", len(line)) // Process the line here. fmt.Println(" > > " + limitLength(line, 50)) if err != nil { break } } if err != io.EOF { fmt.Printf(" > Failed!: %v\n", err) } return } func readFileWithScanner(fn string) (err error) { fmt.Println("readFileWithScanner - this will fail!") // Don't use this, it doesn't work with long lines... file, err := os.Open(fn) defer file.Close() if err != nil { return err } // Start reading from the file using a scanner. scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() fmt.Printf(" > Read %d characters\n", len(line)) // Process the line here. fmt.Println(" > > " + limitLength(line, 50)) } if scanner.Err() != nil { fmt.Printf(" > Failed!: %v\n", scanner.Err()) } return } func readFileWithReadLine(fn string) (err error) { fmt.Println("readFileWithReadLine") file, err := os.Open(fn) defer file.Close() if err != nil { return err } // Start reading from the file with a reader. reader := bufio.NewReader(file) for { var buffer bytes.Buffer var l []byte var isPrefix bool for { l, isPrefix, err = reader.ReadLine() buffer.Write(l) // If we've reached the end of the line, stop reading. if !isPrefix { break } // If we're just at the EOF, break if err != nil { break } } if err == io.EOF { break } line := buffer.String() fmt.Printf(" > Read %d characters\n", len(line)) // Process the line here. fmt.Println(" > > " + limitLength(line, 50)) } if err != io.EOF { fmt.Printf(" > Failed!: %v\n", err) } return } func main() { testLongLines() testLinesThatDoNotFinishWithALinebreak() } func testLongLines() { fmt.Println("Long lines") fmt.Println() createFileWithLongLine("longline.txt") readFileWithReadString("longline.txt") fmt.Println() readFileWithScanner("longline.txt") fmt.Println() readFileWithReadLine("longline.txt") fmt.Println() } func testLinesThatDoNotFinishWithALinebreak() { fmt.Println("No linebreak") fmt.Println() createFileThatDoesNotEndWithALineBreak("nolinebreak.txt") readFileWithReadString("nolinebreak.txt") fmt.Println() readFileWithScanner("nolinebreak.txt") fmt.Println() readFileWithReadLine("nolinebreak.txt") fmt.Println() } func createFileThatDoesNotEndWithALineBreak(fn string) (err error) { file, err := os.Create(fn) defer file.Close() if err != nil { return err } w := bufio.NewWriter(file) w.WriteString("Does not end with linebreak.") w.Flush() return } func createFileWithLongLine(fn string) (err error) { file, err := os.Create(fn) defer file.Close() if err != nil { return err } w := bufio.NewWriter(file) fs := 1024 * 1024 * 4 // 4MB // Create a 4MB long line consisting of the letter a. for i := 0; i < fs; i++ { w.WriteRune('a') } // Terminate the line with a break. w.WriteRune('\n') // Put in a second line, which doesn't have a linebreak. w.WriteString("Second line.") w.Flush() return } func limitLength(s string, length int) string { if len(s) < length { return s } return s[:length] }Я тестировал на:
- go версия go1. 7 windows / amd64
- версию перейти go1.6.3 для linux/amd64 на
- версию перейти go1.7.4 Дарвин/amd64 на
выходы тестовой программы:
Long lines readFileWithReadString > Read 4194305 characters > > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > Read 12 characters > > Second line. readFileWithScanner - this will fail! > Failed!: bufio.Scanner: token too long readFileWithReadLine > Read 4194304 characters > > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > Read 12 characters > > Second line. No linebreak readFileWithReadString > Read 28 characters > > Does not end with linebreak. readFileWithScanner - this will fail! > Read 28 characters > > Does not end with linebreak. readFileWithReadLine > Read 28 characters > > Does not end with linebreak.
есть два распространенных способа чтения файла строка за строкой.
- использовать bufio.Сканер
- Использовать ReadString / ReadBytes/... в bufio.Читатель
в моем примере, ~250 МБ, ~2,500,000 строк, bufio.Сканер (время использования: 0.395491384 С) работает быстрее, чем bufio.Читатель.ReadString (time_used: 0.446867622 s).
исходный код: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
читать bufio использовать файл.Сканер,
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 } }чтение файла с помощью bufio.Читатель,
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 } }
вы также можете использовать ReadString с \n в качестве разделителя:
f, err := os.Open(filename) if err != nil { fmt.Println("error opening file ", err) os.Exit(1) } defer f.Close() r := bufio.NewReader(f) for { path, err := r.ReadString(10) // 0x0A separator = newline if err == io.EOF { // do something here break } else if err != nil { return err // if you return error } }
пример суть
func readLine(path string) { inFile, err := os.Open(path) if err != nil { fmt.Println(err.Error() + `: ` + path) return } else { defer inFile.Close() } scanner := bufio.NewScanner(inFile) scanner.Split(bufio.ScanLines) for scanner.Scan() { fmt.Println(scanner.Text()) // the line } }но это дает ошибку, когда есть строка, которая больше, чем буфер сканера.
когда это произошло, то, что я делаю, это использовать
reader := bufio.NewReader(inFile)создать и concat мой собственный буфер, либо с помощьюch, err := reader.ReadByte()илиlen, err := reader.Read(myBuffer)
bufio.Читатель.ReadLine () работает хорошо. Но если вы хотите читать каждую строку по строке, попробуйте использовать ReadString ('\n'). Не нужно изобретать велосипед.
// strip '\n' or read until EOF, return error if read error func readline(reader io.Reader) (line []byte, err error) { line = make([]byte, 0, 100) for { b := make([]byte, 1) n, er := reader.Read(b) if n > 0 { c := b[0] if c == '\n' { // end of line break } line = append(line, c) } if er != nil { err = er return } } return }
в приведенном ниже коде я читаю интересы из CLI, пока пользователь не нажмет enter, и я использую Readline:
interests := make([]string, 1) r := bufio.NewReader(os.Stdin) for true { fmt.Print("Give me an interest:") t, _, _ := r.ReadLine() interests = append(interests, string(t)) if len(t) == 0 { break; } } fmt.Println(interests)
import ( "bufio" "os" ) var ( reader = bufio.NewReader(os.Stdin) ) func ReadFromStdin() string{ result, _ := reader.ReadString('\n') witl := result[:len(result)-1] return witl }вот пример с функцией
ReadFromStdin()это какfmt.Scan(&name)но он принимает все строки с пробелами, как: "привет, Меня зовут ..."var name string = ReadFromStdin() println(name)
Мне нравится решение Lzap, я новичок в Go, Я хотел бы попросить lzap, но я не мог этого сделать, у меня еще нет 50 очков.. Я немного изменю ваше решение и завершу код...
package main import ( "bufio" "fmt" "io" "os" ) func main() { f, err := os.Open("archiveName") if err != nil { fmt.Println(err) os.Exit(1) } defer f.Close() r := bufio.NewReader(f) line, err := r.ReadString(10) // line defined once for err != io.EOF { fmt.Print(line) // or any stuff line, err = r.ReadString(10) // line was defined before } }Я не уверен, почему мне нужно снова проверить "err", но в любом случае мы можем это сделать. Но, главный вопрос.. почему Go не производит ошибку с предложением = > line, err: = r. ReadString (10), внутри цикла ? Он определяется снова и снова при каждом выполнении цикла. Я избегаю этой ситуации с моей сдачей, какие-нибудь комментарии? Я установил условие EOF в " for " как похожее на некоторое время тоже. Спасибо
Comments