我无法在Go中找到file.ReadLine
函数。我可以弄清楚如何快速写一个,但只是想知道我是否在这里忽略了一些东西。如何逐行读取文件?
答案 0 :(得分:487)
在Go 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()
。
答案 1 :(得分:104)
包bufio
中有功能ReadLine。
请注意,如果该行不适合读取缓冲区,则该函数将返回不完整的行。如果您希望通过一次调用函数来总是在程序中读取整行,则需要将ReadLine
函数封装到您自己的函数中,该函数在for循环中调用ReadLine
。 / p>
bufio.ReadString('\n')
与ReadLine
不完全等效,因为ReadString
无法处理文件的最后一行未以换行符结尾的情况。
答案 2 :(得分:57)
使用:
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.
答案 3 :(得分:48)
我写了一种方法来轻松读取文件中的每一行。 Readln(* bufio.Reader)函数从底层的bufio.Reader结构返回一行(sans \ n)。
// 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)
}
干杯!
答案 4 :(得分:18)
有两种常用的逐行读取文件的方法。
在我的测试用例中, ~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
}
}
答案 5 :(得分:8)
您也可以使用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
}
}
答案 6 :(得分:6)
此gist
的示例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
}
}
但是如果有一条线大于Scanner的缓冲区,则会出错。
当发生这种情况时,我所做的就是使用reader := bufio.NewReader(inFile)
创建并使用ch, err := reader.ReadByte()
或len, err := reader.Read(myBuffer)
答案 7 :(得分:4)
bufio.Reader.ReadLine()效果很好。但是,如果您想通过字符串读取每一行,请尝试使用ReadString('\n')。它不需要重新发明轮子。
答案 8 :(得分:3)
// 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
}
答案 9 :(得分:1)
在下面的代码中,我从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)
答案 10 :(得分:1)
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)
类似,但它的所有字符串都带有空格,例如:&#34; Hello My Name is ...&#34;
var name string = ReadFromStdin()
println(name)
答案 11 :(得分:1)
另一种方法是使用io/ioutil
和strings
库读取整个文件的字节,将它们转换为字符串,然后使用“ \n
”(换行符)将它们拆分为分隔符,例如:
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
file_content := string(bytesRead)
lines := strings.Split(file_content, "\n")
}
从技术上讲,您不是逐行读取文件,但是可以使用此技术解析每一行。此方法适用于较小的文件。如果您尝试解析海量文件,请使用一种逐行读取的技术。
答案 12 :(得分:1)
在新版本的Go 1.16中,我们可以使用包嵌入来读取文件内容,如下所示。
# dummy data:
array1 = np.random.randint(10, size=10)
array2 = np.random.randint(10, size=15)
grid = sns.displot([array1, array2], kind="kde")
lg = grid.fig.legends[0]
lg.texts[0].set_text('ZERO')
lg.texts[1].set_text('ONE')
有关更多详细信息,请访问https://tip.golang.org/pkg/embed/ 和 https://golangtutorial.dev/tips/embed-files-in-go/
答案 13 :(得分:0)
我喜欢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
}
}
我不确定为什么我需要再次测试'错误',但无论如何我们都可以做到。 但是,主要问题是...... 为什么围棋不会产生错误 =&GT; line,err:= r.ReadString(10),在循环中? 每次执行循环时都会一次又一次地定义它。 我的变化,任何评论都避免了这种情况? 我将'for'中的条件EOF设置为类似于While。 感谢