Saya tidak dapat menemukan file.ReadLinefungsi 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?
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 ReadLinefungsi tersebut ke dalam fungsi Anda sendiri yang memanggil ReadLinefor-loop.
bufio.ReadString('\n')tidak sepenuhnya setara dengan ReadLinekarena ReadStringtidak dapat menangani kasus ketika baris terakhir file tidak berakhir dengan karakter baris baru.
Dari dokumen: "ReadLine adalah primitif pembacaan baris tingkat rendah. Sebagian besar penelepon harus menggunakan ReadBytes ('\ n') atau ReadString ('\ n') sebagai gantinya atau menggunakan Scanner."
mdwhatcott
12
@ mdwhatcott mengapa itu penting bahwa itu adalah "primitif membaca garis tingkat rendah"? Bagaimana hal itu sampai pada kesimpulan bahwa "Sebagian besar penelepon harus menggunakan ReadBytes ('\ n') atau ReadString ('\ n') sebagai gantinya atau menggunakan Scanner."?
Charlie Parker
12
@CharlieParker - Tidak yakin, hanya mengutip dokumen untuk menambahkan konteks.
mdwhatcott
11
Dari dokumen yang sama .. "Jika ReadString menemukan kesalahan sebelum menemukan pembatas, itu mengembalikan data yang dibaca sebelum kesalahan dan kesalahan itu sendiri (sering io.EOF)." Jadi Anda bisa memeriksa kesalahan io.EOF dan tahu Anda sudah selesai.
eduncan911
1
Perhatikan bahwa membaca atau menulis dapat gagal karena panggilan sistem terputus, yang menghasilkan kurang dari jumlah byte yang diharapkan yang dibaca atau ditulis.
Justin Swanhart
599
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:
Ini adalah cara terbersih untuk membaca dari Readerbaris 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().
Dan karena OP diminta untuk memindai suatu file, itu akan sepele untuk pertama file, _ := os.Open("/path/to/file.csv")dan kemudian memindai menangani file:scanner := bufio.NewScanner(file)
Evan Plumlee
14
Jangan lupa defer file.Close().
Kiril
13
Masalahnya adalah Scanner.Scan () terbatas dalam ukuran buffer byte 4096 [] per baris. Anda akan mendapatkan bufio.ErrTooLongkesalahan, yaitu bufio.Scanner: token too longjika garis terlalu panjang. Dalam hal ini, Anda harus menggunakan bufio.ReaderLine () atau ReadString ().
eduncan911
5
Hanya $ 0,02 saya - ini adalah jawaban yang paling benar di halaman :)
Jika Anda tidak keberatan salurannya bisa sangat panjang (mis. Gunakan banyak RAM). Itu membuat \npada akhir string dikembalikan.
reader.ReadLine()
Jika Anda peduli tentang membatasi konsumsi RAM dan tidak keberatan bekerja ekstra menangani kasus di mana garis lebih besar dari ukuran buffer pembaca.
Saya menguji berbagai solusi yang disarankan dengan menulis sebuah program untuk menguji skenario yang diidentifikasi sebagai masalah dalam jawaban lain:
File dengan garis 4MB.
File yang tidak berakhir dengan jeda baris.
Saya menemukan bahwa:
Itu Scanner solusi tidak menangani antrean panjang.
The ReadLinesolusi adalah kompleks untuk melaksanakan.
The ReadStringsolusi 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 stringfor{
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.Buffervar l []bytevar isPrefix boolfor{
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, breakif 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:
go versi go1.7 windows / amd64
go versi go1.6.3 linux / amd64
go versi go1.7.4 darwin / amd64
Output program pengujian:
Long lines
readFileWithReadString
>Read4194305 characters
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
>Read12 characters
>>Second line.
readFileWithScanner -this will fail!>Failed!: bufio.Scanner: token too long
readFileWithReadLine
>Read4194304 characters
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
>Read12 characters
>>Second line.No linebreak
readFileWithReadString
>Read28 characters
>>Doesnotendwith linebreak.
readFileWithScanner -this will fail!>Read28 characters
>>Doesnotendwith linebreak.
readFileWithReadLine
>Read28 characters
>>Doesnotendwith linebreak.
Anda harus memeriksa kesalahan dengan benar seperti yang terlihat dalam dokumen: play.golang.org/p/5CCPzVTSj6 yaitu jika err == io.EOF {break} else {return err}
Chuque
53
EDIT: Pada go1.1, solusi idiomatik adalah dengan menggunakan bufio.Scanner
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...)}returnstring(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)}
Saya menulis jawaban ini sebelum Go 1.1 keluar. Go 1.1 memiliki paket Scanner di stdlib. yang menyediakan fungsi yang sama dengan jawaban saya. Saya akan merekomendasikan menggunakan Scanner daripada jawaban saya karena Scanner ada di stdlib. Selamat melakukan peretasan! :-)
Malcolm
30
Ada dua cara umum untuk membaca file baris demi baris.
Gunakan bufio.Scanner
Gunakan ReadString / ReadBytes / ... di bufio.Reader
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).
Perlu diketahui bahwa bufio.Readercontoh ini tidak akan membaca baris terakhir dalam file jika tidak diakhiri dengan baris baru. ReadStringakan mengembalikan baris terakhir dan io.EOFdalam hal ini.
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:
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}
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
Berikut ini adalah contoh dengan fungsi ReadFromStdin()itu seperti fmt.Scan(&name)tetapi mengambil semua string dengan spasi kosong seperti: "Hello My Name Is ..."
Metode lain adalah dengan menggunakan io/ioutildan stringspustaka untuk membaca seluruh byte file, mengubahnya menjadi string dan membaginya menggunakan karakter " \n" (baris baru) sebagai pembatas, misalnya:
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.
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 memanggilReadLine
for-loop.bufio.ReadString('\n')
tidak sepenuhnya setara denganReadLine
karenaReadString
tidak dapat menangani kasus ketika baris terakhir file tidak berakhir dengan karakter baris baru.sumber
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: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()
.sumber
file, _ := os.Open("/path/to/file.csv")
dan kemudian memindai menangani file:scanner := bufio.NewScanner(file)
defer file.Close()
.bufio.ErrTooLong
kesalahan, yaitubufio.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
:Saya diuji pada:
Output program pengujian:
sumber
defer file.Close()
harus setelah cek error; jika tidak pada kesalahan itu akan panik.EDIT: Pada go1.1, solusi idiomatik adalah dengan menggunakan bufio.Scanner
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.
Anda dapat menggunakan Readln untuk membaca setiap baris dari file. Kode berikut membaca setiap baris dalam file dan menampilkan setiap baris ke stdout.
Bersulang!
sumber
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,
Baca file menggunakan bufio.Reader,
sumber
bufio.Reader
contoh ini tidak akan membaca baris terakhir dalam file jika tidak diakhiri dengan baris baru.ReadString
akan mengembalikan baris terakhir danio.EOF
dalam hal ini.Contoh dari intisari ini
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 menggunakanch, 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:
sumber
-1
?Anda juga dapat menggunakan ReadString dengan \ n sebagai pemisah:
sumber
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.
sumber
sumber
Dalam kode di bawah, saya membaca minat dari CLI hingga pengguna mengklik masuk dan saya menggunakan Readline:
sumber
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 ...
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
sumber
Berikut ini adalah contoh dengan fungsi
ReadFromStdin()
itu sepertifmt.Scan(&name)
tetapi mengambil semua string dengan spasi kosong seperti: "Hello My Name Is ..."sumber
Metode lain adalah dengan menggunakan
io/ioutil
danstrings
pustaka untuk membaca seluruh byte file, mengubahnya menjadi string dan membaginya menggunakan karakter "\n
" (baris baru) sebagai pembatas, misalnya: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.
sumber