os.File

在Go中,文件被抽象为File结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
type File struct {
	*file // os specific
}

type file struct {
	pfd         poll.FD
	name        string
	dirinfo     *dirInfo // nil unless directory being read
	nonblock    bool     // whether we set nonblocking mode
	stdoutOrErr bool     // whether this is stdout or stderr
	appendMode  bool     // whether file is opened for appending
}

File是操作系统相关的,在linux下,File支持的方法在os/file_posix.go中:

方法签名 用途
Name() string 文件名
Close() 关闭文件
Read(b []byte) (n int, err error) 读取最多len(b)长度的内容到 b 这个 byte 切片中, 返回填充数
ReadAt(b []byte, off int64) (n int, err error) 从文件的offset处读取最多len(b)长度的内容到切片b中
ReadFrom(r io.Reader) (n int64, err error) 读取r这个Reader中的内容到文件中
Write(b []byte) (n int, err error) 将切片b的内容写入到文件中,并返回写入的长度
WriteAt(b []byte, off int64) (n int, err error) 将切片b的内容从文件的offset处开始写,返回写入数
Seek(offset int64, whence int) (ret int64, err error) 为下一次Read()Write()设置偏移量offsetwhence指示offset的参照物,为0时相对于文件开头,为1时相对于当前文件指针位置,为2时相对于文件结尾
WriteString(s string) (n int, err error) 将字符串n写入到文件中,返回写入的字节数
Chmod(mode FileMode) error 文件权限控制
SetDeadline(t time.Time) error 大多数系统的普通文件不支持Deadline相关的操作,但是pipe支持
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
SyscallConn() (syscall.RawConn, error)
Chown(uid, gid int) error linux下,修改文件的uid和gid
Truncate(size int64) error linux下,修改文件的大小,不改变offset指针
Sync() error linux下,将文件刷到磁盘上
Chdir() error linux下,修改目录到其他目录(当前文件必须是目录)

文件打开操作:os.Open()和os.OpenFile()

Open(name string) (*File, error), 默认打开文件的模式是只读,返回的是File类型实例

1
2
filename := "test.txt"
fp, err := os.Open(filename)

使用os.OpenFile(name string, flag int, perm FileMode), 支持的flag的常量如下:

  • O_RDONLY 只读
  • O_WDONLY 只写
  • O_RDWR 读和写
  • O_APPEND 追加在末尾
  • O_CREATE 如果不存在,则创建
  • O_EXCL 配合O_CREATE使用,文件必须不存在
  • O_SYNC 同步IO
  • O_TRUNC

文件读操作: File.Read() 和 bufio.Reader、bufio.Scanner

file.Read()的实现:

1
2
3
4
5
6
7
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

参数是一个byte切片,读取len(b)长度,返回实际长度n。实际使用时,循环读取。

bufio.Reader的定义:

1
2
3
4
5
6
7
8
type Reader struct {
	buf          []byte
	rd           io.Reader // reader provided by the client
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // last byte read for UnreadByte; -1 means invalid
	lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

bufio.Reader的使用方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main(){
	var filename = string("test.txt")
	file, err := os.Open(filename); if err != nil {
		fmt.Println("file open fail")
	}
	defer file.Close()

	var buf []byte
	n, err := file.Read(buf); if err != nil{
		fmt.Println("Read fail")
	}
	fmt.Printf("file.Read() read %d byte\n", n)

	reader := bufio.NewReader(file)
	line, _, err := reader.ReadLine()
	fmt.Println(string(line))

	line2, err := reader.ReadBytes('\n'); if err != nil {
		fmt.Println("reader ReadBytes() fail", err)
	}
	fmt.Printf(string(line2))

	line3, err := reader.ReadString('\n'); if err != nil {
		fmt.Println("reader ReadString() err", err)
	}
	fmt.Println(line3)
}

test.txt的内容为:

1
2
3
4
hello, world!
来lai ♂
wu~ ♂

标准库注释推荐使用reader.ReadBytes('\n')或reader.ReadString('\n')或bufio.Scanner

reader支持的方法:

方法签名 用途
Size() int Reader中的buf的长度(注意不是容量)
Reset(r io.Reader) 将当前的Reader.rd置为r, Reader.lastByte和Reader.lastRuneSize置为-1
Peek(n int) ([]byte, error) 读取reader的后n个字节,但不移动reader的读取指针,即不修改Reader.r
Discard(n int) (discarded int, err error) 丢弃接下来的n个字节,并修改bufio.Reader.r
Read(p []byte) (n int, err error) 读取最多len(p)个字节到切片p中
ReadByte() (byte, error) 读取一个字节
UnreadByte() error 将最后一个已读的字节改为未读, 即bufio.Reader.r–
ReadRune() (r rune, size int, err error) 读一个rune
UnreadRune() error 取消读一个rune
Buffered() int 返回当前bufio.Reader.buf可读长度
ReadSlice(delim byte) (line []byte, err error) 根据分隔符进行切分,并把内容装到切片中返回
ReadLine() (line []byte, isPrefix bool, err error) 读取一行,如果这行太长,导致buf无法全部装下,第一次返回时isPrefix为true,后续全为false
ReadBytes(delim byte) ([]byte, error) 读取第一个分隔符之前的所有内容,不受bufio.reader.buf的大小限制。 注意包含分隔符
ReadString(delim byte) (string, error) 类似与ReadBytes(), 但是返回值是string,注意包括分隔符
WriteTo(w io.Writer) (n int64, err error) 将reader的内容写去w

bufio.Scanner的定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
type Scanner struct {
	r            io.Reader // The reader provided by the client.
	split        SplitFunc // The function to split the tokens.
	maxTokenSize int       // Maximum size of a token; modified by tests.
	token        []byte    // Last token returned by split.
	buf          []byte    // Buffer used as argument to split.
	start        int       // First non-processed byte in buf.
	end          int       // End of data in buf.
	err          error     // Sticky error.
	empties      int       // Count of successive empty tokens.
	scanCalled   bool      // Scan has been called; buffer is in use.
	done         bool      // Scan has finished.
}

bufio.Scanner的使用方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
   "bufio"
   "fmt"
   "os"
)

func main(){
   var filename = string("test.txt")
   file, err := os.Open(filename); if err != nil {
   	fmt.Println("file open fail")
   }
   defer file.Close()

   sc := bufio.NewScanner(file)
   for sc.Scan(){
   	fmt.Println(sc.Text())
   }
}

bufio.Scanner默认使用ScanLine()作为分割函数,会去掉多余的\r\n

方法签名 用途
Bytes() []byte 返回上一次Scanner.Scan()方法执行后的Scanner.token
Text() string 返回上一次Scanner.Scan()方法执行后的string(Scanner.token)
Scan() bool 获取下一个token
Buffer(buf []byte, max int) 给Scanner设置buffer和最大值,默认大小为64*1024 Byte,即64kB,只能在Scanner调用Scan()前设置
Split(split SplitFunc) 给Scanner设置分割函数,只能在调用Scan()前设置

文件写操作: File.Write()、File.WriteString()、bufio.Writer

File.Write()和File.WriteString():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
	"fmt"
	"os"
)


func main(){
	var filename = string("test.txt")

	file, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666); if err != nil {
		fmt.Println("file open fail: ", err)
		return
	}
	defer file.Close()

	_, err = file.WriteString("\nI wanted you"); if err != nil {
		fmt.Println("WriteString() fail: ", err)
	}
	file.Write([]byte("\nI wanted you one more time!"))
	file.Sync()
}

bufio.Writer是带缓冲区的文件写入,其定义为:

1
2
3
4
5
6
type Writer struct {
	err error
	buf []byte
	n   int
	wr  io.Writer
}

bufio.Writer的使用方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main(){
	var filename = string("test.txt")

	file, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666); if err != nil {
		fmt.Println("file open fail: ", err)
		return
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	writer.WriteString("\n画一个姑娘陪着我")
	writer.Flush()
}
bufio.Writer方法签名 用途
NewWriter(w io.Writer) *Writer 新建,默认缓冲大小 4096 Byte
NewWriterSize(w io.Writer, size int) *Writer 创建一个缓冲大小为n的bufio.Writer
Write(p []byte) (n int, err error) 写入字节数组p,返回写入字节数
WriteString(s string) (int, error) 写入一个字符串
WriteByte(c byte) error 写入一个字节
WriteRune(r rune) (size int, err error) 写入一个rune
Flush() error 将缓冲中的数据写入底层的io.Writer