内容
1 io 接口
2 ioutil
3 文件读取
io接口
golang io包里封装了操作IO基本原语的接口,主要的基础接口有如下四个:
1
1.1 Read 将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len(p))以及任何遇到的错误。
1.2 即使 Read 返回的 n < len(p),它也会在调用过程中使用 p 的全部作为暂存空间。
1.3 若一些数据可用但不到 len(p) 个字节,Read 会照例返回可用的数据,而不是等待更多数据。
1.4 当 Read 在成功读取 n > 0 个字节后遇到一个错误或EOF(end-of-file),它就会返回读取的字节数。
1.5 它会从相同的调用中返回(非nil的)错误或从随后的调用中返回错误(同时 n == 0)。
一般情况的一个例子就是 Reader 在输入流结束时会返回一个非零的字节数,同时返回的err不是EOF就是nil。
type Reader interface {
Read(p []byte) (n int, err error)
}
2
2.1 Write 将 len(p) 个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数 n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。
2.2 若 Write 返回的 n < len(p),它就必须返回一个 非nil 的错误。
type Writer interface {
Write(p []byte) (n int, err error)
}
3
3.1 Seek 方法是用于设置偏移量的,这样可以从某个特定位置开始操作数据流。
3.2 Seek 设置下一次 Read 或 Write 的偏移量为 offset,它的解释取决于 whence: 0 表示相对于文件的起始处,1 表示相对于当前的偏移,而 2 表示相对于其结尾处。 Seek 返回新的偏移量和一个错误,如果有的话。
type Closer interface {
Close() error
}
4
4.1 只有一个 Close() 方法,用于关闭数据流。
4.2 文件 (os.File)、归档(压缩包)、数据库连接、Socket 等需要手动关闭的资源都实现了 Closer 接口。实际编程中,经常将 Close 方法的调用放在 defer 语句中。
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
实现了io接口的具体类型有:
os.File 同时实现了 io.Reader 和 io.Writer
strings.Reader 实现了 io.Reader
bufio.Reader/Writer 分别实现了 io.Reader 和 io.Writer
bytes.Buffer 同时实现了 io.Reader 和 io.Writer
bytes.Reader 实现了 io.Reader
compress/gzip.Reader/Writer 分别实现了 io.Reader 和 io.Writer
crypto/cipher.StreamReader/StreamWriter 分别实现了 io.Reader 和 io.Writer
crypto/tls.Conn 同时实现了 io.Reader 和 io.Writer
encoding/csv.Reader/Writer 分别实现了 io.Reader 和 io.Writer
mime/multipart.Part 实现了 io.Reader
net/conn 分别实现了 io.Reader 和 io.Writer(Conn接口定义了Read/Write)
常用的类型有:os.File、strings.Reader、bufio.Reader/Writer、bytes.Buffer、bytes.Reader
ioutil
io包中封装了io原语的基本接口,但是具体使用起来不是很方便,ioutil中提供了很多很方便的函数;
1 NopCloser(r io.Reader) io.ReadCloser
2 ReadAll(r io.Reader) ([]byte, error)
3 ReadFile(filename string) ([]byte, error)
4 WriteFile(filename string, data []byte, perm os.FileMode) error
5 ReadDir(dirname string) ([]os.FileInfo, error)
功能说明:
1 NopCloser 方法主要是将一个实现了Reader接口的对象转换成ReadCloser;
2 ReadAll 从r中把一次性所有的内容读取到一个字节数组;内部使用一个byte.Buffer来存储数据,调用io.ReadFrom来读取数据;读取成功后err返回nil, 而不是EOF,成功读取完毕应该为 err == io.EOF,这里返回 nil 由于该函数成功期望 err == io.EOF,符合无错误不处理的理念
3 ReadFile 读取整个文件的内容,ReadFile 会先判断文件的大小,给 bytes.Buffer 一个预定义容量,避免额外分配内存。
4 WriteFile 将data写入filename文件中,当文件不存在时会根据perm指定的权限进行创建一个,文件存在时会先清空文件内容。
5 ReadDir 它读取目录并返回排好序的文件和子目录名( []os.FileInfo )
详细io说明可参考:http://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter01/01.0.html
文件读取
3 Read字节方式
第一种,是最直观会想到的,也就是打开文件,把文件读取一遍。
func main() {
file,err:=os.Open("water")
if err ==nil {
sum := 0
buf:=make([]byte,2014)
for {
n,err:=file.Read(buf)
sum+=n
if err==io.EOF {
break
}
}
fmt.Println("file size is ",sum)
}
}
这种方式需要打开文件,通过for循环读取文件的字节内容,然后算出文件的大小,这样时也是最不能用的办法,因为效率低,代码量大。
2 ioutil方式
上面的代码比较啰嗦,这时候我们可能想到了使用ioutil包的ReadFile来代替,直接获得文件的内容,进而计算出文件的大小。
func main() {
content,err:=ioutil.ReadFile("water")
if err == nil {
fmt.Println("file size is ",len(content))
}
}
通过ioutil.ReadFile函数,我们三行代码就可以搞定,的确方便很多,但是效率慢的问题依然,存在,如果是个很大的文件呢?
3 Stat方法
继续再进一步,我们不读取文件的内容来计算了,我们通过文件的信息
func main() {
file,err:=os.Open("water")
if err == nil {
fi,_:=file.Stat()
fmt.Println("file size is ",fi.Size())
}
}
这种方式不会再读取文件的内容,而是通过Stat方法直接获取,速度会非常快,尤其对于大文件尤其有用。但是它还不是我们今天要讲的终极办法,因为它还是会打开文件,会占用它。
4 终极方案
好了,我们的终极方案终于要登场了,他的代码也非常简单。
func main() {
fi,err:=os.Stat("water")
if err ==nil {
fmt.Println("file size is ",fi.Size(),err)
}
}
是的,也只需要三行代码即可实现,这里使用的是os.Stat,通过他可以获得文件的元数据信息,现在我们看看它能获取到哪些信息。
获取文件信息
通过os.Stat方法,我们可以获取文件的信息,比如文件大小、名字等。
func main() {
fi,err:=os.Stat("water")
if err ==nil {
fmt.Println("name:",fi.Name())
fmt.Println("size:",fi.Size())
fmt.Println("is dir:",fi.IsDir())
fmt.Println("mode::",fi.Mode())
fmt.Println("modTime:",fi.ModTime())
}
}
运行这段代码看下结果:
name: water
size: 403
is dir: false
mode:: -rw-r--r--
modTime: 2018-05-06 18:52:07 +0800 CST
以上就是可以获取到的文件信息,还包括判断是否是目录,权限模式和修改时间。所以我们对于文件的信息获取要使用os.Stat函数,它可以在不打开文件的情况下,高效获取文件信息。
判断文件是否存在
os.Stat函数有两个返回值,一个是文件信息,一个是err,通过err我们可以判断文件是否存在。
首先,err==nil的时候,文件肯定是存在的;其次err!=nil的时候也不代表不存在,这时候我们就需要进行严密的判断。
func main() {
_,err:=os.Stat(".")
if err ==nil {
fmt.Println("file exist")
}else if os.IsNotExist(err){
fmt.Println("file not exist")
}else{
fmt.Println(err)
}
}
通过os.IsNotExist来判断一个文件不存在。最后else的可能性比较少,这个时候可以看下具体的错误是什么,再根据错误来判断文件是否存在。
注:以上板块转载自:https://www.flysnow.org/2020/07/25/golang-file-size.html
网友评论