String 在Go中逐行读取文件

String 在Go中逐行读取文件,string,file,parsing,go,line,String,File,Parsing,Go,Line,我在Go中找不到文件.ReadLine函数。我能想出如何快速编写一个,但我只是想知道我是否忽略了一些东西。如何逐行读取文件?注意:在早期版本的Go中,公认的答案是正确的。包含实现此目的的最新惯用方法 包bufio中有函数 请注意,如果该行不适合读取缓冲区,则函数将返回不完整的行。如果您希望通过对函数的一次调用始终读取程序中的整行,则需要将ReadLine函数封装到自己的函数中,该函数在for循环中调用ReadLine bufio.ReadString('\n')并不完全等同于ReadLine,因

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

注意:在早期版本的Go中,公认的答案是正确的。包含实现此目的的最新惯用方法

bufio
中有函数

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

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

EDIT:从go1.1开始,惯用的解决方案是使用 我写了一个从文件中轻松读取每一行的方法。Readln(*bufio.Reader)函数从基础bufio.Reader结构返回一行(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读取文件中的每一行。下面的代码读取文件中的每一行,并将每一行输出到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)
}

干杯

效果很好。但是如果你想用一个字符串来读每一行,试着使用。它不需要重新设计控制盘。

您也可以将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
    }
  }

在Go 1.1及更新版本中,最简单的方法是使用一个。下面是一个从文件中读取行的简单示例:

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)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}
这是逐行读取
读取器
的最干净的方法

有一个警告:扫描仪不能很好地处理长度超过65536个字符的行。如果这对您来说是一个问题,那么您可能应该在
Reader.Read()

这个示例的顶部滚动您自己的

但当有一行比扫描仪的缓冲区大时,就会出现错误

当发生这种情况时,我所做的是使用
reader:=bufio.NewReader(infle)
创建并使用
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
}

在下面的代码中,我从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)
使用:

  • reader.ReadString('\n')
    • 如果您不介意线路可能很长(即使用大量RAM)。它将
      \n
      保留在返回的字符串末尾
  • reader.ReadLine()
    • 如果您关心限制RAM消耗,并且不介意处理行大于读取器缓冲区大小的情况的额外工作
我通过编写一个程序来测试其他答案中确定为问题的场景,从而测试了建议的各种解决方案:

  • 具有4MB行的文件
  • 不以换行符结尾的文件
我发现:

  • 扫描仪
    解决方案不处理长线
  • ReadLine
    解决方案实施起来很复杂
  • ReadString
    解决方案是最简单的,适用于长行
下面是演示每个解决方案的代码,可以通过
go-run-main.go
,或在

下面是一个函数
ReadFromStdin()
的示例,它类似于
fmt.Scan(&name)
,但它会使用带有空格的所有字符串,如:“您好,我的名字是…”


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

  • 使用bufio扫描仪
  • 使用ReadString/ReadBytes/。。。在bufio.Reader
  • 在我的测试用例中,~250MB,~2500000行,bufio.Scanner(使用时间:0.395491384s)比bufio.Reader.ReadString(使用时间:0.446867622s)快

    源代码:

    使用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
        }
    }
    

    另一种方法是使用
    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")
    }
    

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

    在新版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))
    }
    
    有关更多详细信息,请浏览 及

    在Go 1.1发布之前,我写下了这个答案。Go 1.1在stdlib中有一个扫描仪包。这提供了与我的答案相同的功能。我建议使用Scanner而不是我的答案,因为Scanner在stdlib中。快乐黑客!:-)由于OP要求扫描一个文件,所以先扫描
    文件,:=os.Open(“/path/to/file.csv”)
    ,然后扫描文件句柄:
    scanner:=bufio.NewScanner(文件)
    问题是scanner.scan()限制在每行4096[]字节的缓冲区大小。如果行太长,您将得到
    bufio.ErrTooLong
    error,即
    bufio.Scanner:token toom long
    。在这种情况下,您必须使用bufio.ReaderLine()或ReadString()。仅我的$0.02-这是页面上最正确的答案:)从源代码开始,现在限制为64 KB,而不是4 KB,请参阅:您可以使用其Buffer()方法将扫描仪配置为处理更长的行:从Go1.1开始,bufio.Scanner是最好的方法。从文档:“ReadLine是一种低级的行读取原语。大多数调用者应该使用ReadBytes('\n')或ReadString('\n'),或者使用扫描仪。“@mdwhatcott它为什么会
    import (
         "bufio"
         "os"
    )
    
    var (
        reader = bufio.NewReader(os.Stdin)
    )
    
    func ReadFromStdin() string{
        result, _ := reader.ReadString('\n')
        witl := result[:len(result)-1]
        return witl
    }
    
    var name string = ReadFromStdin()
    
    println(name)
    
    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
        }
    }
    
    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
        }
    }
    
    import (
        "io/ioutil"
        "strings"
    )
    
    func main() {
        bytesRead, _ := ioutil.ReadFile("something.txt")
        file_content := string(bytesRead)
        lines := strings.Split(file_content, "\n")
    }
    
    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))
    }