以下是个人理解
1、bufio Reader 读取
bufio包设计旨在为文本操作提供帮助,以Reader Writer为主。
bufio.NewReader() “包装一个 io.Reader 或 io.Writer 对象,创建另一个对象(Reader 或 Writer),该对象也实现了该接口,但为文本 I/O 提供了缓冲和一些帮助”源。
// Reader implements buffering for an io.Reader object.
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
// Writer implements buffering for an io.Writer object.
// If an error occurs writing to a Writer, no more data will be
// accepted and all subsequent writes, and Flush, will return the error.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer.
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}
使用案例:
统计 Uncideo字符的个数
in := bufio.NewReader(os.Stdin)
for {
r, n, err := in.ReadRune() // returns rune, nbytes, error
if err == io.EOF {
// EOF的按钮是 ctrl+D
break
}
if err != nil {
fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
os.Exit(1)
}
if r == unicode.ReplacementChar && n == 1 {
// If it is illegal UTF-8, handle it here
continue
}
// Other operations
}
2、os.Args 命令行读入
Args hold the command-line arguments, starting with the program name.
for _, arg := range os.Args[1:] {
t, err := strconv.ParseFloat(arg, 64)
if err != nil {
fmt.Fprintf(os.Stderr, "cf: %v\n", err)
os.Exit(1)
}
// 获取到 float格式的数据
}
3、bufio NewScanner 读取
Scanner provides a convenient interface for reading data such as a file of newline-delimited lines of text. Successive calls to the Scan method will step through the 'tokens' of a file, skipping the bytes between the tokens. The specification of a token is defined by a split function of type SplitFunc; the default split function breaks the input into lines with line termination stripped. Split functions are defined in this package for scanning a file into lines, bytes, UTF-8-encoded runes, and space-delimited words. The client may instead provide a custom split function.
Scanning stops unrecoverably at EOF, the first I/O error, or a token too large to fit in the buffer. When a scan stops, the reader may have advanced arbitrarily far past the last token. Programs that need more control over error handling or large tokens, or must run sequential scans on a reader, should use bufio.Reader instead.
大概意思是,NewScanner是一个很方便的读取数据的方式。可以按照什么样的方式读取数据,比如不按照行读取,按照单词读取。只需要 在scan前加入 "input.Split(bufio.ScanWords)” 即可
使用案例
// Dedup prints only one instance of each line; duplicates are removed.
package main
import (
"bufio"
"fmt"
"os"
)
//!+
func main() {
seen := make(map[string]bool) // a set of strings
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
line := input.Text()
if !seen[line] {
seen[line] = true
fmt.Println(line)
}
}
if err := input.Err(); err != nil {
fmt.Fprintf(os.Stderr, "dedup: %v\n", err)
os.Exit(1)
}
}
4、fmt.Scanf
fmt.Scanf() 从一个流中读取数据(可能会或可能不会缓冲它。由 bufio 包返回),将输入按空格拆分,以将其存储在切片中。
它和 bufio.NewReader(os.Stdin) 是两种不同的方式读取数据
inputText := ""
fmt.Scanf("%s", &inputText) //注意此方法在win下会因为\r\n读取两次
fmt.Printf("fmt.Scanf: %q\r\n", inputText)
5、os.Stdin
通过阅读源码,可以看出 os.Stdin 是通过创建一个临时文件去保存控制台输入的值。然后将这个文件内容输出。
下面这个图是 syscall.Handel的类型,描述 比图 stdin的 参数 是:
STD_INPUT_HANDLE = -10
创建stdin的输入 放在下图。
稍微解释下。序列化处理的地方。简单围绕下面几点考虑
- 创建是一个文件,那么就需要有对象的终结器(关闭文件等操作)
- 终结器何时关闭?
- SetFinalizer(obj, nil) 清除任何与 obj 关联的终结器。
- 输入文件变得不可达时,如果处理
源码中的描述是这么说的:
SetFinalizer 将与 obj 关联的终结器设置为提供的 终结器函数。当垃圾收集器发现无法到达的块时 使用关联的终结器,它清除关联并运行 finalizer(obj) 在一个单独的 goroutine 中
例如,如果 p 指向一个包含文件描述符 d 的结构, 并且 p 有一个终结器来关闭该文件描述符,如果最后一个 在函数中使用 p 是对 syscall.Write(p.d, buf, size) 的调用,然后 一旦程序进入 syscall.Write,p 就可能无法访问。这 终结器可能在那个时候运行,关闭 p.d,导致 syscall.Write 失败,因为它正在写入一个关闭的文件描述符(或者,更糟糕的是, 到由不同 goroutine 打开的完全不同的文件描述符)。 为避免此问题,请在调用后调用 runtime.KeepAlive(p) 系统调用