Saya tidak dapat menemukan file.ReadLine
fungsi di Go. Saya bisa mencari cara untuk menulisnya dengan cepat, tetapi saya hanya ingin tahu apakah saya mengabaikan sesuatu di sini. Bagaimana cara membaca file baris demi baris?
Saya tidak dapat menemukan file.ReadLine
fungsi di Go. Saya bisa mencari cara untuk menulisnya dengan cepat, tetapi saya hanya ingin tahu apakah saya mengabaikan sesuatu di sini. Bagaimana cara membaca file baris demi baris?
Jawaban:
CATATAN: Jawaban yang diterima benar dalam versi awal Go. Lihat jawaban terpilih tertinggi berisi cara idiomatik terbaru untuk mencapai ini.
Ada fungsi ReadLine dalam paket bufio
.
Harap dicatat bahwa jika baris tidak sesuai dengan buffer baca, fungsi akan mengembalikan baris yang tidak lengkap. Jika Anda ingin selalu membaca seluruh baris dalam program Anda dengan satu panggilan ke suatu fungsi, Anda harus merangkum ReadLine
fungsi tersebut ke dalam fungsi Anda sendiri yang memanggil ReadLine
for-loop.
bufio.ReadString('\n')
tidak sepenuhnya setara dengan ReadLine
karena ReadString
tidak dapat menangani kasus ketika baris terakhir file tidak berakhir dengan karakter baris baru.
Di Go 1.1 dan yang lebih baru cara paling sederhana untuk melakukan ini adalah dengan a bufio.Scanner
. Berikut adalah contoh sederhana yang membaca baris dari file:
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)
}
}
Ini adalah cara terbersih untuk membaca dari Reader
baris demi baris.
Ada satu peringatan: Pemindai tidak berurusan dengan garis lebih dari 65536 karakter. Jika itu merupakan masalah bagi Anda maka Anda mungkin harus memutar sendiri di atas Reader.Read()
.
file, _ := os.Open("/path/to/file.csv")
dan kemudian memindai menangani file:scanner := bufio.NewScanner(file)
defer file.Close()
.
bufio.ErrTooLong
kesalahan, yaitu bufio.Scanner: token too long
jika garis terlalu panjang. Dalam hal ini, Anda harus menggunakan bufio.ReaderLine () atau ReadString ().
Menggunakan:
reader.ReadString('\n')
\n
pada akhir string dikembalikan.reader.ReadLine()
Saya menguji berbagai solusi yang disarankan dengan menulis sebuah program untuk menguji skenario yang diidentifikasi sebagai masalah dalam jawaban lain:
Saya menemukan bahwa:
Scanner
solusi tidak menangani antrean panjang.ReadLine
solusi adalah kompleks untuk melaksanakan.ReadString
solusi adalah yang paling sederhana dan bekerja untuk garis panjang.Berikut adalah kode yang menunjukkan setiap solusi, dapat dijalankan melalui 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]
}
Saya diuji pada:
Output program pengujian:
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.
defer file.Close()
harus setelah cek error; jika tidak pada kesalahan itu akan panik.
Saya menulis cara untuk dengan mudah membaca setiap baris dari file. Fungsi Readln (* bufio.Reader) mengembalikan baris (sans \ n) dari struct bufio.Reader yang mendasarinya.
// 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
}
Anda dapat menggunakan Readln untuk membaca setiap baris dari file. Kode berikut membaca setiap baris dalam file dan menampilkan setiap baris ke 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)
}
Bersulang!
Ada dua cara umum untuk membaca file baris demi baris.
Di ruang uji saya, ~ 250MB, ~ 2.500.000 baris , bufio.Scanner (waktu yang digunakan: 0.395491384s) lebih cepat daripada bufio.Reader.ReadString (time_used: 0.446867622s).
Kode sumber: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Baca file menggunakan 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
}
}
Baca file menggunakan 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
}
}
bufio.Reader
contoh ini tidak akan membaca baris terakhir dalam file jika tidak diakhiri dengan baris baru. ReadString
akan mengembalikan baris terakhir dan io.EOF
dalam hal ini.
Contoh dari intisari ini
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
}
}
tetapi ini memberikan kesalahan ketika ada garis yang lebih besar dari buffer Scanner.
Ketika itu terjadi, apa yang saya lakukan adalah menggunakan reader := bufio.NewReader(inFile)
create dan concat buffer saya sendiri baik menggunakan ch, err := reader.ReadByte()
ataulen, err := reader.Read(myBuffer)
Cara lain yang saya gunakan (ganti os.Stdin dengan file seperti di atas), ini cocok ketika baris panjang (isPrefix) dan mengabaikan baris kosong:
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
}
-1
?
Anda juga dapat menggunakan ReadString dengan \ n sebagai pemisah:
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
}
}
bufio.Reader.ReadLine () berfungsi dengan baik. Tetapi jika Anda ingin membaca setiap baris dengan sebuah string, coba gunakan ReadString ('\ n') . Tidak perlu menemukan kembali roda.
// 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
}
Dalam kode di bawah, saya membaca minat dari CLI hingga pengguna mengklik masuk dan saya menggunakan 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)
Saya suka solusi Lzap, saya baru di Go, saya ingin bertanya ke lzap tapi saya tidak bisa melakukannya saya belum 50 poin .. Saya mengubah sedikit solusi Anda dan menyelesaikan kode ...
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
}
}
Saya tidak yakin mengapa saya perlu menguji 'err' lagi, tetapi dengan cara apa pun kita bisa melakukannya. Tapi, pertanyaan utamanya adalah .. mengapa Go tidak menghasilkan kesalahan dengan kalimat => line, err: = r.ReadString (10), di dalam loop? Ini didefinisikan lagi dan lagi setiap kali loop dijalankan. Saya menghindari situasi itu dengan perubahan saya, ada komentar? Saya mengatur kondisi EOF di 'untuk' yang mirip dengan Sementara juga. Terima kasih
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Berikut ini adalah contoh dengan fungsi ReadFromStdin()
itu seperti fmt.Scan(&name)
tetapi mengambil semua string dengan spasi kosong seperti: "Hello My Name Is ..."
var name string = ReadFromStdin()
println(name)
Metode lain adalah dengan menggunakan io/ioutil
dan strings
pustaka untuk membaca seluruh byte file, mengubahnya menjadi string dan membaginya menggunakan karakter " \n
" (baris baru) sebagai pembatas, misalnya:
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
file_content := string(bytesRead)
lines := strings.Split(file_content, "\n")
}
Secara teknis Anda tidak membaca file baris demi baris, namun Anda dapat mengurai setiap baris menggunakan teknik ini. Metode ini berlaku untuk file yang lebih kecil. Jika Anda mencoba mem-parsing file besar gunakan salah satu teknik yang membaca baris demi baris.