在Go中逐行读取文件

我无法在Go中找到file.ReadLine函数。

如何一行一行地读取文件?

500920 次浏览

在早期版本的围棋中接受的答案是正确的。查看投票最高的答案包含了实现这一点的最新惯用方法。

在包bufio中有函数ReadLine

请注意,如果该行不适合读缓冲区,该函数将返回一个不完整的行。如果你想总是通过对函数的一次调用来读取程序中的整行,你需要将ReadLine函数封装到你自己的函数中,该函数在for循环中调用ReadLine

bufio.ReadString('\n')并不完全等同于ReadLine,因为ReadString无法处理文件的最后一行不以换行符结束的情况。

编辑:从go1.1开始,惯用的解决方案是使用bufio。扫描仪

我写了一种从文件中轻松读取每一行的方法。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从文件中读取每一行。下面的代码读取文件中的每一行,并将每一行输出到标准输出。

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)
}

干杯!

bufio.Reader.ReadLine ()工作良好。但如果你想通过字符串读取每一行,请尝试使用ReadString(“\ n”)。它不需要重新发明轮子。

你也可以使用带\n的ReadString作为分隔符:

  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.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)
// 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个字符时,Scanner将出错。如果你知道你的行长大于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() {
...

要点的例子

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 with file like above),当行很长(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
}

在下面的代码中,我从CLI中读取兴趣,直到用户按下enter键,我使用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),但它接受所有带空格的字符串,比如:"Hello My Name is…"

var name string = ReadFromStdin()


println(name)

有两种常见的逐行读取文件的方法。

  1. 使用bufio。扫描仪
  2. 使用ReadString / ReadBytes /……在bufio。读者

在我的测试用例中,~250MB, ~250万行, bufio。Scanner(使用时间:0.395491384s)比bufio.Reader快。ReadString (time_used: 0.446867622 s)。

源代码: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
}
}

另一种方法是使用io/ioutilstrings库来读取整个文件的字节,将它们转换为字符串并使用"\n"(换行符)字符作为分隔符,例如:

import (
"io/ioutil"
"strings"
)


func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
fileContent := string(bytesRead)
lines := strings.Split(fileContent, "\n")
}

从技术上讲,您不是逐行读取文件,但是您可以使用这种技术解析每一行。此方法适用于较小的文件。如果您试图解析一个大型文件,请使用逐行读取的技术之一。

在Go 1.16的新版本中,我们可以使用包嵌入来读取文件内容,如下所示。

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/ < / p >

扫描*功能是伟大的用户在这里。下面是go-lang docs中稍微修改过的单词扫描器示例版本,用于扫描文件中的行。

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)
}