zoukankan      html  css  js  c++  java
  • Golang文件操作

    前言

     本文主要介绍了Go语言中文件读写的相关操作。

    文件是什么?

    计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件。

     Go中所有跟文件相关操作都使用OS这个模块来实现。

    文件读取

    os.Open(path)函数能够打开一个文件返回1个文件句柄,从而实现文件读取相关功能。   

    返回一个*File(文件类型的指针)和一个err(错误)

    对得到的文件实例调用close()方法对文件进行关闭。

    在golang中读取文件有3种方式

    1.文件句柄.Read()
     
    func (f *File) Read(b []byte) (n int, err error) {.....}
    

    encounter: v 遭遇/遇到 n:邂逅

    接收一个字节切片,返回 读取的字节数 可能的具体错误,读到文件末尾时会返回0io.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.NewReader()源码启示

    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文件权限设计思想启示

    Linux文件权限为什么使用 4、2、1代表 读、写、执行权限?
    因为4/2/1这3个数字中任意2个十进制数字相加的的结果正好=它们转换成2进制相与(|)的结果 
    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
    	}
    }
     
    插入内容
    fileObj.Seek(set int64, whence int)
    Seek sets the offset for the next Read or Write on file to offset, interpreted   给要偏移的文件设置1个读写文件的开端。
    according to whence:               whence参数
    0: means relative to the origin of the file,                  文件源头
    1: means  relative to the current offset, and               当前
    2 means relative to the end.                                          结束
    It returns the new offset and an error, if any. The behavior of Seek on a file opened with O_APPEND is not specified.
    如果有个有 新的offset开端或者错误它会返回。以O_APPEND模式打开文件,seek的将会不准确。
     
    插入覆盖
    使用seek是偏移到文件的某个位置,然后开始写,但是覆盖了原来的内容
    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地址

    参考

  • 相关阅读:
    【POJ 2259】Team Queue【队列】
    【POJ 2259】Team Queue【队列】
    【HDU 4699】Editor【栈】
    【HDU 4699】Editor【栈】
    【HDU 4699】Editor【栈】
    【POJ 2559】Largest Rectangle in a Histogram【栈】
    数据结构实验之栈八:栈的基本操作
    数据结构实验之栈八:栈的基本操作
    数据结构实验之栈七:出栈序列判定
    数据结构实验之栈七:出栈序列判定
  • 原文地址:https://www.cnblogs.com/sss4/p/12697893.html
Copyright © 2011-2022 走看看