提问人:g06lin 提问时间:1/6/2012 最后编辑:Amin Shojaeig06lin 更新时间:12/7/2022 访问量:559813
在 Go 中逐行读取文件
Reading a file line by line in Go
答:
注意:在早期版本的 Go 中,公认的答案是正确的。请参阅得票最高的答案包含实现此目的的最新惯用方法。
包中有函数 ReadLine。bufio
请注意,如果该行不适合读取缓冲区,该函数将返回一个不完整的行。如果您希望始终通过对函数的单次调用来读取程序中的整行,则需要将该函数封装到您自己的函数中,该函数在 for 循环中调用。ReadLine
ReadLine
bufio.ReadString('\n')
不完全等同于 因为无法处理文件最后一行不以换行符结尾的情况。ReadLine
ReadString
评论
编辑:从 go1.1 开始,惯用的解决方案是使用 bufio。扫描器
我写了一种方法来轻松读取文件中的每一行。The Readln(*bufio.Reader) 函数从底层 bufio 返回一行 (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)
}
干杯!
评论
布菲奥。Reader.ReadLine() 运行良好。但是,如果要按字符串读取每一行,请尝试使用 ReadString('\n')。它不需要重新发明轮子。
还可以将 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
}
}
评论
// 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
}
在 Go 1.1 及更高版本中,最简单的方法是使用 bufio。扫描仪
。下面是从文件中读取行的简单示例:
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)
// optionally, resize scanner's capacity for lines over 64K, see next example
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
这是逐行阅读的最干净的方式。Reader
有一点需要注意:如果行长超过 65536 个字符,扫描程序就会出错。如果您知道行长度大于 64K,请使用 Buffer()
方法增加扫描仪的容量:
...
scanner := bufio.NewScanner(file)
const maxCapacity int = longLineLen // your required line length
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)
for scanner.Scan() {
...
评论
file, _ := os.Open("/path/to/file.csv")
scanner := bufio.NewScanner(file)
bufio.ErrTooLong
bufio.Scanner: token too long
这个要点的例子
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 的缓冲区时,这会产生错误。
发生这种情况时,我所做的就是使用 create 和连接我自己的缓冲区,或者reader := bufio.NewReader(inFile)
ch, err := reader.ReadByte()
len, err := reader.Read(myBuffer)
我使用的另一种方式(替换 os.Stdin 文件如上所示),当行很长 (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
在下面的代码中,我从 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)
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)
var name string = ReadFromStdin()
println(name)
有两种常见的方法可以逐行读取文件。
- 使用 bufio。扫描器
- 使用 ReadString/ReadBytes/...在布菲奥。读者
在我的测试用例中,~250MB,~2,500,000 行,bufio。扫描仪(使用时间:0.395491384s)比 bufio 快。Reader.ReadString(time_used:0.446867622s)。
源代码: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
使用 bufio 读取文件。扫描器
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 读取文件。读者
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
另一种方法是使用 和 库来读取整个文件的字节,将它们转换为字符串,并使用 “”(换行符)字符作为分隔符来拆分它们,例如:io/ioutil
strings
\n
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
fileContent := string(bytesRead)
lines := strings.Split(fileContent, "\n")
}
从技术上讲,您不是逐行读取文件,但是您可以使用此技术解析每一行。此方法适用于较小的文件。如果您尝试解析大型文件,请使用逐行读取的技术之一。
评论
os.ReadFile()
似乎也在做同样的事情。
在新版本的 Go 1.16 中,我们可以使用 package embed 来读取文件内容,如下所示。
package main
import _"embed"
func main() {
//go:embed "hello.txt"
var s string
print(s)
//go:embed "hello.txt"
var b []byte
print(string(b))
//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))
}
有关更多详细信息,请查看 https://tip.golang.org/pkg/embed/ 和 https://golangtutorial.dev/tips/embed-files-in-go/
评论
嵌入
包,但我认为你的回答并没有解决问题的核心。OP 希望逐行读取文件。即便如此,你还是为他提供了一种非常好和惯用的方式,让他阅读整个文件。
Scan* 功能在这里非常有用。这是 go-lang 文档中用于扫描文件中行的 word scanner 示例的略微修改版本。
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// An artificial input source.
const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
scanner := bufio.NewScanner(strings.NewReader(input))
// Set the split function for the scanning operation.
scanner.Split(bufio.ScanLines)
// Count the lines.
count := 0
for scanner.Scan() {
fmt.Println(scanner.Text())
count++
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
fmt.Printf("%d\n", count)
}
评论