繁体   English   中英

在 Go 中逐行读取文件

[英]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 的早期版本中是正确的。 查看投票最高的答案包含实现此目的的最新惯用方法。

bufio有函数ReadLine

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

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

我在Go中找不到file.ReadLine函数。 我可以弄清楚如何快速编写一个,但是我只是想知道我是否在这里忽略了一些东西。 如何逐行读取文件?

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

我写了一种方法来轻松读取文件中的每一行。 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)
}

干杯!

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

  1. 使用 bufio.Scanner
  2. 在 bufio.Reader 中使用 ReadString/ReadBytes/...

在我的测试用例中, ~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/ioutilstrings库读取整个文件的字节,将它们转换为字符串并使用“ \\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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM