读文件

在日常开发中我们少不了对文件读取,今天我们就从三部分来讲解:全量读,带缓冲区读,任意位置读。

全量读

我们先来看看一个简单的例子:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. )
  6. func main() {
  7. dat, err := ioutil.ReadFile("./main.go")
  8. fmt.Println(err)
  9. fmt.Println(string(dat))
  10. }

运行程序可以打印整个 main.go 文件内容,如果我们将 ./main.go 修改为 ./main.go1,程序将出现 no such file or directory 的错误,所以在文件读取的时候一定要注意检查 err。

带缓冲区读

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. f, _ := os.Open("./main.go")
  8. defer f.Close()
  9. buf := make([]byte, 16)
  10. f.Read(buf)
  11. fmt.Println(string(buf))
  12. }

运行程序会输出 main.go 的前 16 个字节内容,具体为:

  1. package main
  2. im

任意位置读

有些时候我们想在一个文件特定地方读取特定长度的内容,那我们有什么方法可以使用呢?

第一种: f.Seek + f.Read

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. f, _ := os.Open("./main.go")
  8. defer f.Close()
  9. b1 := make([]byte, 2)
  10. f.Seek(5, 0)
  11. f.Read(b1)
  12. fmt.Println(string(b1))
  13. }

运行代码输出结果为:

  1. ge

第二种:使用 f.ReadAt

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. )
  6. func main() {
  7. f, _ := os.Open("./main.go")
  8. defer f.Close()
  9. b1 := make([]byte, 2)
  10. f.ReadAt(b1, 5)
  11. fmt.Println(string(b1))
  12. }

运行结果同样为:

  1. ge

但注意:

第一种方式是非并发安全的,例如:

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "time"
  6. )
  7. func main() {
  8. f, _ := os.Open("./main.go")
  9. defer f.Close()
  10. for i := 0; i < 5; i++ {
  11. go func() {
  12. b1 := make([]byte, 2)
  13. f.Seek(5, 0)
  14. f.Read(b1)
  15. fmt.Println(string(b1))
  16. f.Seek(2, 0)
  17. f.Read(b1)
  18. fmt.Println(string(b1))
  19. }()
  20. }
  21. time.Sleep(time.Second)
  22. }

输出结果为:

  1. ge
  2. ge
  3. ge
  4. ck
  5. ck
  6. ai
  7. ck
  8. m
  9. ck
  10. ck

第二种 f.ReadAt 是并发安全的,例如:

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "time"
  6. )
  7. func main() {
  8. f, _ := os.Open("./main.go")
  9. defer f.Close()
  10. for i := 0; i < 5; i++ {
  11. go func() {
  12. b1 := make([]byte, 2)
  13. f.ReadAt(b1, 5)
  14. fmt.Println(string(b1))
  15. f.ReadAt(b1, 2)
  16. fmt.Println(string(b1))
  17. }()
  18. }
  19. time.Sleep(time.Second)
  20. }

输出结果为:

  1. ge
  2. ge
  3. ck
  4. ck
  5. ge
  6. ge
  7. ck
  8. ck
  9. ge
  10. ck

项目实战

实战项目一: 如何使用 buf 实现 ioutil.ReadFile 类似效果:

  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. )
  7. func main() {
  8. f, _ := os.Open("./main.go")
  9. defer f.Close()
  10. content := make([]byte, 0)
  11. buf := make([]byte, 16)
  12. for {
  13. n, err := f.Read(buf)
  14. if err == io.EOF {
  15. break
  16. }
  17. if n == 16 {
  18. content = append(content, buf...)
  19. } else {
  20. content = append(content, buf[0:n]...)
  21. }
  22. }
  23. fmt.Println(string(content))
  24. }

实战项目二: 使用 bufio 实现行统计:

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. func main() {
  9. f, _ := os.Open("./main.go")
  10. defer f.Close()
  11. br := bufio.NewReader(f)
  12. totalLine := 0
  13. for {
  14. _, isPrefix, err := br.ReadLine()
  15. if !isPrefix {
  16. totalLine += 1
  17. }
  18. if err == io.EOF {
  19. break
  20. }
  21. }
  22. fmt.Println("total lines is:", totalLine)
  23. }

运行结果为:

  1. total lines is: 31