区块链开发之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 中定义为常量(以下值并非所有操作系统都可用)
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 // 如果可能,打开时清空文件
)
- 参数 perm 指定了文件的模式和权限位 //尴尬,这行排了好久排不好版,微信有bug。。
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权限位(用于通过&获取类型位))
读取文件内容: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 返回错误:
关闭一个未打开的文件;
两次关闭同一个文件;
通常,我们不回去检查 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 或 终端。
截断文件
func Truncate(name string, size int64) error
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),籍此可以判定文件的属主和属组。
func Chown(name string, uid, gid int) error
func Lchown(name string, uid, gid int) error
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
解析路径名字符串
func Dir(path string) string
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 函数返回一个相对路径
fmt.Println(filepath.Rel("/home/polaris/studygolang", "/home/polaris/studygolang/src/logic/topic.go"))
fmt.Println(filepath.Rel("/home/polaris/studygolang", "/data/studygolang"))
// Output:
// src/logic/topic.go <nil>
// ../../../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语言—文件系统的主要内容,如果未能解决你的问题,请参考以下文章