I/O操作也叫输入输出操作

  • 其中I是指Input
  • O是指Output
  • 主要用来读取或写入数据,很多语言中也叫做流操作

Go 输入和输出操作是使用原语实现的

  • 这些原语将数据模拟成可以读或者可以写的字节流

io库属于底层接口定义库

  • 作用是定义一些基本的接口和基本常量
  • 其他人调用它常量,如io.EOF

io库比较常用的接口有三个,分别是Reader,Writer和Close。

  • io库中实现的上述接口可以 以流的方式高效处理数据
  • 并不需要考虑数据是什么,数据来自哪里,数据要发送到哪里

Reader

  • io.Reader表示一个读取器
  • 它从某个地方读取数据到 传输的缓存区
  • 在缓存区里面,数据可以被流式的使用
  • 接口签名如下
  1. type Reader interface {
  2. Read(p []byte) (n int, err error)
  3. }

strings.NewReader

  • 上面提到了io.Reader 接口只有一个方法Read方法
  • 换句话说 只要有个对象实现了Read方法,那么这个对象就是一个读取器
  • Read() 首先要有一个读缓冲区的参数
  • Read() 返回两个值,第一个是读取到的字节数,第二个是读取时发生的错误
  • 注意:返回到的读取字节个数n可能小于缓冲区的大小
  • io.EOF 表示输入的流已经读到头了
  • 代码如下
  1. package main
  2. import (
  3. "io"
  4. "log"
  5. "strings"
  6. )
  7. // 实现一个reader 每次读取4个字节
  8. func main() {
  9. // 从字符串创建一个reader对象
  10. reader := strings.NewReader("mage jiaoyue 2021 golang")
  11. // new一个4字节的读取缓冲区
  12. p := make([]byte, 4)
  13. for {
  14. // reader对象怎么读数据呢
  15. n, err := reader.Read(p)
  16. if err != nil {
  17. if err == io.EOF {
  18. log.Printf("[数据已读完 Eof:%d]", n)
  19. break
  20. }
  21. log.Printf("[未知错误:%v]", err)
  22. return
  23. }
  24. log.Printf("[打印读取的字节数:%d 内容:%s]", n, string(p[:n]))
  25. }
  26. }

自己实现一个Reader

  • 要求过滤输入字符串中的非字母字符
    • 输入 “mage jiaoyue 2021 go !!!!”
    • 输出是”magejiaoyuego”
  1. package main
  2. import (
  3. "io"
  4. "log"
  5. )
  6. type zimuguolv struct {
  7. src string //输入的字符串
  8. cur int //当前读取的位置
  9. }
  10. func alpha(r byte) byte {
  11. // r在 A-Z 或者 a-z
  12. if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
  13. return r
  14. }
  15. return 0
  16. }
  17. func (z *zimuguolv) Read(p []byte) (int, error) {
  18. // 当前位置>=字符串的长度,说明已经读取到结尾了 返回EOF
  19. if z.cur >= len(z.src) {
  20. return 0, io.EOF
  21. }
  22. // 定义一个剩余还没读到的长度
  23. x := len(z.src) - z.cur
  24. // bound叫做本次读取长度
  25. // n代表本次遍历 bound的索引
  26. n, bound := 0, 0
  27. if x >= len(p) {
  28. // 剩余长度超过缓冲区大小,说明本次可以完全填满换冲区
  29. bound = len(p)
  30. } else {
  31. // 剩余长度小于缓冲区大小,使用剩余长度输出,缓冲区填不满
  32. bound = x
  33. }
  34. buf := make([]byte, bound)
  35. for n < bound {
  36. if char := alpha(z.src[z.cur]); char != 0 {
  37. buf[n] = char
  38. }
  39. // n++索引++
  40. n++
  41. z.cur++
  42. }
  43. copy(p, buf)
  44. return n, nil
  45. }
  46. func main() {
  47. zmreader := zimuguolv{
  48. src: "mage jiaoyue 2021 g0 !!!",
  49. }
  50. p := make([]byte, 4)
  51. for {
  52. n, err := zmreader.Read(p)
  53. if err == io.EOF {
  54. log.Printf("[eof错误]")
  55. break
  56. }
  57. log.Printf("[读取到的长度%d 内容%s]", n, string(p[:n]))
  58. }
  59. }

组合多个 Reader,目的是重用和屏蔽下层实现的复杂度

  • 标准库里面已经有了很多Reader
  • 使用一个Reader A作为一个Reader B的一部分
  • 目的是复用逻辑,流式处理
  • 复用的io.Reader
  1. package main
  2. import (
  3. "io"
  4. "log"
  5. "strings"
  6. )
  7. type alphaReader struct {
  8. ioReader io.Reader
  9. }
  10. func (a *alphaReader) Read(p []byte) (int, error) {
  11. //想复用io.reader的read方法
  12. n, err := a.ioReader.Read(p)
  13. if err != nil {
  14. return n, err
  15. }
  16. buf := make([]byte, n)
  17. for i := 0; i < n; i++ {
  18. if char := alpha(p[i]); char != 0 {
  19. buf[i] = char
  20. }
  21. }
  22. copy(p, buf)
  23. return n, nil
  24. }
  25. func alpha(r byte) byte {
  26. // r在 A-Z 或者 a-z
  27. if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
  28. return r
  29. }
  30. return 0
  31. }
  32. func main() {
  33. myReader := alphaReader{
  34. strings.NewReader("mage jiaoyu 2021 go !!!"),
  35. }
  36. p := make([]byte, 4)
  37. for {
  38. n, err := myReader.Read(p)
  39. if err == io.EOF {
  40. log.Printf("[EOF错误]")
  41. break
  42. }
  43. log.Printf("[读取到的长度%d 内容%s]", n, string(p[:n]))
  44. }
  45. }

os.File 结合

  • 以下代码展示了 alphaReader 如何与 os.File 结合以过滤掉文件中的非字母字符:
  • 因为os.Open得到一个file对象 ,它实现了io.Reader的Read方法
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "os"
  7. )
  8. type alphaReader struct {
  9. ioReader io.Reader
  10. }
  11. func (a *alphaReader) Read(p []byte) (int, error) {
  12. //想复用io.reader的read方法
  13. n, err := a.ioReader.Read(p)
  14. if err != nil {
  15. return n, err
  16. }
  17. buf := make([]byte, n)
  18. for i := 0; i < n; i++ {
  19. if char := alpha(p[i]); char != 0 {
  20. buf[i] = char
  21. }
  22. }
  23. copy(p, buf)
  24. return n, nil
  25. }
  26. func alpha(r byte) byte {
  27. // r在 A-Z 或者 a-z
  28. if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
  29. return r
  30. }
  31. return 0
  32. }
  33. func main() {
  34. file, err := os.Open("test.txt")
  35. if err != nil {
  36. fmt.Println(err)
  37. os.Exit(1)
  38. }
  39. defer file.Close()
  40. //myReader := alphaReader{
  41. // strings.NewReader("mage jiaoyu 2021 go !!!"),
  42. //}
  43. myReader := alphaReader{
  44. file,
  45. }
  46. p := make([]byte, 4)
  47. for {
  48. n, err := myReader.Read(p)
  49. if err == io.EOF {
  50. log.Printf("[EOF错误]")
  51. break
  52. }
  53. log.Printf("[读取到的长度%d 内容%s]", n, string(p[:n]))
  54. }
  55. }

Writer

  • io.Writer 表示一个编写器,它从缓冲区读取数据,并将数据写入目标资源。
    IO操作 - 图1
  1. type Writer interface {
  2. Write(p []byte) (n int, err error)
  3. }
  • Write() 方法有两个返回值,一个是写入到目标资源的字节数,一个是发生错误时的错误。

closer

bytes.Buffer库

  • bytes.Buffer 的针对的是内存到内存的缓存

ioutil库 工具包

  • 在io目录下,它是一个工具包,实现一些实用的工具

readFile实例

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "log"
  6. )
  7. func main() {
  8. fileName := "go.mod"
  9. bytes, err := ioutil.ReadFile(fileName)
  10. if err != nil {
  11. fmt.Println(err)
  12. return
  13. }
  14. log.Printf("[内容:%s]", bytes)
  15. }

writeFile 写入文件

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. )
  6. func main() {
  7. fileName := "白日梦.txt"
  8. err := ioutil.WriteFile(fileName, []byte("升职加薪\n迎娶白富美"), 0644)
  9. fmt.Println(err)
  10. }

readDir 读取目录下的文件元信息

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. )
  6. func main() {
  7. fs, err := ioutil.ReadDir("./")
  8. if err != nil {
  9. fmt.Println(err)
  10. return
  11. }
  12. for _, f := range fs {
  13. fmt.Printf("[name:%v][size:%v][isDir:%v][mode:%v][ModTime:%v]\n",
  14. f.Name(),
  15. f.Size(),
  16. f.IsDir(),
  17. f.Mode(),
  18. f.ModTime(),
  19. )
  20. }
  21. /*
  22. [name:.idea][size:0][isDir:true][mode:drwxrwxrwx][ModTime:2021-07-10 11:55:50.3698648 +0800 CST]
  23. [name:01_错误处理.md][size:2286][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 10:39:06.3537141 +0800 CST]
  24. [name:02_IO操作.md][size:7565][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 12:00:13.7431769 +0800 CST]
  25. [name:03_反射.md][size:559][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-09 21:01:18.791 +0800 CST]
  26. [name:04_包和工程.md][size:3417][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-09 21:01:18.803 +0800 CST]
  27. [name:05_单元测试和基准测试.md][size:1211][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-09 21:01:18.787 +0800 CST]
  28. [name:day05_作业_set.md][size:3945][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 09:28:34.6737994 +0800 CST]
  29. [name:day05_作业_增量常驻任务管理.md][size:4846][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 09:59:11.7201007 +0800 CST]
  30. [name:go.mod][size:71][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 09:25:48.2006074 +0800 CST]
  31. [name:go.sum][size:179][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 09:25:48.2016588 +0800 CST]
  32. [name:pic][size:0][isDir:true][mode:drwxrwxrwx][ModTime:2021-07-10 08:37:34.9023974 +0800 CST]
  33. [name:reader.go][size:313][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 12:00:13.7391923 +0800 CST]
  34. [name:test.txt][size:35][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 11:49:07.0627814 +0800 CST]
  35. [name:day05作业.md][size:640][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 08:43:56.913919 +0800 CST]
  36. [name:大纲.md][size:122][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 09:01:28.648695 +0800 CST]
  37. [name:白日梦.txt][size:28][isDir:false][mode:-rw-rw-rw-][ModTime:2021-07-10 11:57:30.3136522 +0800 CST]
  38. */
  39. }

os库 操作系统打交道

os.create

  • 创建得到的file对象可以用 write和writestring写内容
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. file, err := os.Create("a.txt")
  8. if err != nil {
  9. fmt.Println(err)
  10. return
  11. }
  12. defer file.Close()
  13. for i := 0; i < 5; i++ {
  14. file.WriteString("writeString写进来的\n")
  15. file.Write([]byte("write写进来的\n"))
  16. }
  17. }

os其他常用函数

  • 常见的函数
  1. package main
  2. import (
  3. "log"
  4. "os"
  5. )
  6. func main() {
  7. log.Printf("[获取命令行参数][res:%v]", os.Args)
  8. hn, _ := os.Hostname()
  9. log.Printf("[获取主机名][res:%v]", hn)
  10. log.Printf("[获取当前进程名][res:%v]", os.Getpid())
  11. log.Printf("[获取一条环境变量][res:%v]", os.Getenv("GOROOT"))
  12. // 获取所有环境变量
  13. env := os.Environ()
  14. for _, v := range env {
  15. log.Printf("[获取所有环境变量][res:%v]", v)
  16. }
  17. dir, _ := os.Getwd()
  18. log.Printf("[获取当前目录][res:%v]", dir)
  19. _ = os.Mkdir("config", 0755)
  20. log.Printf("[创建单一目录config目录]")
  21. // mkdir -p
  22. os.MkdirAll("config1/yaml/local", 0755)
  23. log.Printf("[递归创建目录config1目录]")
  24. // rm dir
  25. err := os.Remove("config")
  26. log.Printf("[删除单一目录config1目录][err:%v]", err)
  27. // rm -rf
  28. //err = os.RemoveAll("config1")
  29. //log.Printf("[全部删除目录config1目录][err:%v]", err)
  30. }

读取文件 ioutil.ReadFile vs bufio

  1. 2021/07/10 14:29:03 [方法一 ioutil.ReadFile][res:我梦到我发财了
  2. 我醒了,啥都没了]
  3. 2021/07/10 14:29:03 [方法二 os.Open+ioutil.ReadAll][res:我梦到我发财了
  4. 我醒了,啥都没了]
  5. 2021/07/10 14:29:03 [方法三 os.Open+file.Read][res:我梦到我发财了
  6. 我醒了,啥都没了]
  7. 2021/07/10 14:29:03 [方法四 os.Open+bufio.Read][res:我梦到我发财了
  8. 我醒了,啥都没了]
  • 代码
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "os"
  8. )
  9. func main() {
  10. fileName := "a.txt"
  11. //方式一
  12. bytes, err := ioutil.ReadFile(fileName)
  13. if err != nil {
  14. return
  15. }
  16. log.Printf("[方法一 ioutil.ReadFile][res:%v]", string(bytes))
  17. //
  18. file, err := os.Open(fileName)
  19. if err != nil {
  20. return
  21. }
  22. bytes, err = ioutil.ReadAll(file)
  23. if err != nil {
  24. return
  25. }
  26. log.Printf("[方法二 os.Open+ioutil.ReadAll][res:%v]", string(bytes))
  27. file.Close()
  28. file, _ = os.Open(fileName)
  29. buf := make([]byte, 50)
  30. _, err = file.Read(buf)
  31. if err != nil {
  32. fmt.Println(err)
  33. return
  34. }
  35. log.Printf("[方法三 os.Open+file.Read][res:%v]", string(bytes))
  36. file.Close()
  37. file, _ = os.Open(fileName)
  38. // bufio.NewReader
  39. rd := bufio.NewReader(file)
  40. buf1 := make([]byte, 50)
  41. _, err = rd.Read(buf1)
  42. if err != nil {
  43. return
  44. }
  45. log.Printf("[方法四 os.Open+bufio.Read][res:%v]", string(bytes))
  46. file.Close()
  47. }
  • 上述都提供了文件读写的能力
  • bufio多了一层缓存的能力。优势体现在读取大文件的时候
  • ioutil.ReadFile是一次性将内容加载到内存,大文件容易爆掉

标准输出,标准输入

  • os.StdOut.Write 代替fmt.print
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. os.Stdout.Write([]byte("小乙和李逵"))
  8. fmt.Println("小乙和李逵")
  9. }

os.stdin 作为脚本的输入内容

  • 写一个脚本,和命令一个输入的文件
  • 文件作为脚本的stdin,执行
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. )
  7. func main() {
  8. /*
  9. echo "ss -ntlp " > a.txt
  10. go run a.go < a.txt
  11. */
  12. cmd := exec.Command("sh")
  13. cmd.Stdin = os.Stdin
  14. cmd.Stdout = os.Stdout
  15. cmd.Stderr = os.Stderr
  16. err := cmd.Run()
  17. if err != nil {
  18. fmt.Println("run.err", err)
  19. return
  20. }
  21. }