区块链开发之Go语言—文件系统

Posted 林欣哲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区块链开发之Go语言—文件系统相关的知识,希望对你有一定的参考价值。

处理的文件名

  • path库

  • filepath库

查看文件的元信息

  • os.Stat

  • os.Lstat

操作临时文件区域

  • os.TempDir

os — 平台无关的操作系统功能实现

os 封装了系统无关的实现。在实际编程中,我们应该总是优先使用 os 中提供的功能,而不是 syscall。

文件 I/O

了解IO需要参照Unix文件系统的概念。

  • 在 Unix 系统调用中,所有执行 I/O 操作以文件描述符,一个非负整数(通常是小整数),来指代打开的文件。

  • 文件描述符用以表示所有类型的已打开文件,包括管道(pipe)、FIFO、socket、终端、设备和普通文件。

  • 在 Go 中,文件描述符封装在 os.File 结构中,通过 File.Fd() 可以获得底层的文件描述符:fd。

  • 3 种标准的文件描述符:

    • 0-标准输入;os.Stdin;对应Unix的/dev/stdin

    • 1-标准输出;os.Stdout;对应Unix的/dev/stdout

    • 2-标准错误;os.Stderr;对应Unix的/dev/stderr

打开一个文件:OpenFile

  • funcOpenFile(namestring,flagint,permFileMode)(*File,error)

    • 参数 name 指定,它可以是绝对路径或相对路径(相对于进程当前工作目录),也可以是一个符号链接(会对其进行解引用)。

    • 参数 flag 位掩码用于指定文件的访问模式,可用的值在 os 中定义为常量(以下值并非所有操作系统都可用)

 
   
   
 
  1.    const (

  2.    O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件

  3.    O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件

  4.    O_RDWR   int = syscall.O_RDWR   // 读写模式打开文件

  5.    O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部

  6.    O_CREATE int = syscall.O_CREAT  // 如果不存在将创建一个新文件

  7.    O_EXCL   int = syscall.O_EXCL   // 和O_CREATE配合使用,文件必须不存在

  8.    O_SYNC   int = syscall.O_SYNC   // 打开文件用于同步I/O

  9.    O_TRUNC  int = syscall.O_TRUNC  // 如果可能,打开时清空文件

  10.    )

-    参数 perm 指定了文件的模式和权限位     //尴尬,这行排了好久排不好版,微信有bug。。

 
   
   
 
  1.    const (

  2.    // 单字符是被 String 方法用于格式化的属性缩写。

  3.    ModeDir        FileMode = 1 << (32 - 1 - iota) // d: 目录

  4.    ModeAppend                                     // a: 只能写入,且只能写入到末尾

  5.    ModeExclusive                                  // l: 用于执行

  6.    ModeTemporary                                  // T: 临时文件(非备份文件)

  7.    ModeSymlink                                    // L: 符号链接(不是快捷方式文件)

  8.    ModeDevice                                     // D: 设备

  9.    ModeNamedPipe                                  // p: 命名管道(FIFO)

  10.    ModeSocket                                     // S: Unix域socket

  11.    ModeSetuid                                     // u: 表示文件具有其创建者用户id权限

  12.    ModeSetgid                                     // g: 表示文件具有其创建者组id的权限

  13.    ModeCharDevice                                 // c: 字符设备,需已设置ModeDevice

  14.    ModeSticky                                     // t: 只有root/创建者能删除/移动文件

  15.    // 覆盖所有类型位(用于通过&获取类型位),对普通文件,所有这些位都不应被设置

  16.    ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice

  17.    ModePerm FileMode = 0777 // 覆盖所有Unix权限位(用于通过&获取类型位))

读取文件内容:Read和ReadAt

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

  • func(f*File)ReadAt(b[]byte,off int64)(nint,err error) ReadAt 从指定的位置(相对于文件开始位置)读取长度为 len(b) 个字节数据并写入 b。它返回读取的字节数和可能遇到的任何错误。当 n<len(b) 时,本方法总是会返回错误;如果是因为到达文件结尾,返回值err="" 会是="" io.eof。<="" li="" style="box-sizing: border-box;">

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

数据写入文件:Write

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

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

    • 打开文件时指定 os.O_SYNC;

    • 调用 File.Sync() 方法。

关闭文件:Close

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

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

    1. 关闭一个未打开的文件;

    2. 两次关闭同一个文件;

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

改变文件偏移量:Seek

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

  • func(f*File)Seek(offset int64,whenceint)(ret int64,err error)Seek 可以调整文件偏移量

    • Seek 设置下一次读/写的位置。

    • offset 为相对偏移量,

    • whence 决定相对位置:0为相对文件开头,1为相对当前位置,2为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。使用中,whence 应该使用 os 包中的常量:SEEKSET、SEEKCUR 和 SEEK_END。 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 或 终端。

截断文件

 
   
   
 
  1. func Truncate(name string, size int64) error

  2. func (f *File) Truncate(size int64) error

丢弃文件中大于size的后续字节。

文件属性

  • 文件属性,也即文件元数据。

  • 文件属性具体信息通过 os.FileInfo 接口获取。函数 Stat、Lstat 和 File.Stat 可以得到该接口的实例。

    • stat 会返回所命名文件的相关信息。

    • lstat 与 stat 类似,区别在于如果文件是符号链接,那么所返回的信息针对的是符号链接自身(而非符号链接所指向的文件)。

    • fstat 则会返回由某个打开文件描述符(Go 中则是当前打开文件 File)所指代文件的相关信息。

改变文件时间戳

  • funcChtimes(namestring,atime time.Time,mtime time.Time)error 可以显示改变文件的访问时间和修改时间。

文件属主

每个文件都有一个与之关联的用户ID(UID)和组ID(GID),籍此可以判定文件的属主和属组。

 
   
   
 
  1. func Chown(name string, uid, gid int) error

  2. func Lchown(name string, uid, gid int) error

  3. func (f *File) Chown(uid, gid int) error

文件权限

这里介绍是应用于文件和目录的权限方案,但其规则可适用于所有文件类型,包括设备文件、FIFO 以及 Unix 域套接字等。

普通文件的权限
  • Owner(亦称为 user):授予文件属主的权限。

  • Group:授予文件属组成员用户的权限。

  • Other:授予其他用户的权限。 每一类用户授予的权限如下:

    • Read:可阅读文件的内容。

    • Write:可更改文件的内容。

    • Execute:可以执行文件(如程序或脚本)

目录权限
  • 读权限:可列出(比如,通过 ls 命令)目录之下的内容(即目录下的文件名)

  • 写权限:可在目录内创建、删除文件。注意,要删除文件,对文件本身无需有任何权限。

  • 可执行权限:可访问目录中的文件。因此,有时也将对目录的执行权限称为 search(搜索)权限。

Sticky 位

一般用于目录,起限制删除位的作用。可以在多用户环境下的共享文件夹里删除自己各自的文件。

  • os.Chmod 和 os.File.Chmod 可以修改文件权限(包括 sticky 位),分别对应系统调用 chmod 和 fchmod。

目录与链接

创建和移除(硬)链接

硬链接是针对文件而言的,目录不允许创建硬链接。

  • funcLink(oldname,newnamestring)error

Link 创建一个名为 newname 指向 oldname 的硬链接。如果出错,会返回 *LinkError 类型的错误。

  • funcRemove(namestring)error

Remove 删除 name 指定的文件或目录。如果出错,会返回 *PathError 类型的错误。如果目录不为空,Remove 会返回失败。

更改文件名

funcRename(oldpath,newpathstring)error Rename 修改一个文件的名字或移动一个文件。如果 newpath 已经存在,则替换它。注意,可能会有一些个操作系统特定的限制。

使用符号链接

funcSymlink(oldname,newnamestring)error

Symlink 创建一个名为 newname 指向 oldname 的符号链接。如果出错,会返回 *LinkError 类型的错误。

有时候,我们希望通过符号链接,能获取其所指向的路径名。系统调用 readlink 能做到,Go 的封装函数是 os.Readlink:

funcReadlink(namestring)(string,error)

创建和移除目录

  • funcMkdir(namestring,permFileMode)error Mkdir 使用指定的权限和名称创建一个目录。如果出错,会返回 *PathError 类型的错误。

  • 因为 Mkdir 所创建的只是路径名中的最后一部分,如果父目录不存在,创建会失败。os.MkdirAll 用于递归创建所有不存在的目录。

  • funcRemove(namestring)error 移除一个指定的目录,目录可以是绝对路径或相对路径。

  • funcRemoveAll(pathstring)error RemoveAll 删除 path 指定的文件,或目录及它包含的任何下级对象。它会尝试删除所有东西,除非遇到错误并返回。如果 path 指定的对象不存在,RemoveAll 会返回 nil 而不返回错误。

读目录

func(f*File)Readdirnames(nint)(names[]string,err error) Readdirnames 读取目录 f 的内容,返回一个最多有 n 个成员的[]string,切片成员为目录中文件对象的名字,采用目录顺序。对本函数的下一次调用会返回上一次调用未读取的内容的信息。

如果 n>0,Readdirnames 函数会返回一个最多 n 个成员的切片。这时,如果 Readdirnames 返回一个空切片,它会返回一个非 nil 的错误说明原因。如果到达了目录 f 的结尾,返回值 err 会是 io.EOF。

如果 n<=0,Readdirnames 函数返回目录中剩余所有文件对象的名字构成的切片。此时,如果 Readdirnames 调用成功(读取所有内容直到结尾),它会返回该切片和 nil 的错误值。如果在到达结尾前遇到错误,会返回之前成功读取的名字构成的切片和该错误。

  • func(f*File)Readdir(nint)(fi[]FileInfo,err error) Readdir 内部会调用 Readdirnames,将得到的 names 构造路径,通过 Lstat 构造出 []FileInfo。

path/filepath — 兼容操作系统的文件路径操作

路径分隔符使用 os.PathSeparator

解析路径名字符串

 
   
   
 
  1. func Dir(path string) string

  2. func Base(path string) string

  • Dir() 函数将一个路径名字符串分解成目录名。返回路径中除去最后一个路径元素的部分,即该路径最后一个元素所在的目录。在使用 Split 去掉最后一个元素后,会简化路径并去掉末尾的斜杠。如果路径是空字符串,会返回".";如果路径由1到多个斜杠后跟0到多个非斜杠字符组成,会返回"/";其他任何情况下都不会返回以斜杠结尾的路径。


  • Base() 函数将一个路径名字符串分解成文件名。函数返回路径的最后一个元素。在提取元素前会去掉末尾的斜杠。如果路径是"",会返回".";如果路径是只有一个斜杆构成的,会返回"/"。


  • funcExt(pathstring)string Ext 函数返回 path 文件扩展名。扩展名是路径中最后一个从 . 开始的部分,包括 .。如果该元素没有 . 会返回空字符串。


相对路径和绝对路径

  • funcIsAbs(pathstring)bool 返回路径是否是一个绝对路径

  • funcAbs(pathstring)(string,error) Abs 函数返回 path 代表的绝对路径,如果 path 不是绝对路径,会加入当前工作目录以使之成为绝对路径。

  • funcRel(basepath,targpathstring)(string,error) Rel 函数返回一个相对路径

 
   
   
 
  1. fmt.Println(filepath.Rel("/home/polaris/studygolang", "/home/polaris/studygolang/src/logic/topic.go"))

  2. fmt.Println(filepath.Rel("/home/polaris/studygolang", "/data/studygolang"))

  3. // Output:

  4. // src/logic/topic.go <nil>

  5. // ../../../data/studygolang <nil>

路径的切分和拼接

  • funcSplit(pathstring)(dir,filestring) Split 函数根据最后一个路径分隔符将路径 path 分隔为目录和文件名两部分(dir 和 file)

  • funcJoin(elem...string)string Join 用于将多个路径拼接起来,会根据情况添加路径分隔符。

  • funcSplitList(pathstring)[]string SplitList 分割 PATH 或 GOPATH 之类的环境变量(这些路径被特定于OS 的列表分隔符连接起来)

规整化路径

符号链接指向的路径名

  • funcEvalSymlinks(pathstring)(string,error) path 或返回值是相对路径,则是相对于进程当前工作目录。

文件路径匹配

  • funcGlob(patternstring)(matches[]string,err error) Glob 函数返回所有匹配了 模式字符串 pattern 的文件列表或者nil(如果没有匹配的文件)。Glob 的常见用法,是读取某个目录下所有的文件,比如写单元测试时,读取 testdata 目录下所有测试数据: filepath.Glob("testdata/*.input")

遍历目录

  • funcWalk(rootstring,walkFnWalkFunc)error Walk 函数会遍历 root 指定的目录下的文件树,对每一个该文件树中的目录和文件都会调用 walkFn,包括 root 自身。所有访问文件/目录时遇到的错误都会传递给 walkFn 过滤。文件是按字典顺序遍历的,这让输出更漂亮,但也导致处理非常大的目录时效率会降低。Walk 函数不会遍历文件树中的符号链接(快捷方式)文件包含的路径。

参考

1.[《Go语言标准库》The Golang Standard Library by Example]( https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter01/01.0.html)


以上是关于区块链开发之Go语言—文件系统的主要内容,如果未能解决你的问题,请参考以下文章

新人必看区块链开发零基础必备技能之GO语言

区块链基础语言——Go语言工程管理

区块链开发 Go语言:并发编程

区块链技术人才之技能掌握

区块链之开发命令行操作模块

区块链之开发命令行操作模块