文件描述
源码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
打印具体内容我注释掉了,我是读取我另一个文件的内容,具体大家试一哈就晓得了,写的实例也很简单,这里就不演示了。