[英]Reading a file line by line in Go
我无法在 Go 中找到file.ReadLine
function。
如何逐行读取文件?
在 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 个字符时出错。 如果您知道您的行长度大于 64K,请使用Buffer()
方法来增加扫描仪的容量:
...
scanner := bufio.NewScanner(file)
const maxCapacity = longLineLen // your required line length
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)
for scanner.Scan() {
...
我在Go中找不到file.ReadLine
函数。 我可以弄清楚如何快速编写一个,但是我只是想知道我是否在这里忽略了一些东西。 如何逐行读取文件?
我写了一种方法来轻松读取文件中的每一行。 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(time used: 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
}
}
这个要点的例子
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),当行很长(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
}
您还可以使用 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
}
}
bufio.Reader.ReadLine()运行良好。 但是,如果您想通过字符串读取每一行,请尝试使用ReadString('\\n') 。 它不需要重新发明轮子。
// 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
}
在下面的代码中,我从 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)
另一种方法是使用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")
}
从技术上讲,您不是逐行读取文件,但是您可以使用此技术解析每一行。 此方法适用于较小的文件。 如果您试图解析一个大文件,请使用一种逐行读取的技术。
我喜欢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
}
}
我不确定为什么需要再次测试“ err”,但是无论如何我们都可以做到。 但是,主要问题是..为什么Go在循环内不会在句子=>行err:= r.ReadString(10)上产生错误? 每次循环执行一次又一次定义。 我可以通过更改来避免这种情况,对此有何评论? 我也在'for'中将条件EOF设置为类似于While。 谢谢
在 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/
Scan* 函数在这里非常有用。 这是来自 go-lang 文档的单词扫描器示例的稍微修改版本,用于扫描文件中的行。
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)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.