ฉันไม่พบfile.ReadLine
ฟังก์ชันใน Go ฉันสามารถหาวิธีเขียนได้อย่างรวดเร็ว แต่ฉันแค่สงสัยว่าฉันมองอะไรบางอย่างที่นี่ หนึ่งจะอ่านไฟล์ทีละบรรทัดได้อย่างไร
ฉันไม่พบfile.ReadLine
ฟังก์ชันใน Go ฉันสามารถหาวิธีเขียนได้อย่างรวดเร็ว แต่ฉันแค่สงสัยว่าฉันมองอะไรบางอย่างที่นี่ หนึ่งจะอ่านไฟล์ทีละบรรทัดได้อย่างไร
คำตอบ:
หมายเหตุ:คำตอบที่ยอมรับนั้นถูกต้องใน Go เวอร์ชันก่อนหน้า ดูคำตอบที่ได้รับการโหวตสูงสุดประกอบด้วยวิธีการสำนวนล่าสุดเพื่อให้บรรลุนี้
มีฟังก์ชั่นReadLinebufio
ในแพคเกจ
โปรดทราบว่าหากบรรทัดไม่พอดีกับบัฟเฟอร์การอ่านฟังก์ชันจะส่งคืนบรรทัดที่ไม่สมบูรณ์ หากคุณต้องการอ่านทั้งบรรทัดในโปรแกรมของคุณด้วยการเรียกใช้ฟังก์ชันเพียงครั้งเดียวคุณจะต้องสรุปReadLine
ฟังก์ชั่นลงในฟังก์ชั่นของคุณเองซึ่งเรียกใช้ฟังก์ชันReadLine
for-loop
bufio.ReadString('\n')
ไม่เทียบเท่าอย่างเต็มที่ReadLine
เนื่องจากReadString
ไม่สามารถจัดการเคสได้เมื่อบรรทัดสุดท้ายของไฟล์ไม่ได้ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่
ไปใน 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()
ออกมา
file, _ := os.Open("/path/to/file.csv")
แล้วสแกนมากกว่าการจัดการไฟล์:scanner := bufio.NewScanner(file)
defer file.Close()
อย่าลืมให้
bufio.ErrTooLong
ข้อผิดพลาดซึ่งbufio.Scanner: token too long
ถ้าสายยาวเกินไป ในกรณีนี้คุณจะต้องใช้ bufio.ReaderLine () หรือ ReadString ()
ใช้:
reader.ReadString('\n')
\n
ตอนท้ายของสตริงกลับมาreader.ReadLine()
ฉันทดสอบวิธีแก้ปัญหาต่าง ๆ ที่แนะนำโดยการเขียนโปรแกรมเพื่อทดสอบสถานการณ์ที่ระบุว่าเป็นปัญหาในคำตอบอื่น ๆ :
ฉันพบว่า:
Scanner
แก้ปัญหาไม่ได้จัดการกับสายยาวReadLine
แก้ปัญหามีความซับซ้อนในการใช้งาน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]
}
ฉันทดสอบเมื่อ:
ผลลัพธ์ของโปรแกรมทดสอบ:
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()
ควรจะเป็นหลังจากเช็คข้อผิดพลาด; มิฉะนั้นในข้อผิดพลาดมันจะตกใจ
ฉันเขียนวิธีอ่านแต่ละบรรทัดจากไฟล์ ฟังก์ชัน Readln (* bufio.Reader) ส่งคืนบรรทัด (sans \ n) จากโครงสร้าง bufio.Reader
// 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)
}
ไชโย!
มีวิธีการทั่วไปสองวิธีในการอ่านไฟล์ทีละบรรทัด
ใน testcase ของฉัน~ 250MB ~ 2,500,000 บรรทัด bufio.Scanner (เวลาที่ใช้: 0.395491384s) เร็วกว่า bufio.Reader.ReadString (time_used: 0.446867622s)
รหัสแหล่งที่มา: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
อ่านไฟล์ใช้ 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
}
}
อ่านไฟล์ใช้ 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
ตัวอย่างนี้จะไม่อ่านบรรทัดสุดท้ายในไฟล์หากไม่ได้ขึ้นบรรทัดใหม่ ReadString
จะส่งคืนทั้งบรรทัดสุดท้ายและio.EOF
ในกรณีนี้
ตัวอย่างจากส่วนสำคัญนี้
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
}
}
แต่สิ่งนี้จะให้ข้อผิดพลาดเมื่อมีบรรทัดที่ใหญ่กว่าบัฟเฟอร์ของสแกนเนอร์
เมื่อสิ่งนั้นเกิดขึ้นสิ่งที่ฉันทำคือใช้reader := bufio.NewReader(inFile)
สร้างและต่อเชื่อมบัฟเฟอร์ของฉันเองโดยใช้ch, err := reader.ReadByte()
หรือlen, err := reader.Read(myBuffer)
อีกวิธีหนึ่งที่ฉันใช้ (แทนที่ os.Stdin ด้วยไฟล์เหมือนด้านบน) อันนี้ concats เมื่อสายยาว (isPrefix) และละเว้นบรรทัดว่าง:
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
?
คุณยังสามารถใช้ 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
}
}
bufio.Reader.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 จนกระทั่งผู้ใช้กดและฉันใช้ 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)
ฉันชอบโซลูชัน 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
}
}
ฉันไม่แน่ใจว่าทำไมฉันต้องทดสอบ 'ผิดพลาด' อีกครั้ง แต่เราสามารถทำได้ แต่คำถามหลักคือ .. ทำไม Go จึงไม่เกิดข้อผิดพลาดกับประโยค => line, err: = r.ReadString (10) ภายในวง? มันถูกกำหนดอีกครั้งและอีกครั้งในแต่ละครั้งที่มีการดำเนินการวง ฉันหลีกเลี่ยงสถานการณ์นั้นเมื่อมีการเปลี่ยนแปลงความคิดเห็นใด ๆ ฉันตั้งเงื่อนไข EOF ใน 'for' คล้ายกับในขณะเดียวกัน ขอบคุณ
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)
แต่มันใช้สายทั้งหมดที่มีช่องว่างเช่น: "Hello My Name Is ... "
var name string = ReadFromStdin()
println(name)