英文:
Reading a file line by line in Go
问题
我在Go中找不到file.ReadLine
函数。
如何逐行读取文件?
英文:
I'm unable to find file.ReadLine
function in Go.
How does one read a file line by line?
答案1
得分: 919
在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)
// 可选地,调整scanner的容量以处理超过64K的行,参见下一个示例
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
这是从Reader
逐行读取的最简洁的方法。
有一个注意事项:如果行的长度超过65536个字符,Scanner将报错。如果你知道你的行长度大于64K,请使用Buffer()
方法来增加scanner的容量:
...
scanner := bufio.NewScanner(file)
const maxCapacity int = longLineLen // 你需要的行长度
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)
for scanner.Scan() {
...
英文:
In Go 1.1 and newer the most simple way to do this is with a bufio.Scanner
. Here is a simple example that reads lines from a file:
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)
}
}
This is the cleanest way to read from a Reader
line by line.
There is one caveat: Scanner will error with lines longer than 65536 characters. If you know your line length is greater than 64K, use the Buffer()
method to increase the scanner's capacity:
...
scanner := bufio.NewScanner(file)
const maxCapacity int = longLineLen // your required line length
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)
for scanner.Scan() {
...
答案2
得分: 191
**注意:**在Go的早期版本中,接受的答案是正确的。查看最高投票的答案包含了更近期的惯用方法来实现这个。
在<code>bufio</code>包中有一个名为ReadLine的函数。
请注意,如果一行内容无法完全放入读取缓冲区,该函数将返回一个不完整的行。如果你想通过一次函数调用始终读取整行内容,你需要将<code>ReadLine</code>函数封装到你自己的函数中,并在一个for循环中调用<code>ReadLine</code>。
bufio.ReadString('\n')
并不完全等同于ReadLine
,因为ReadString
无法处理文件的最后一行不以换行符结尾的情况。
英文:
NOTE: The accepted answer was correct in early versions of Go. See the highest voted answer contains the more recent idiomatic way to achieve this.
There is function ReadLine in package <code>bufio</code>.
Please note that if the line does not fit into the read buffer, the function will return an incomplete line. If you want to always read a whole line in your program by a single call to a function, you will need to encapsulate the <code>ReadLine</code> function into your own function which calls <code>ReadLine</code> in a for-loop.
bufio.ReadString('\n')
isn't fully equivalent to ReadLine
because ReadString
is unable to handle the case when the last line of a file does not end with the newline character.
答案3
得分: 64
EDIT: 从go1.1开始,惯用的解决方案是使用bufio.Scanner。
我写了一个简单的方法来从文件中轻松读取每一行。Readln(*bufio.Reader)函数从底层的bufio.Reader结构中返回一行(不包括\n)。
// Readln从输入缓冲读取器中返回一行(不包括结尾的\n)
// 如果有输入缓冲读取器的错误,则返回错误。
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("打开文件时出错:%v\n",err)
os.Exit(1)
}
r := bufio.NewReader(f)
s, e := Readln(r)
for e == nil {
fmt.Println(s)
s,e = Readln(r)
}
干杯!
英文:
EDIT: As of go1.1, the idiomatic solution is to use bufio.Scanner
I wrote up a way to easily read each line from a file. The Readln(*bufio.Reader) function returns a line (sans \n) from the underlying bufio.Reader struct.
// 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
}
You can use Readln to read every line from a file. The following code reads every line in a file and outputs each line to 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)
}
Cheers!
答案4
得分: 45
有两种常见的方法可以逐行读取文件。
- 使用bufio.Scanner
- 使用bufio.Reader中的ReadString/ReadBytes/...
在我的测试用例中,对于大约250MB,大约2,500,000行的文件,bufio.Scanner(用时:0.395491384秒)比bufio.Reader.ReadString(用时:0.446867622秒)更快。
源代码: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("打开文件错误:%v", err)
return
}
defer f.Close()
sc := bufio.NewScanner(f)
for sc.Scan() {
_ = sc.Text() // 获取行字符串
}
if err := sc.Err(); err != nil {
log.Fatalf("扫描文件错误:%v", err)
return
}
}
使用bufio.Reader读取文件,
func readFileLines() {
f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
if err != nil {
log.Fatalf("打开文件错误:%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("读取文件行错误:%v", err)
return
}
_ = line // 获取行字符串
}
}
英文:
There two common way to read file line by line.
- Use bufio.Scanner
- Use ReadString/ReadBytes/... in bufio.Reader
In my testcase, ~250MB, ~2,500,000 lines, bufio.Scanner(time used: 0.395491384s) is faster than bufio.Reader.ReadString(time_used: 0.446867622s).
Source code: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Read file use 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
}
}
Read file use 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
得分: 21
Example from this 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
}
}
but this gives an error when there is a line that larger than Scanner's buffer.
When that happened, what I do is use reader := bufio.NewReader(inFile)
create and concat my own buffer either using ch, err := reader.ReadByte()
or len, err := reader.Read(myBuffer)
Another way that I use (replace os.Stdin with file like above), this one concats when lines are long (isPrefix) and ignores empty lines:
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
}
英文:
Example from this 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
}
}
but this gives an error when there is a line that larger than Scanner's buffer.
When that happened, what I do is use reader := bufio.NewReader(inFile)
create and concat my own buffer either using ch, err := reader.ReadByte()
or len, err := reader.Read(myBuffer)
Another way that I use (replace os.Stdin with file like above), this one concats when lines are long (isPrefix) and ignores empty lines:
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
}
答案6
得分: 13
你也可以使用ReadString和\n作为分隔符:
f, err := os.Open(filename)
if err != nil {
fmt.Println("打开文件错误", err)
os.Exit(1)
}
defer f.Close()
r := bufio.NewReader(f)
for {
path, err := r.ReadString(10) // 0x0A分隔符 = 换行符
if err == io.EOF {
// 在这里做一些操作
break
} else if err != nil {
return err // 如果返回错误
}
}
英文:
You can also use ReadString with \n as a separator:
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
}
}
答案7
得分: 7
另一种方法是使用io/ioutil
和strings
库来读取整个文件的字节,将其转换为字符串,并使用“\n
”(换行符)作为分隔符进行拆分,例如:
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
fileContent := string(bytesRead)
lines := strings.Split(fileContent, "\n")
}
从技术上讲,你并不是逐行读取文件,但是你可以使用这种技术解析每一行。这种方法适用于较小的文件。如果你要解析一个大文件,请使用逐行读取的技术之一。
英文:
Another method is to use the io/ioutil
and strings
libraries to read the entire file's bytes, convert them into a string and split them using a "\n
" (newline) character as the delimiter, for example:
import (
"io/ioutil"
"strings"
)
func main() {
bytesRead, _ := ioutil.ReadFile("something.txt")
fileContent := string(bytesRead)
lines := strings.Split(fileContent, "\n")
}
Technically you're not reading the file line-by-line, however you are able to parse each line using this technique. This method is applicable to smaller files. If you're attempting to parse a massive file use one of the techniques that reads line-by-line.
答案8
得分: 6
bufio.Reader.ReadLine()工作得很好。但是如果你想通过字符串读取每一行,请尝试使用ReadString('\n')。它不需要重新发明轮子。
英文:
bufio.Reader.ReadLine() works well. But if you want to read each line by a string, try to use ReadString('\n'). It doesn't need to reinvent the wheel.
答案9
得分: 4
// 去除 '\n' 或读取直到文件结束,如果读取错误则返回错误
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' { // 行结束
break
}
line = append(line, c)
}
if er != nil {
err = er
return
}
}
return
}
英文:
// 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
}
答案10
得分: 2
在下面的代码中,我使用Readline从CLI读取兴趣,直到用户按下回车键:
interests := make([]string, 1)
r := bufio.NewReader(os.Stdin)
for true {
fmt.Print("给我一个兴趣:")
t, _, _ := r.ReadLine()
interests = append(interests, string(t))
if len(t) == 0 {
break;
}
}
fmt.Println(interests)
英文:
In the code bellow, I read the interests from the CLI until the user hits enter and I'm using 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)
答案11
得分: 1
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Here is an example with function ReadFromStdin()
it's like fmt.Scan(&name)
but its takes all strings with blank spaces like: "Hello My Name Is ..."
var name string = ReadFromStdin()
println(name)
英文:
import (
"bufio"
"os"
)
var (
reader = bufio.NewReader(os.Stdin)
)
func ReadFromStdin() string{
result, _ := reader.ReadString('\n')
witl := result[:len(result)-1]
return witl
}
Here is an example with function ReadFromStdin()
it's like fmt.Scan(&name)
but its takes all strings with blank spaces like: "Hello My Name Is ..."
var name string = ReadFromStdin()
println(name)
答案12
得分: 0
The Scan* functions are of great user here. Here is a slightly modified version of word scanner example from go-lang docs to scan lines from a file.
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)
}
英文:
The Scan* functions are of great user here. Here is a slightly modified version of word scanner example from go-lang docs to scan lines from a file.
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)
}
答案13
得分: -2
在Go 1.16的新版本中,我们可以使用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/
英文:
In the new version of Go 1.16 we can use package embed to read the file contents as shown below.
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))
}
For more details go through https://tip.golang.org/pkg/embed/
And
https://golangtutorial.dev/tips/embed-files-in-go/
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论