zoukankan      html  css  js  c++  java
  • go 文件与目录操作

    文件打开与关闭

    文件打开

    原始的文件打开函数:

    func OpenFile(name string, flag int, perm FileMode) (*File, error)

    name:绝对路径或相对路径(相对于进程当前工作目录)
    flag:指定文件的访问模式,在os中这些参数被定义为常量

    const (
    O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
    O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
    O_RDWR int = syscall.O_RDWR // 读写模式打开文件
    O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
    O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
    O_EXCL int = syscall.O_EXCL // 和 O_CREATE 配合使用,文件必须不存在
    O_SYNC int = syscall.O_SYNC // 打开文件用于同步 I/O
    O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
    )
    flag参数

    其中,O_RDONLY、O_WRONLY、O_RDWR 应该只指定一个,剩下的通过 | 操作符来指定。该函数内部会给 flags 加上 syscall.O_CLOEXEC,在 fork 子进程时会关闭通过 OpenFile 打开的文件,即子进程不会重用该文件描述符。

    perm:指定了文件的模式和权限位,类型是 os.FileMode,文件模式位常量定义在 os

    const (
    // 单字符是被 String 方法用于格式化的属性缩写。
    ModeDir FileMode = 1 << (32 - 1 - iota) // d: 目录
    ModeAppend // a: 只能写入,且只能写入到末尾
    ModeExclusive // l: 用于执行
    ModeTemporary // T: 临时文件(非备份文件)
    ModeSymlink // L: 符号链接(不是快捷方式文件)
    ModeDevice // D: 设备
    ModeNamedPipe // p: 命名管道(FIFO)
    ModeSocket // S: Unix 域 socket
    ModeSetuid // u: 表示文件具有其创建者用户 id 权限
    ModeSetgid // g: 表示文件具有其创建者组 id 的权限
    ModeCharDevice // c: 字符设备,需已设置 ModeDevice
    ModeSticky // t: 只有 root/ 创建者能删除 / 移动文件
    
    // 覆盖所有类型位(用于通过 & 获取类型位),对普通文件,所有这些位都不应被设置
    ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice
    ModePerm FileMode = 0777 // 覆盖所有 Unix 权限位(用于通过 & 获取类型位)
    )
    perm参数

    以上常量在所有操作系统都有相同的含义(可用时),因此文件的信息可以在不同的操作系统之间安全的移植。不是所有的位都能用于所有的系统,唯一共有的是用于表示目录的 ModeDir 位。

    以上这些被定义的位是 FileMode 最重要的位。另外 9 个位(权限位)为标准 Unix rwxrwxrwx 权限(所有人都可读、写、运行)。

    FileMode 还定义了几个方法,用于判断文件类型的 IsDir() 和 IsRegular(),用于获取权限的 Perm()。

    返回的 error,具体实现是 *os.PathError,它会记录具体操作、文件路径和错误原因。

    一般打开文件使用下面这两个函数:

    func Open(name string) (*File, error) {
        return OpenFile(name, O_RDONLY, 0)
    }
    
    func Create(name string) (*File, error) {
        return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
    }

    文件关闭

    close() 系统调用关闭一个打开的文件描述符,并将其释放回调用进程,供该进程继续使用。当进程终止时,将自动关闭其已打开的所有文件描述符。

    func (f *File) Close() error

    os.File.Close() 是对 close() 的封装。我们应该养成关闭不需要的文件的良好编程习惯。文件描述符是资源,Go 的 gc 是针对内存的,并不会自动回收资源,如果不关闭文件描述符,长期运行的服务可能会把文件描述符耗尽。

    所以,通常的写法如下:

    file, err := os.Open("/tmp/studygolang.txt")
    if err != nil {
        // 错误处理,一般会阻止程序往下执行
        return
    }
    defer file.Close()

    关于返回值 error

    以下两种情况会导致 Close 返回错误:

    1. 关闭一个未打开的文件;
    2. 两次关闭同一个文件;
    

    通常,我们不会去检查 Close 的错误。

    文件打开与关闭示例

    package main
    
     
    import (
        "bufio"
        "fmt"
        "io"
        "io/ioutil"
        "os"
    )
     
    func main00(){
        //打开文件与1关闭文件
        file,err :=os.Open("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt")
        if err == nil {
            fmt.Printf("文件打开成功")
            fmt.Println(file)
        } else{
            fmt.Printf("文件打开失败,err:",err)
            return
        }
        defer func(){
            file.Close()
            fmt.Printf("关闭文件")
        }()
        //
    }

    判断文件是否存在

     //使用os包状态检测结合os.IsNotExist(err)判断文件是否存在
        //获取指定文件的信息
        fileInfo,err := os.Stat("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt")
        if err != nil {
            fmt.Println("err=",err)
            //校验错误是否为【文件不存在错误】
            if os.IsNotExist(err){
                fmt.Println("文件不存在!")
            }
            return
        }else{
            fmt.Println("文件存在!")
            fmt.Println(fileInfo)
        }

    文件读取

    func (f *File) Read(b []byte) (n int, err error)

    Read 方法从 f 中读取最多 len(b) 字节数据并写入 b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取 0 个字节且返回值 err 为 io.EOF。

    从方法声明可以知道,File 实现了 io.Reader 接口。

    Read 对应的系统调用是 read。

    对比下 ReadAt 方法:

    func (f *File) ReadAt(b []byte, off int64) (n int, err error)

    ReadAt 从指定的位置(相对于文件开始位置)读取长度为 len(b) 个字节数据并写入 b。它返回读取的字节数和可能遇到的任何错误。当 n<len(b) 时,本方法总是会返回错误;如果是因为到达文件结尾,返回值 err 会是 io.EOF。它对应的系统调用是 pread。

    Read 和 ReadAt 的区别:前者从文件当前偏移量处读,且会改变文件当前的偏移量;而后者从 off 指定的位置开始读,且不会改变文件当前偏移量。

    func main01(){
        //以只读方式打开一个文件,创建其带缓冲的读取器,逐行读取到末尾
        //4=readable,2=writeable,1=executeable,6=4+2
        file,err :=os.OpenFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt",os.O_RDONLY,0666)
        //判断读入是否成功
        if err == nil {
            fmt.Printf("文件打开成功")
            //打印内容
            fmt.Println(file)
        } else{
            fmt.Printf("文件打开失败,err:",err)
            return
        }
        //延迟关闭文件:在函数return前执行的程序
        defer func(){
            file.Close()
            fmt.Printf("关闭文件")
        }()
        //创建文件的读取器
        Reader := bufio.NewReader(file)
        for{
            //一次读取一行
            data,err := Reader.ReadString('
    ')
            if err == nil{
                fmt.Println(data)
            }else{
                //到达文件结尾,跳出循环
                if err == io.EOF{
                    fmt.Println("已经文件结尾")
                    break
                }else{
                    //读取异常,打印异常并结束
                    fmt.Println("读取失败,err:",err)
                    return
                }
            }
        }
    }  

    文件写入

    func (f *File) Write(b []byte) (n int, err error)

    Write 向文件中写入 len(b) 字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值 n!=len(b),本方法会返回一个非 nil 的错误。

    从方法声明可以知道,File 实现了 io.Writer 接口。

    Write 对应的系统调用是 write。

    Write 与 WriteAt 的区别同 Read 与 ReadAt 的区别一样。为了方便,还提供了 WriteString 方法,它实际是对 Write 的封装。

    注意:Write 调用成功并不能保证数据已经写入磁盘,因为内核会缓存磁盘的 I/O 操作。如果希望立刻将数据写入磁盘(一般场景不建议这么做,因为会影响性能),有两种办法:

    1. 打开文件时指定 `os.O_SYNC`;
    2. 调用 `File.Sync()` 方法。
    说明:File.Sync() 底层调用的是 fsync 系统调用,这会将数据和元数据都刷到磁盘;如果只想刷数据到磁盘(比如,文件大小没变,只是变了文件数据),需要自己封装,调用 fdatasync 系统调用。(syscall.Fdatasync)

    func mian04(){
        //以【创写追加】或【创写覆盖】方式打开一个文件,缓冲写入几行数据,倒干缓冲区
        //打开文件,模式:不存在就创建+只写+追加,生成的文件的权限666
        file,err :=os.OpenFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt",os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666)
        //判断读入是否成功
        if err != nil {
            fmt.Printf("文件打开失败,err:",err)
            return
        }
        //延迟关闭文件:在函数return前执行的程序
        defer func(){
            file.Close()
            fmt.Printf("关闭文件")
        }()
        //分批写入数据
        writer := bufio.NewWriter(file)
        writer.WriteString("hello")
        writer.WriteString("2019")
        writer.WriteString("。。。。")
        //写入一个字符
        writer.WriteRune('你')
        writer.WriteRune('好')
        //写一位数据
        writer.WriteByte(123)
        //写一个字节数据,范围:0-255
        writer.Write([]byte{123,222,21})
        //把缓冲区清空,立即将最后的数据写入文件
        writer.Flush()
    }
    func main05(){
        //反引号代表保留原始排版的字符串
        data := `测试文档、1、2、3、4、5。`
        fmt.Printf("data type=%T,value=%v
    ",data,data)
        //将数据字符串转换为原始字节切片
        dataBytes := []byte(data)
        fmt.Printf("data type=%T,value=%v
    ",dataBytes,dataBytes)
        //向指定文件写入上面的字节数据
        err := ioutil.WriteFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt",dataBytes,0666)
        if err != nil {
            fmt.Println("写入错误")
        }else{
            fmt.Println("写入成功")
        }
    }

    文件拷贝

    //使用ioutil包做一个傻瓜式拷贝
    //使用io.Copy进行文件拷贝
    //使用缓冲1K的缓冲区配合缓冲读写器进行图片拷贝
    func main07(){
        //使用ioutil包做一个傻瓜式拷贝
        data,_:= ioutil.ReadFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt")
        err := ioutil.WriteFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test1.txt",data,0666)
        if err != nil{
            fmt.Println("拷贝失败",err)
        }else{
            fmt.Println("拷贝成功")
        }
        fmt.Println("执行完成")
    }
     
    func main08(){
        //使用io.Copy进行文件拷贝
     
        //打开拷贝源文件,模式为只读模式
        srcFile,err := os.OpenFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt",os.O_RDONLY,0666)
     
        //打开目标文件,模式为创建|写入
        dstFile,err := os.OpenFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test1.txt",os.O_WRONLY |os.O_CREATE,0666)
     
        //执行源文件到目标文件的拷贝,writen为拷贝字节数
        writen,err := io.Copy(dstFile,srcFile)
     
        if err == nil {
            fmt.Println("拷贝成功,字节数=",writen)
        }else{
            fmt.Println("拷贝失败,err=",err)
        }
    }
     
    func main09(){
        //使用缓冲1K的缓冲区配合缓冲读写器进行图片拷贝
     
        //打开源文件
        srcFile,_ := os.OpenFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test.txt",os.O_RDONLY,0666)
     
        //打开目标文件
        dstFile,_ := os.OpenFile("C:/Users/Administrator/go/src/awesomeProject/demo/test/test1.txt",os.O_WRONLY |os.O_CREATE,0666)
     
        defer func(){
            srcFile.Close()
            dstFile.Close()
            fmt.Println("文件全部关闭!")
        }()
        //创建源文件的缓冲读取器
        reder := bufio.NewReader(srcFile)
        //创建目标文件的缓冲写出器
        writer := bufio.NewWriter(dstFile)
     
        //创建缓冲区
        buffer := make([]byte,1024)
     
        //将数据存入,直到io.EOF
        for {
            //循环的读取数据到缓冲区,然后把缓冲区的数据写入到目标文件
            _,err :=reder.Read(buffer)
            if err !=nil{
                if err == io.EOF{
                    fmt.Println("源文件读取完毕!")
                    break
                }else{
                    fmt.Println("读取文件发生错误,err=",err)
                }
            }else{
                _,err := writer.Write(buffer)
                if err !=nil {
                    fmt.Println("写出错误,err=",err)
                    return
                }
            }
            }
    }

    改变文件偏移量:Seek

    对于每个打开的文件,系统内核会记录其文件偏移量,有时也将文件偏移量称为读写偏移量或指针。文件偏移量是指执行下一个 Read 或 Write 操作的文件其实位置,会以相对于文件头部起始点的文件当前位置来表示。文件第一个字节的偏移量为 0。

    文件打开时,会将文件偏移量设置为指向文件开始,以后每次 Read 或 Write 调用将自动对其进行调整,以指向已读或已写数据后的下一个字节。因此,连续的 Read 和 Write 调用将按顺序递进,对文件进行操作。

    而 Seek 可以调整文件偏移量。方法定义如下:

    func (f *File) Seek(offset int64, whence int) (ret int64, err error)

    Seek 设置下一次读 / 写的位置。offset 为相对偏移量,而 whence 决定相对位置:0 为相对文件开头,1 为相对当前位置,2 为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。使用中,whence 应该使用 os 包中的常量:SEEK_SETSEEK_CUR 和 SEEK_END

    注意:Seek 只是调整内核中与文件描述符相关的文件偏移量记录,并没有引起对任何物理设备的访问。

    一些 Seek 的使用例子(file 为打开的文件对象),注释说明了将文件偏移量移动到的具体位置:

    file.Seek(0, os.SEEK_SET)    // 文件开始处
    file.Seek(0, SEEK_END)        // 文件结尾处的下一个字节
    file.Seek(-1, SEEK_END)        // 文件最后一个字节
    file.Seek(-10, SEEK_CUR)     // 当前位置前 10 个字节
    file.Seek(1000, SEEK_END)    // 文件结尾处的下 1001 个字节

    最后一个例子在文件中会产生“空洞”。

    Seek 对应系统调用 lseek。该系统调用并不适用于所有类型,不允许将 lseek 应用于管道、FIFO、socket 或 终端。

    文件目录操作

    创建名称为name的目录,权限设置是perm,例如0777

    func Mkdir(name string, perm FileMode) error

    根据path创建多级子目录,例如astaxie/test1/test2。

     func MkdirAll(path string, perm FileMode) error

    删除名称为name的目录,当目录下有文件或者其他目录时会出错

    func Remove(name string) error

    根据path删除多级子目录,如果path是单个名称,那么该目录下的子目录全部删除。

    func RemoveAll(path string) error 

    更改文件名,Rename 修改一个文件的名字或移动一个文件。如果 newpath 已经存在,则替换它。

    func Rename(oldpath, newpath string) error
    

      

    例子:

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
        os.Mkdir("test", 0777)
        os.MkdirAll("test/test1/test2", 0777)
        err := os.Remove("test")
        if err != nil {
            fmt.Println(err)
        }
        os.RemoveAll("test")
    }
    

      

    笔记来源:

    https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter06/06.1.html

  • 相关阅读:
    http协议get方式获取图片
    listview全选,和反选的实现
    猜红桃A游戏
    java虚拟机的类加载器
    正则表达式基本用法
    echarts饼图配置模板
    spring----bean的使用
    mysql特殊查询----分组后排序
    java程序存入数据库中文乱码解决方案
    echarts功能配置实例----柱/折线、饼图
  • 原文地址:https://www.cnblogs.com/-wenli/p/12334844.html
Copyright © 2011-2022 走看看