Yanıtlar:
NOT: Kabul edilen cevap Go'nun ilk sürümlerinde doğruydu. En yüksek oyu alan cevabın bunu başarmanın daha yeni deyimsel yolunu içerdiğini görün .
Pakette ReadLine işlevi vardır bufio.
Satır okuma arabelleğine sığmazsa, işlevin eksik bir satır döndüreceğini lütfen unutmayın. Bir fonksiyona tek bir çağrı yaparak programınızdaki tüm bir satırı her zaman okumak istiyorsanız, ReadLinefonksiyonu ReadLinebir for-loop'u çağıran kendi fonksiyonunuza dahil etmeniz gerekir .
bufio.ReadString('\n')tam olarak eşdeğer değildir, ReadLineçünkü ReadStringbir dosyanın son satırı yeni satır karakteri ile bitmediğinde durumu işleyemez.
Go 1.1 ve daha yeni sürümlerde bunu yapmanın en basit yolu a bufio.Scanner. Bir dosyadan satırları okuyan basit bir örnek:
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)
}
}
Bu Readersatır satır okumanın en temiz yoludur .
Bir uyarı var: Tarayıcı 65536 karakterden uzun satırlarla iyi başa çıkmıyor. Bu sizin için bir sorunsa, muhtemelen kendi üstüne dönmelisiniz Reader.Read().
file, _ := os.Open("/path/to/file.csv")dosya taraması ve ardından dosya tanıtıcısını taramak önemsiz olacaktır :scanner := bufio.NewScanner(file)
defer file.Close().
bufio.ErrTooLongHata alırsınız , yani bufio.Scanner: token too longçizgi çok uzunsa. Bu durumda, bufio.ReaderLine () veya ReadString () kullanmanız gerekir.
kullanın:
reader.ReadString('\n')
\nDöndürülen dizenin sonunda tutar .reader.ReadLine()
Diğer cevaplarda sorun olarak tanımlanan senaryoları test etmek için bir program yazarak önerilen çeşitli çözümleri test ettim:
Onu buldum:
ScannerSolüsyon uzun satırları işlemez.ReadLineÇözümü uygulamak için karmaşıktır.ReadStringÇözüm basit olanıdır ve uzun hatlar için çalışır.İşte her çözümü gösteren kod, üzerinden çalıştırılabilir 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]
}
Şunu test ettim:
Test programı çıktıları:
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()Hata kontrolü sonra olmalıdır; aksi halde hata durumunda panik olur.
Her satırı bir dosyadan kolayca okumak için bir yol yazdım. Readln (* bufio.Reader) işlevi, temeldeki bufio.Reader yapısından bir satır (sans \ n) döndürür.
// 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
}
Bir dosyadaki her satırı okumak için Readln komutunu kullanabilirsiniz. Aşağıdaki kod bir dosyadaki her satırı okur ve her satırı stdout'a çıkarır.
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)
}
Şerefe!
Dosyayı satır satır okumanın iki yaygın yolu vardır.
Benim testcase, In ~ 250MB, ~ 2,500,000 hatları , bufio.Scanner (zaman kullanılır: 0.395491384s) (: 0.446867622s time_used) hızlı bufio.Reader.ReadString fazla.
Kaynak kodu: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Dosyayı okuyun 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
}
}
Okuma dosyası bufio.Reader kullanın,
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.Readersatırla bitmezse, bu örneğin bir dosyadaki son satırı okumayacağını unutmayın. ReadStringhem son satırı hem io.EOFde bu durumda geri döner .
Bu özden örnek
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
}
}
ancak bu, Scanner'ın arabelleğinden daha büyük bir çizgi olduğunda bir hata verir.
Bu olduğunda, yaptığım şey, reader := bufio.NewReader(inFile)ya kendi tamponumu ya kullanarak ch, err := reader.ReadByte()veyalen, err := reader.Read(myBuffer)
Kullandığım başka bir yol (os.Stdin'i yukarıdaki gibi bir dosyayla değiştirin), bu satırlar uzun olduğunda (isPrefix) sona erer ve boş satırları yok sayar:
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
}
-1misin?
Ayrıcı olarak \ n ile ReadString'i de kullanabilirsiniz:
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 () iyi çalışıyor. Ancak her satırı bir dizeyle okumak istiyorsanız, ReadString ('\ n') öğesini kullanmayı deneyin . Tekerleği yeniden icat etmeye gerek yok.
// 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
}
Aşağıdaki kodda, kullanıcı girdi isabet ve ben Readline kullanıyorum kadar CLI çıkarlarını okuyun:
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)
Lzap çözümünü seviyorum, Go'da yeniyim, lzap istemek istiyorum ama yapamadım henüz 50 puanım yok .. Çözümünüzü biraz değiştirip kodu tamamlıyorum ...
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
}
}
Neden 'err' i tekrar test etmem gerektiğinden emin değilim, ama yine de yapabiliriz. Ama asıl soru şu: Go neden döngü içinde => satırı, err: = r.ReadString (10) ile hata üretmiyor? Döngü her yürütüldüğünde tekrar tekrar tanımlanır. Değişikliğimden dolayı bu durumdan kaçınırım, herhangi bir yorum? 'For' da EOF koşulunu bir While'a benzer şekilde ayarladım. Teşekkürler
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
İşte işlevi ReadFromStdin()gibi bir örnek fmt.Scan(&name)ama boş dizeleri olan tüm dizeleri alır: "Merhaba benim adım ..."
var name string = ReadFromStdin()
println(name)