我无法在 Go 语言中找到 file.ReadLine
函数。
如何逐行读取文件?
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个字符,扫描器会出错。如果您知道您的行长度大于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)
,这很容易实现。 - Evan Plumleebufio.ErrTooLong
错误,即bufio.Scanner:token too long
。这种情况下,您将不得不使用bufio.ReaderLine()或ReadString()。 - eduncan911注意:在Go的早期版本中,接受的答案是正确的。请参阅得票最高的答案包含了更近期的惯用方法来实现这一点。
在bufio
包中有一个名为ReadLine的函数。
请注意,如果行不适合读取缓冲区,该函数将返回不完整的行。如果您希望通过单个函数调用始终读取整行,请将ReadLine
函数封装到自己的函数中,并在for循环中调用ReadLine
。
bufio.ReadString('\n')
与ReadLine
并不完全等价,因为ReadString
无法处理文件的最后一行不以换行符结尾的情况。
我写了一个简单的方法来从文件中轻松读取每行内容。Readln(*bufio.Reader)函数从基础的bufio.Reader结构返回一行内容(不包括\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)
}
干杯!
读取文件的两种常见方法:
在我的测试用例中,对于 ~250MB、~2,500,000 行的文件,bufio.Scanner (时间使用:0.395491384s) 比 bufio.Reader.ReadString (时间使用: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
。 - konrad这里有一个来自于 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)
另一种我使用的方法(用文件代替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
}
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
}
}
另一种方法是使用 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()
似乎做相同的事情。 - user2233706// 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
}
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)