Go 语言提供了很多文件操作的支持,在不同场景下,有对应的处理方式,今天就来系统地梳理一下,几种常用的文件读写的形式。

一、读取文件内容

1、按字节读取文件

这种方式是以字节为单位来读取,相对底层一些,代码量也较大,我们看下面代码:

// read-bytes.go

package main

import (
"fmt"
"io"
"os"
) func main() {
file, _ := os.Open("test.txt") defer file.Close() // 字节切片缓存 存放每次读取的字节
buf := make([]byte, 1024) // 该字节切片用于存放文件所有字节
var bytes []byte for {
// 返回本次读取的字节数
count, err := file.Read(buf) // 检测是否到了文件末尾
if err == io.EOF {
break;
} // 取出本次读取的数据
currBytes := buf[:count] // 将读取到的数据 追加到字节切片中
bytes = append(bytes, currBytes...)
} // 将字节切片转为字符串 最后打印出来文件内容
fmt.Println(string(bytes))
}

2、结合 ioutil 来读取

如果我们不想那么麻烦,可以结合 ioutil 来精简上面的代码:

// read-with-util.go

package main

import (
"fmt"
"io/ioutil"
"os"
) func main() {
file, _ := os.Open("test.txt") defer file.Close() // ReadAll接收一个io.Reader的参数 返回字节切片
bytes, _ := ioutil.ReadAll(file) fmt.Println(string(bytes))
}

由于 os.File 也是 io.Reader 的实现,我们可以调用 ioutil.ReadAll(io.Reader) 方法,将文件所有字节读取出来,省去了使用字节缓存循环读取的过程。

3、仅使用 ioutil 包来完成读取操作:

为了进一步简化文件读取操作,ioutil 还提供了 ioutil.ReadFile(filename string) 方法,一行代码搞定读取任务:

// read-by-util.go

package main

import (
"fmt"
"io/ioutil"
) func main() {
bytes, _ := ioutil.ReadFile("test.txt") fmt.Println(string(bytes))
}

4、逐行读取:

有时候为了便于分析处理,我们希望能够逐行读取文件内容,这个时候可以 Scanner 来完成:

// read-line-by-line.go

package main

import (
"bufio"
"fmt"
"os"
) func main() {
file, _ := os.Open("test.txt") defer file.Close() // 接受io.Reader类型参数 返回一个bufio.Scanner实例
scanner := bufio.NewScanner(file) var count int for scanner.Scan() {
count++ // 读取当前行内容
line := scanner.Text() fmt.Printf("%d %s\n", count, line)
}
}

这简直就是 java.util.Scanner 翻版嘛,并且 Go 语言中的 Scanner 也可以接收不同的输入源,比如 os.Stdin 等。

上面代码直接打印出了每一行的数据,如果大家想得到最终文件的内容,可以创建一个字符串切片,每次逐行扫描时,将当前行内容追加到切片中即可。

以上就是几种常用的文件读取方式,当然还有其他更高级的方式,有机会再做总结。

二、写入文件操作

1、使用 ioutil 完成写入操作

上面我们介绍了 ioutil.ReadFile(filename string),与之对应地有 ioutil.WriteFile(filename string, ...) 方法,可以轻松完成写入操作:

// write-by-util.go

package main

import (
"io/ioutil"
) func main() {
data := []byte("hello goo\n") // 覆盖式写入
ioutil.WriteFile("test.txt", data, 0664)
}

我们看到,WriteFile() 方法需要传入三个参数,它的完整签名是:ioutil.WriteFile(filename string, data []byte, perm os.FileMode)。如果文件不存在,则会根据指定的权限创建文件,如果存在,则会先清空文件原有内容,然后再写入新数据。

需要注意最后一个参数,它是一个无符号 32 位整数,表示当前文件的权限,也是标准的 Unix 文件权限格式。

Unix 使用 -rwxrwxrwx 这样的形式来表示文件权限,其中:

  • 第1位:文件属性,- 表示是普通文件,d 表示是一个目录
  • 第2-4位:文件所有者的权限
  • 第5-7位:文件所属用户组的权限
  • 第8-10位:其他人的权限

在权限设置中:

  • r 表示 read,值为 4
  • w 表示 write,值为 2
  • x 表示 exec,值为 1

下面我们通过 os.FileMode 测试一下:

package main

import (
"fmt"
"os"
) func showMode(code int) {
fmt.Println(os.FileMode(code).String())
} func main() {
showMode(0777)
showMode(0766)
showMode(0764)
}

运行程序,控制台打印如下:

-rwxrwxrwx
-rwxrw-rw-
-rwxrw-r--

2、通过File句柄完成写入操作

上面我们曾使用过 os.Open(name string) 方法,这个方法是以只读方式打开文件的,os 包还提供了 os.OpenFile(name string, flag int, perm FileMode) 方法,通过指定额外的 读写方式文件权限 参数,使文件操作变得更为灵活。

其中,flag 有以下几种常用的值:

  • os.O_CREATE: create if none exists 不存在则创建
  • os.O_RDONLY: read-only 只读
  • os.O_WRONLY: write-only 只写
  • os.O_RDWR: read-write 可读可写
  • os.O_TRUNC: truncate when opened 文件长度截为0:即清空文件
  • os.O_APPEND: append 追加新数据到文件

在打开文件之后,我们可以通过 Write() 和 WriteString() 方法写入数据,最后通过 Sync() 方法将数据持久化到磁盘:

// write-by-file-descriptor.go

package main

import (
"fmt"
"os"
) // 打印写入的字节数
func printWroteBytes(count int) {
fmt.Printf("wrote %d bytes\n", count)
} func main() {
// 以指定的权限打开文件
file, _ := os.OpenFile("test2.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0664) defer file.Close() data := []byte("hello go\n") // 写入字节
count, _ := file.Write(data) printWroteBytes(count) // 写入字符串
count, _ = file.WriteString("hello world\n") printWroteBytes(count) // 确保写入到磁盘
file.Sync()
}

3、通过bufio包完成写入操作

这种方式其实是在 File 句柄上做了一层封装,调用方式和上面直接写入非常相似,大家仅做个参考:

// write-with-bufio.go

package main

import (
"bufio"
"fmt"
"os"
) func printWroteBytes(count int) {
fmt.Printf("wrote %d bytes\n", count)
} func main() {
file, _ := os.OpenFile("test.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0664) defer file.Close() // 获取bufio.Writer实例
writer := bufio.NewWriter(file) // 写入字符串
count, _ := writer.Write([]byte("hello go\n")) fmt.Printf("wrote %d bytes\n", count) // 写入字符串
count, _ = writer.WriteString("hello world\n") fmt.Printf("wrote %d bytes\n", count) // 清空缓存 确保写入磁盘
writer.Flush()
}

以上就是常用的文件读写方式,今天就总结到这里吧,后续有机会再探讨更多内容。

最新文章

  1. 如何用php生成1-10之间的不重复随机数
  2. Jquery实现遮罩层,就是弹出DIV周围都灰色不能操作
  3. EF 实践
  4. Apache是目前应用最广的Web服务器,PHP3是一种类似ASP的脚本语言
  5. TClientDataSet 设计期 多次New 字段问题
  6. PVPGN 暗黑破坏神2 1.11b战网配置问题汇总
  7. [c++]程序的内存划分理解
  8. java学习之数组排序一:选择排序
  9. pyqt columnView例子学习
  10. iOS开发系列--打造自己的“美图秀秀”
  11. (转)JVM内存组成及分配
  12. window配置mongodb集群(副本集)
  13. kerberos环境storm配置:Running Apache Storm Securely
  14. spring【一】 学习
  15. Android 启动APP时黑屏白屏的解决方案
  16. oracle 10g函数大全--日期型函数
  17. K8S 基本操作
  18. MT【279】分母为根式的两个函数
  19. 字符串之StringBuffer 与 StringBuilder的对比
  20. spoj high

热门文章

  1. 2090. 「ZJOI2016」旅行者 分治,最短路
  2. B 题解————2019.10.16
  3. ZROI 暑期高端峰会 A班 Day5 杂题选讲
  4. Python语言基础考察点:python语言基础常见考题(一)
  5. SpringData JPA实现增删改查
  6. HTML5 - websocket的应用 之 简易聊天室
  7. BAT脚本入门
  8. JAVA锁的膨胀过程和优化(阿里)
  9. [Gamma]Scrum Meeting#5
  10. Python argparse 处理命令行小结