前言
本文主要介绍了Go语言中文件读写的相关操作。
文件是什么?
计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件。
Go中所有跟文件相关操作都使用OS这个模块来实现。
文件读取
os.Open(path)函数能够打开一个文件返回1个文件句柄,从而实现文件读取相关功能。
返回一个*File(文件类型的指针)和一个err(错误)。
对得到的文件实例调用close()方法对文件进行关闭。
在golang中读取文件有3种方式

func (f *File) Read(b []byte) (n int, err error) {.....}
encounter: v 遭遇/遇到 n:邂逅
它接收一个字节切片,返回 读取的字节数 和可能的具体错误,读到文件末尾时会返回0和io.EOF
package main
import (
"fmt"
"io"
"os"
)
func main() {
//1.打开文件
fileObj, err := os.Open("./main.go")
if err != nil {
fmt.Printf("Open file faild %v", err)
return
}
//返回1个指针类型文件句柄&{0xc000074780}
fmt.Println(fileObj)
//最后不要忘记 关闭文件
defer fileObj.Close()
//2.读取文件:每次读取长度控制在128个字节
var tmp [128]byte
for {
//每次读给Read传1个长度为tmp[:]的口袋,存每次读取的数据。
n, err := fileObj.Read(tmp[:])
//io.EOF
if err == io.EOF {
fmt.Println("文件读完了")
return
}
//其他错误
if err != nil {
fmt.Printf("Open file faild %v", err)
}
fmt.Printf("读了%d个字节
", n)
//把每次读完得128个字节强转为字符串,输出
fmt.Println(string(tmp[:n]))
//每次读128个字节,最后1次读,肯定不是128所以意味着读完了!退出
if n < 128 {
return
}
}
}
2.bufio读取文件
bufio是在file文件对象之上封装了一层API,支持更多的功能。
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
fileObj, err := os.Open("./main.go")
if err != nil {
fmt.Printf("Open file faild error:%s", err)
return
}
defer fileObj.Close()
reader := bufio.NewReader(fileObj)
for {
line, err := reader.ReadString('
')
//文件末尾 读完了
if err == io.EOF {
fmt.Printf("Sorry the end of file error:%s", err)
return
}
//读的过程中出错了
if err != nil {
fmt.Printf("read file fild error:%s", err)
return
}
fmt.Print(line)
}
}
bufio.NewReade( )既可以接收fileObj,也可以获取os.Stdin屏幕输入。
bufio.NewReader() 参数为接口类型
只要实现了Read()方法,都可以被传进去!
package main
import (
"bufio"
"fmt"
"os"
)
//获取用户输入时如果有空格
func userScan() {
var s string
fmt.Print("请输入内容:")
fmt.Scanln(&s)
fmt.Printf("你输入的是%s
", s)
}
func useBufio() {
var s1 string
/*
使用bufio及可以接收 fileObj、也可以获取os.Stdin屏幕输入
说明:bufio.NewReader(参数)为接口类型
只要实现了Read()方法,都可以被传进去!
*/
reader := bufio.NewReader(os.Stdin)
fmt.Print("请输入内容:")
s1, _ = reader.ReadString('
')
fmt.Printf("你输入的内容:%s
", s1)
}
func main() {
// userScan()
useBufio()
}
3.ioutill打开文件
package main
import (
"fmt"
"io/ioutil"
)
func main() {
ret, err := ioutil.ReadFile("./main.go")
if err != nil {
fmt.Printf("Read file faild,erro:%s", err)
return
}
fmt.Println(string(ret))
}
Linux文件权限设计思想启示
package main
import "fmt"
/*
linux文件系统使用二进制位 代表 1个权限,因为二进制运算和存储都很快。
分别使用3个位来表示以下3种权限
100 读
010 写
001 执行
*/
/*Linux文件权限为什么使用 4、2、1代表 读、写、执行权限呢?
因为4、2、1这3个数字中任意2个十进制数字相加的的结果,正好=它们转换成2进制向与(|)的结果。
*/
const (
read int = 4 //100
write int = 2 //010
execute int = 1 //001
//拥有读执行r+x权限 5 101
//拥有读写r+w权限 6 110
//拥有所有r+w+x权限 7 111
//又有写和执w+x权限 3 011
)
/*所以在Linux文件权限中:
7----->满权角色
5----->角色1
3----->角色2
............
*/
//Linux用户 就是 user
func judgeRole(per int) {
fmt.Printf("用户眼中的权限 10进制:%d linux眼中的权限:%b
", per, per)
}
func main() {
judgeRole(read | write) //6----110
judgeRole(read | write | execute) //7----111
}
根据Linux系统这种文件权限设计思想,也可以做自己web开发中RBAC权限。
文件写入操作
os.OpenFile()函数能够以指定模式打开1个文件,返回1个文件句柄,从而实现文件写入相关功能。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
...
}
name:要打开的文件名 flag:打开文件的模式。 模式有以下几种:
| 模式 | 含义 |
|---|---|
os.O_WRONLY |
只写 |
os.O_CREATE |
创建文件 |
os.O_RDONLY |
只读 |
os.O_RDWR |
读写 |
os.O_TRUNC |
清空 |
os.O_APPEND |
追加 |
perm:文件权限,一个八进制数。r(读)04,w(写)02,x(执行)01。
Write和WriteString
package main
import (
"fmt"
"os"
)
func main() {
//打开文件写内容
fileObj, err := os.OpenFile("./xx.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 777)
defer fileObj.Close()
if err != nil {
fmt.Printf("Open file faild %s", err)
return
}
//Write
fileObj.Write([]byte("Hello world.
"))
//WriteString
fileObj.WriteString("您好世界。
")
}
bufio.NewWriter
bufio是先把文件内容写到缓冲区中,然后flush到硬盘。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//打开文件写内容
fileObj, err := os.OpenFile("./xx.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 777)
defer fileObj.Close()
if err != nil {
fmt.Printf("Open file faild %s", err)
return
}
//bufio.NewWriter
writer := bufio.NewWriter(fileObj)
for i := 0; i < 10; i++ {
writer.WriteString("Hello China
")
}
writer.Flush()
}
ioutil.WriteFile
package main
import (
"fmt"
"io/ioutil"
)
func main() {
str := "Los Angeles"
err := ioutil.WriteFile("./xx.txt", []byte(str), 777)
if err != nil {
fmt.Println("Write file faild ", err)
return
}
}
package main
import (
"os"
)
func main() {
// var ret [10]byte
fileObj, _ := os.OpenFile("./main.go", os.O_WRONLY|os.O_APPEND, 777)
//seek:offset偏移多少:9 whence 从哪里偏移?
fileObj.Seek(13, 1)
// //插入内容
fileObj.Write([]byte{'/', '/'})
fileObj.WriteString("写点注释啊!
")
defer fileObj.Close()
}
文件中插入内容
package main
import (
"fmt"
"io"
"os"
"strings"
)
func newSeek(offset int64) int64 {
fileObj, _ := os.Open("./main.go")
//seek:offset偏移多少:9 whence 从哪里偏移?
newSeek, _ := fileObj.Seek(offset, 1)
// //插入内容
defer fileObj.Close()
return newSeek
}
type metadata struct {
name string
start int
end int
step int
appendFlag int
}
func appendFile(name string, content string) int64 {
fileObj, _ := os.OpenFile(name, os.O_APPEND, 777)
fileObj.WriteString(content)
//获取当前
curOffset, _ := fileObj.Seek(0, os.SEEK_CUR)
defer fileObj.Close()
return curOffset
}
func restructFile(order metadata) string {
// var ret string
//中间文件的名称
s1 := strings.Split(order.name, "/")
s1[len(s1)-1] = fmt.Sprintf("tmp_%s", s1[len(s1)-1])
tmpName := strings.Join(s1, "/")
newFileObj, _ := os.OpenFile(tmpName, os.O_CREATE|os.O_WRONLY|order.appendFlag, 777)
fileObj, err := os.Open(order.name)
if err != nil {
fmt.Println("打开文件时错误")
}
//没有指定结束边界就以文件最后位置
if order.end == -1 {
fi, err := fileObj.Stat()
if err != nil {
// Could not obtain stat, handle error
}
order.end = int(fi.Size())
}
//创建指定长度的数组
var bag = make([]byte, order.step, order.step)
//从指定位置开始
fileObj.Seek(int64(order.start), 0)
//计算需要获取的量
total := order.end - order.start
//开始读文件
for i := 0; i < total; i += order.step {
var currentPostion = order.start + i
var left = order.end - currentPostion
//最后1次不能被整除的情况
if left < order.step {
bag = bag[:left+1]
}
bag = bag[:]
n, err := fileObj.Read(bag)
if err == io.EOF {
fmt.Println("文件读完了")
break
}
if err != nil {
fmt.Printf("读取文件时错误%v", err)
break
}
if n == 0 {
break
}
newFileObj.WriteString(string(bag[:n]))
// ret += string(bag[:n])
}
defer newFileObj.Close()
defer fileObj.Close()
return tmpName
}
func main() {
var struct1 = metadata{
name: "./test.txt",
start: 0, //从文件的多少字节之后开始
end: 16, //读取到多少个字节后结束
step: 3, //一次读取多少字节
appendFlag: os.O_TRUNC,
}
tmpFile := restructFile(struct1)
curOffset := appendFile(tmpFile,
`
吾令羲和弭节兮,望崦嵫而勿迫。
路漫漫其修远兮,吾将上下而求索。
`)
struct1.start = int(curOffset)
struct1.end = -1
struct1.appendFlag = os.O_APPEND
restructFile(struct1)
os.Remove(struct1.name)
os.Rename("./tmp_test.txt", struct1.name)
}
日志库
用户可以指定日志级别:debug、infro、error
支持用户格式化输出日志内容到stdout
支持用户格式化输出日志内容写入到文件,且可以指定日志文件切割时间。
github地址