Go xmas2020 学习笔记 07、Formatted & File I/O

课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)

主讲老师 Matt Holiday

image-20220401081031592

07-Formatted & File I/O

I/O steams

image-20220404094820605

操作系统具有三个标准 io 流,标准输入、标准输出、标准错误。它们分别可以重定向。


formatted I/O

image-20220404095050876

Println 将参数默认输出到标准输出流,如果会用 Fprintln 可以指定输出到某个流,比如 os.Stderr


fmt functions

image-20220404095236836

Sprintln 格式化字符串并返回。

image-20220404095458035


package main

import "fmt"

func main() {
	a, b := 12, 345
	c, d := 1.2, 3.45

	fmt.Printf("%d %d\n", a, b)
	fmt.Printf("%x %x\n", a, b)
	fmt.Printf("%#x %#x\n", a, b)
	fmt.Printf("%f %.2f", c, d)
	fmt.Println()
	fmt.Printf("|%6d|%6d|\n", a, b)
	fmt.Printf("|%-6d|%-6d|\n", a, b)
	fmt.Printf("|%06d|%06d|\n", a, b)
	fmt.Printf("|%9f|%9.2f|\n", c, d) // ^ 当数字过大时也会超出
}
12 345
c 159
0xc 0x159
1.200000 3.45
|    12|   345|
|12    |345   |
|000012|000345|
| 1.200000|     3.45|

package main

import (
	"fmt"
)

func main() {
	s := []int{1, 2, 3}
	a := [3]rune{'a', 'b', 'c'}
	m := map[string]int{"and": 1, "or": 2}
	ss := "a string"
	b := []byte(ss)

	fmt.Printf("%T\n", s)
	fmt.Printf("%v\n", s)
	fmt.Printf("%#v\n", s) // ^ %#v 更符合初始化时输入的形式
	fmt.Println()
	fmt.Printf("%T\n", a)
	fmt.Printf("%v\n", a)
	fmt.Printf("%q\n", a) // ^ 注意这个%q将rune从int32转化成了字符串
	fmt.Printf("%#v\n", a)
	fmt.Println()
	fmt.Printf("%T\n", m)
	fmt.Printf("%v\n", m)
	fmt.Printf("%#v\n", m)
	fmt.Println()
	fmt.Printf("%T\n", ss)
	fmt.Printf("%v\n", ss)
	fmt.Printf("%q\n", ss)
	fmt.Printf("%#v\n", ss)
	fmt.Printf("%v\n", b)
	fmt.Printf("%v\n", string(b)) // ^ 将字节切片转换为字符串
}
[]int
[1 2 3]
[]int{1, 2, 3}

[3]int32
[97 98 99]
['a' 'b' 'c']
[3]int32{97, 98, 99}

map[string]int
map[and:1 or:2]
map[string]int{"and":1, "or":2}

string
a string
"a string"
"a string"
[97 32 115 116 114 105 110 103]
a string

file I/O

image-20220404102923944

Practice ① I/O

编写一个类似 Unix cat 的程序,将多个文件输出到标准输出流中,并输出为一个文件。

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	for _, fname := range os.Args[1:] {
		file, err := os.Open(fname)

		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		if _, err := io.Copy(os.Stdout, file); err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		fmt.Fprint(os.Stdout, "\n") // ^ 每个文件内容末尾添加换行符
		file.Close()
	}
}

io.copy 是一个很棒的功能,它知道如何缓冲、如何以块的形式读入并写会,它不会尝试把整个文件读取到内存中也不会一次读取一个字符。

file.Close 大多数操作系统对程序中打开多少个文件有限制,所以文件使用完成后需要进行关闭。

在当前目录新建 txt 文件,写入内容。执行下面三条命令。

go run . a.txt
go run . a.txt b.txt c.txt
go run . a.txt b.txt c.txt > new.txt

第二条指令结果
[]int{1, 2, 3}
go go go
people car
cat
apple
banana

第三条指令在当前目录生成了 new.txt 文件,内容是 标准输出流 的内容。

image-20220404105436335

image-20220404105445902


Always check the err

image-20220404111400635

Practice ② I/O

编写一个简短的程序计算文件大小。一次性读取(小文件情况下)

我们前面知道, io/ioutil 包可以对整个文件进行读取,存入内存中。我们可以使用它计算文件大小。

原先的 io.Copy 返回的是复制的字节数,而 ReadAll 将返回整个 data ,字节切片和一个err。

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	for _, fname := range os.Args[1:] {
		file, err := os.Open(fname)

		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		data, err := ioutil.ReadAll(file)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		fmt.Println("The file has", len(data), "bytes")
		file.Close()
	}
}
go run . a.txt b.txt c.txt

The file has 30 bytes
The file has 20 bytes
The file has 18 bytes

data, err := ioutil.ReadAll(file)if 中取出单独成行,是因为需要 data 这个变量。如果放在 if 短声明里会导致作用域只在 if 语句块内。


Practice ③ I/O

编写一个 wc 程序(word counter),输出lines、words、characters数量。使用缓冲 buffio(大文件情况下)

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func main() {
	for _, fname := range os.Args[1:] {
		var lc, wc, cc int

		file, err := os.Open(fname)

		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}

		scan := bufio.NewScanner(file)

		for scan.Scan() {
			s := scan.Text()
			wc += len(strings.Fields(s)) // ^ 根据空格、制表符分割 a slice of words
			cc += len(s)
			lc++
		}

		fmt.Printf("%7d %7d %7d %s\n", lc, wc, cc, fname)
		file.Close()
	}
}
go run . a.txt b.txt c.txt

      3       7      26 a.txt
      2       5      18 b.txt
      3       3      14 c.txt

bufio.NewScanner(file) 创建一个扫描器按行扫描。考虑到多行需要用 for 循环 scan.Scan

strings.Fields(s) 根据空格、制表符分割,拿到的是字符串切片。

posted @ 2022-04-04 11:48  小能日记  阅读(12)  评论(0编辑  收藏  举报