Go语言读取文件的方法小结

 更新时间:2024年01月22日 08:13:55   作者:hankeyyh  
写程序时经常需要从一个文件读取数据,然后输出到另一个文件,这篇文章主要为大家详细介绍了Go语言读取文件的几种方法,希望对大家有所帮助

写程序时经常需要从一个文件读取数据,然后输出到另一个文件。最典型的例子比如,从stdin读取,然后输出到stdout。go提供了几种读取文件的方式,我们逐个来看一看~

fmt.Scan

func fmtScan() {
    str := ""
    for {
        fmt.Scan(&str)
        fmt.Printf("[fmt.scan]: %q (%d)\n", str, len(str))
    }
}
// input: abc def ghi
// output:
//[fmt.scan]: "abc" (3)
//[fmt.scan]: "def" (3)
//[fmt.scan]: "ghi" (3)

fmt.Scan 从stdin读取数据,输出到内存变量。它会将空白符作为分割符,把分割后的字符串,依次赋值给传入参数。在上面的例子中,就是循环了3次分别赋值给str。

os.Stdin.Read

func osStdinRead() {
    p := make([]byte, 5)
    for {
        nn, err := os.Stdin.Read(p)
        if err != nil && err != io.EOF {
            panic(err)
        }
        fmt.Printf("[os.stdin.read]: %q (%d)\n", p, nn)
        p = make([]byte, 5) // 清空p
    }
}
// input: abcdefghijk
// output:
//[os.stdin.read]: "abcde" (5)
//[os.stdin.read]: "fghij" (5)
//[os.stdin.read]: "k\n\x00\x00\x00" (2)

最多读取len(p)大小的数据,如果输入的内容超出了len(p),则Read会在下一轮循环中接着读取。

io系列

ReadAll

func ioReadAll() {
    for {
        p, err := io.ReadAll(os.Stdin)
        if err != nil {
            panic(err)
        }
        fmt.Printf("[io.ReadAll]: %q (%d)\n", p, len(p))
    }
}

// input: abc\nEOF
// output: [io.ReadAll]: "abc\n" (4)

ReadAll 读取全部,不把EOF当作错误返回。

ReadFull

func ioReadFull() {
    p := make([]byte, 5)
    for {
        nn, err := io.ReadFull(os.Stdin, p)
        if err != nil && err != io.EOF {
            fmt.Printf("[io.ReadFull]: %q (%d)\n", p, nn)
            panic(err)
        }
        fmt.Printf("[io.ReadFull]: %q (%d)\n", p, nn)
        p = make([]byte, 5)
    }
}

// input: aaaaaa
// output: [io.ReadFull]: "aaaaa" (5)

// input: aa\n\x00
// output: [io.ReadFull]: "aa\n\x00\x00" (3)

ReadFull 的目标是把p填满,也就是说正常情况下只有读到的数据长度nn==len(p)才返回。这里有两种情况需要注意:

  • 没有数据可读,ReadFull返回 (0, EOF)
  • 有一些数据可读,但在填满p前遇到了EOF,ReadFull返回 (读取的长度, ErrUnexpectedEOF)

bufio系列

bufio给io操作提供了一个缓冲,避免了每次都要访问磁盘。

Read

func bufioRead() {
    br := bufio.NewReader(os.Stdin)
    p := make([]byte, 5)
    for {
        nn, err := br.Read(p)
        if err != nil && err != io.EOF{
            panic(err)
        }
        fmt.Printf("[bufio.read]: %q (%d)\n", p, nn)
    }
}

// input: abcdefghijk
// output:
//[bufio.read]: "abcde" (5)
//[bufio.read]: "fghij" (5)
//[bufio.read]: "k\n\x00\x00\x00" (2)

最多读取len(p)大小的数据,如果输入的内容超出了len(p),则Read会在下一轮循环中接着读取。

ReadBytes

func bufioReadBytes() {
    br := bufio.NewReader(os.Stdin)
    for {
        p, err := br.ReadBytes('\n')
        if err != nil && err != io.EOF{
            panic(err)
        }
        fmt.Printf("[bufio.ReadBytes]: %q (%d)\n", p, len(p))
    }
}
// input: abcdef
// output: [bufio.ReadBytes]: "abcdef\n" (7)

ReadBytes 读取直到遇到给定的分界符,这里是'\n'(返回的p包含\n)。如果在读到分界符之前遇到了err,则返回读到的数据和err(一般是EOF)。

ReadString

func bufioReadString() {
    br := bufio.NewReader(os.Stdin)
    for {
        str, err := br.ReadString('\n')
        if err != nil && err != io.EOF {
            panic(err)
        }
        fmt.Printf("[bufio.ReadString]: %q (%d)\n", str, len(str))
    }
}

// input: abcdef
// output: [bufio.ReadString]: "abcdef\n" (7)

ReadString 与 ReadBytes 类似,都是直到读到分界符才返回。

ReadRune

func bufioReadRune() {
    br := bufio.NewReader(os.Stdin)
    for {
        r, sz, err := br.ReadRune()
        if err != nil && err != io.EOF {
            panic(err)
        }
        fmt.Printf("[bufio.ReadRune]: %q (%d)\n", r, sz)
    }
}

// input: abc
// output: 
//[bufio.ReadRune]: 'a' (1)
//[bufio.ReadRune]: 'b' (1)
//[bufio.ReadRune]: 'c' (1)
//[bufio.ReadRune]: '\n' (1)

ReadRune 一次读取一个UTF8字符。

Scan

func bufioScan() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()
        fmt.Printf("[bufio.Scan]: %q (%d)\n", text, len(text))
    }
    if scanner.Err() != nil {
        fmt.Print(scanner.Err().Error())
    }
}

Scan 扫描文件到下一个token并返回true,随后可以通过Text,Bytes读取token。当扫描停止时,Scan返回false,这时可以通过Err获取错误,如果是因为EOF而扫描停止,Err=nil。

scanner在创建时,默认使用 ScanLines 作为分割函数。它会将输入文本分割成一系列的行,每行作为一个token。除此之外还有其他分割函数:

  • ScanLines(默认):按行分割。
  • ScanWords:按空白字符分割的单词。
  • ScanBytes:按字节分割。
  • ScanRunes:按UTF-8编码的字符分割。

总结

如果只是简单从stdin读取,fmt.Scan 足够短小好用。当文件很大时 io.ReadAll, io.ReadFull 感觉在使用时不是很灵活,还是推荐用bufio的一系列方法操作。

到此这篇关于Go语言读取文件的方法小结的文章就介绍到这了,更多相关Go读取文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论