读写文件

文件描述

源码type.go 21行 是由Stat()接口返回文件的描述信息

// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo interface {
    Name() string       // base name of the file
    Size() int64        // length in bytes for regular files; system-dependent for others
    Mode() FileMode     // file mode bits
    ModTime() time.Time // modification time
    IsDir() bool        // abbreviation for Mode().IsDir()
    Sys() interface{}   // underlying data source (can return nil)
}

文件读取

golang标准库 io/ioutil  ioutil.go 49// ReadFile reads the file named by filename and returns the contents.
// A successful call returns err == nil, not err == EOF. Because ReadFile
// reads the whole file, it does not treat an EOF from Read as an error
// to be reported.
func ReadFile(filename string) ([]byte, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    // It's a good but not certain bet that FileInfo will tell us exactly how much to
    // read, so let's try it but be prepared for the answer to be wrong.
    var n int64

    if fi, err := f.Stat(); err == nil {
        // Don't preallocate a huge buffer, just in case.
        if size := fi.Size(); size < 1e9 {
            n = size
        }
    }
    // As initial capacity for readAll, use n + a little extra in case Size is zero,
    // and to avoid another allocation after Read has filled the buffer. The readAll
    // call will read into its allocated internal buffer cheaply. If the size was
    // wrong, we'll either waste some space off the end or reallocate as needed, but
    // in the overwhelmingly common case we'll get it just right.
    return readAll(f, n+bytes.MinRead)
}

通过源码看到,ReadFile函数读取文件先通过stat函数获取文件的大小,读取指定大小的数据,以免浪费空间,我们看到获取文件大小是有个限制条件是size<1e9,e代表10的几次方,1e9表10的9次方,大概953G的文件大小,如果超过953G,则只会读取bytes.MinRead大小的数据,也就是512(bytes/buffer.go 184行)个字节,也就是读取的最小长度。

// readAll reads from r until an error or EOF and returns the data it read
// from the internal buffer allocated with a specified capacity.
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
    buf := bytes.NewBuffer(make([]byte, 0, capacity))
    // If the buffer overflows, we will get bytes.ErrTooLarge.
    // Return that as an error. Any other panic remains.
    defer func() {
        e := recover()
        if e == nil {
            return
        }
        if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
            err = panicErr
        } else {
            panic(e)
        }
    }()
    _, err = buf.ReadFrom(r)
    return buf.Bytes(), err
}

在bytes/buffer.go 190行我们看到真正读取文件是go自己的buffer包,当读取时返回EOF(在linux系统中,不是一个字符,而是系统读取到文件结尾返回的一个信号值-1)时,跳出for循环,具体过程大家自己看吧。

// MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom. As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the
// underlying buffer.
const MinRead = 512

// ReadFrom reads data from r until EOF and appends it to the buffer, growing
// the buffer as needed. The return value n is the number of bytes read. Any
// error except io.EOF encountered during the read is also returned. If the
// buffer becomes too large, ReadFrom will panic with ErrTooLarge.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
    b.lastRead = opInvalid
    // If buffer is empty, reset to recover space.
    if b.off >= len(b.buf) {
        b.Reset()
    }
    for {
        if free := cap(b.buf) - len(b.buf); free < MinRead {
            // not enough space at end
            newBuf := b.buf
            if b.off+free < MinRead {
                // not enough space using beginning of buffer;
                // double buffer capacity
                newBuf = makeSlice(2*cap(b.buf) + MinRead)
            }
            copy(newBuf, b.buf[b.off:])
            b.buf = newBuf[:len(b.buf)-b.off]
            b.off = 0
        }
        m, e := r.Read(b.buf[len(b.buf):cap(b.buf)])
        b.buf = b.buf[0 : len(b.buf)+m]
        n += int64(m)
        if e == io.EOF {
            break
        }
        if e != nil {
            return n, e
        }
    }
    return n, nil // err is EOF, so return nil explicitly
}

文件写入

ioutil包提供的写入文件也是相当的方便,传入文件名,需要写入的文件内容(字节),文件的操作权限(os.ModePerm FileMode = 0777 // Unix permission bits),其实真正操作是用到了系统调用,而且分不同平台,感兴趣的童鞋可以进去看一下,我这里只是演示

// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it with permissions perm;
// otherwise WriteFile truncates it before writing.
func WriteFile(filename string, data []byte, perm os.FileMode) error {
    f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
    if err != nil {
        return err
    }
    n, err := f.Write(data)
    if err == nil && n < len(data) {
        err = io.ErrShortWrite
    }
    if err1 := f.Close(); err == nil {
        err = err1
    }
    return err
}

实例

好了,接下来看看我们如何利用go标准包很方便的来操作文件, 上代码

package file

import (
    "io/ioutil"
    "os"
    "testing"
)

func checkSize(t *testing.T, path string, size int64) {
    file, err := os.Stat(path)
    if err != nil {
        t.Fatalf("Stat:%q err:%s", path, err)
    }
    t.Logf("stat file size:%d readfie size:%d", file.Size(), size)
}

func TestReadFile(t *testing.T) {
    filename := "fileHelper.go"
    content, err := ioutil.ReadFile(filename)
    if err != nil {
        t.Fatalf("Readfile %s: %v", filename, err)
    }
    t.Logf("readf:%s", string(content))
    checkSize(t, filename, int64(len(content)))
}

--- PASS: TestReadFile (0.00s)
        file_test.go:14: stat file size:792 readfie size:792
PASS
ok      command-line-arguments  0.228s

打印具体内容我注释掉了,我是读取我另一个文件的内容,具体大家试一哈就晓得了,写的实例也很简单,这里就不演示了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值