go基础——IO

酷酷码农 · · 460 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

内容

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字节方式
第一种,是最直观会想到的,也就是打开文件,把文件读取一遍。

    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来代替,直接获得文件的内容,进而计算出文件的大小。

    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的时候也不代表不存在,这时候我们就需要进行严密的判断。

    _,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


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:酷酷码农

查看原文:go基础——IO

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

460 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传