Tôi không thể tìm thấy file.ReadLine
chức năng trong Go. Tôi có thể tìm ra cách để nhanh chóng viết một cái, nhưng tôi chỉ tự hỏi liệu tôi có đang nhìn gì đó ở đây không. Làm thế nào để một người đọc một dòng tập tin theo dòng?
Tôi không thể tìm thấy file.ReadLine
chức năng trong Go. Tôi có thể tìm ra cách để nhanh chóng viết một cái, nhưng tôi chỉ tự hỏi liệu tôi có đang nhìn gì đó ở đây không. Làm thế nào để một người đọc một dòng tập tin theo dòng?
Câu trả lời:
LƯU Ý: Câu trả lời được chấp nhận là chính xác trong các phiên bản đầu của Go. Xem câu trả lời được bình chọn cao nhất chứa cách thành ngữ gần đây hơn để đạt được điều này.
Có chức năng ReadLine trong gói bufio
.
Xin lưu ý rằng nếu dòng không vừa với bộ đệm đọc, hàm sẽ trả về một dòng không đầy đủ. Nếu bạn muốn luôn luôn đọc toàn bộ một dòng trong chương trình của mình bằng một cuộc gọi đến một chức năng, bạn sẽ cần phải đóng gói ReadLine
chức năng đó vào chức năng của chính bạn, gọi ReadLine
trong một vòng lặp for.
bufio.ReadString('\n')
không hoàn toàn tương đương ReadLine
vì ReadString
không thể xử lý trường hợp khi dòng cuối cùng của tệp không kết thúc bằng ký tự dòng mới.
Trong Go 1.1 và mới hơn, cách đơn giản nhất để làm điều này là với a bufio.Scanner
. Đây là một ví dụ đơn giản đọc các dòng từ một tệp:
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)
}
}
Đây là cách sạch nhất để đọc từ một Reader
dòng theo dòng.
Có một cảnh báo: Máy quét không xử lý tốt các dòng dài hơn 65536 ký tự. Nếu đó là một vấn đề cho bạn thì có lẽ bạn nên tự mình lăn lộn Reader.Read()
.
file, _ := os.Open("/path/to/file.csv")
và sau đó quét qua phần xử lý tệp:scanner := bufio.NewScanner(file)
defer file.Close()
.
bufio.ErrTooLong
lỗi, đó là bufio.Scanner: token too long
nếu dòng quá dài. Trong trường hợp đó, bạn sẽ phải sử dụng bufio.ReaderLine () hoặc ReadString ().
Sử dụng:
reader.ReadString('\n')
\n
ở cuối chuỗi trả về.reader.ReadLine()
Tôi đã thử nghiệm các giải pháp khác nhau được đề xuất bằng cách viết chương trình để kiểm tra các tình huống được xác định là sự cố trong các câu trả lời khác:
Tôi thấy rằng:
Scanner
giải pháp không xử lý xếp hàng dài.ReadLine
giải pháp phức tạp để thực hiện.ReadString
giải pháp là đơn giản nhất và làm việc cho đường dây dài.Đây là mã trình bày từng giải pháp, nó có thể được chạy qua 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]
}
Tôi đã thử nghiệm trên:
Các đầu ra chương trình thử nghiệm:
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()
nên sau khi kiểm tra lỗi; nếu không có lỗi nó sẽ hoảng loạn.
Tôi đã viết lên một cách để dễ dàng đọc từng dòng từ một tập tin. Hàm Readln (* bufio.Reader) trả về một dòng (sans \ n) từ cấu trúc bufio.Reader bên dưới.
// 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
}
Bạn có thể sử dụng Readln để đọc mọi dòng từ một tệp. Đoạn mã sau đọc từng dòng trong một tệp và xuất từng dòng thành thiết bị xuất chuẩn.
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)
}
Chúc mừng!
Có hai cách phổ biến để đọc từng dòng tệp.
Trong thử nghiệm của tôi, ~ 250MB, ~ 2.500.000 dòng , bufio.Scanner (thời gian sử dụng: 0.395491384s) nhanh hơn bufio.Reader.ReadString (time_use: 0.446867622s).
Mã nguồn: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Đọc tệp sử dụng 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
}
}
Đọc tệp sử dụng 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
ví dụ này sẽ không đọc dòng cuối cùng trong một tệp nếu nó không kết thúc bằng một dòng mới. ReadString
sẽ trả lại cả dòng cuối cùng và io.EOF
trong trường hợp này.
Ví dụ từ ý chính này
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
}
}
nhưng điều này gây ra lỗi khi có một dòng lớn hơn bộ đệm của Scanner.
Khi điều đó xảy ra, những gì tôi làm là sử dụng reader := bufio.NewReader(inFile)
tạo và ghép bộ đệm của riêng tôi bằng cách sử dụng ch, err := reader.ReadByte()
hoặclen, err := reader.Read(myBuffer)
Một cách khác mà tôi sử dụng (thay thế os.Stdin bằng tệp như trên), cách này sẽ che giấu khi các dòng dài (isPrefix) và bỏ qua các dòng trống:
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
?
Bạn cũng có thể sử dụng ReadString với \ n làm dấu phân cách:
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 () hoạt động tốt. Nhưng nếu bạn muốn đọc từng dòng theo một chuỗi, hãy thử sử dụng ReadString ('\ n') . Nó không cần phải phát minh lại bánh xe.
// 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
}
Trong mã dưới đây, tôi đọc các sở thích từ CLI cho đến khi người dùng nhấn enter và tôi đang sử dụng 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)
Tôi thích giải pháp Lzap, tôi mới tham gia Go, tôi muốn hỏi về lzap nhưng tôi không thể làm được. Tôi chưa có 50 điểm .. Tôi thay đổi một chút giải pháp của bạn và hoàn tất mã ...
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
}
}
Tôi không chắc tại sao tôi lại phải kiểm tra 'err', nhưng dù sao thì chúng tôi cũng có thể làm được. Nhưng, câu hỏi chính là .. tại sao Go không tạo ra lỗi với câu => line, err: = r.ReadString (10), bên trong vòng lặp? Nó được định nghĩa lặp đi lặp lại mỗi lần vòng lặp được thực thi. Tôi tránh tình huống đó với sự thay đổi của tôi, bất kỳ bình luận? Tôi cũng đặt điều kiện EOF trong 'for' tương tự như While. Cảm ơn
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Dưới đây là một ví dụ với chức năng ReadFromStdin()
giống như vậy fmt.Scan(&name)
nhưng nó lấy tất cả các chuỗi có khoảng trắng như: "Xin chào Tên tôi là ..."
var name string = ReadFromStdin()
println(name)