io 包提供了对I/O
原语的基本接口,其基本任务是包装这些原语已有的实现(如 os 包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加一些相关的原语的操作。
io 包常用接口
io.Reader 和 io.Writer 接口
io.Reader 接口
// io.Reader 接口用于包装基本的读取方法
type Reader interface {
Read(p []byte) (n int, err error)
}
从底层输入流读取最多len(p)
个字节数据写入到 p 中,返回读取的字节数 n 和遇到的任何错误 err。即使 Read 方法返回的n < len(p)
,在调用过程也会占用len(p)
个字节作为暂存空间。若可读取的数据不足len(p)
个字节,Read 方法会返回可用数据,而不是等待更多数据。
当 Read 方法读取n > 0
个字节后遇到一个错误或 EOF (end-of-file)
,会返回读取的字节数,同时在该次调用返回一个非 nil 错误或者在下一次的调用时返回 0 和该错误。一般情况下,io.Reader
接口会在输入流的结尾返回非 0 的字节数,返回值 err 为 nil 或者io.EOF
,但下次调用必然返回(0, io.EOF)
。调用者在考虑错误之前应当首先处理n > 0
字节的返回数据,这样做可以正确地处理在读取一些字节后产生的I/O
错误,同时允许 EOF 的出现。
☕️ 示例代码
package main
import (
"fmt"
"io"
"os"
"strings"
)
func ReadFrom(reader io.Reader, num int) ([]byte, error) {
p := make([]byte, num)
n, err := reader.Read(p)
if n > 0 {
return p[:n], nil
}
return p, err
}
func main() {
// 例子 1:从标准输入中读取 11 个字节
// 控制台输入:hello world!!!!
data, err := ReadFrom(os.Stdin, 11)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", data) // hello world
// 例子 2:从普通文件中读取 9 个字节
// 文件内容:hello world!!!!
f, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer f.Close()
data, err = ReadFrom(f, 9)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", data) // hello wor
// 例子 3:从字符串中读取 12 个字节
r := strings.NewReader("hello world!!!!")
data, err = ReadFrom(r, 12)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", data) // hello world!
}
io.Writer 接口
// io.Writer 接口用于包装基本的写入方法
type Writer interface {
Write(p []byte) (n int, err error)
}
将len(p)
个字节数据从 p 中写入底层的输出流,返回写入的字节数 n 和遇到的任何错误 err。如果 Write 方法返回的n < len(p)
,它就必须返回一个 非 nil 的错误。Write 方法不能修改切片 p 中的数据,即使临时修改也不行。
⭐️ 示例代码
package main
import (
"fmt"
"io"
"os"
)
func WriteTo(write io.Writer, p []byte) (int, error) {
n, err := write.Write(p)
return n, err
}
func main() {
p := []byte("hello world!!!\n")
// 例子 1:将字符串写入到标准输出
n, err := WriteTo(os.Stdout, p)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes\n", n)
// 例子 2:将字符串写入文件中
f, err := os.Create("./test.txt")
if err != nil {
panic(err)
}
defer f.Close()
n, err = WriteTo(f, p)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes\n", n)
}
// 控制台输出:
// hello world!!!
// write 15 bytes
// write 15 bytes
// test.txt 文件内容:
// hello world!!!
io.Reader 和 io.Writer 接口实现类型
os.File
同时实现了io.Reader
和io.Writer
接口strings.Reader
实现了io.Reader
接口bufio.Reader
和bufio.Writer
分别实现了io.Reader
和io.Writer
接口bytes.Buffer
同时实现了io.Reader
和io.Writer
接口bytes.Reader
实现了io.Reader
接口compress/gzip.Reader
和compress/gzip.Writer
分别实现了io.Reader
和io.Writer
接口crypto/cipher.StreamReader
和crypto/cipher.StreamWriter
分别实现了io.Reader
和io.Writer
接口crypto/tls.Conn
同时实现了io.Reader
和io.Writer
接口encoding/csv.Reader
和encoding/csv.Writer
分别实现了io.Reader
和io.Writer
接口mime/multipart.Part
实现了io.Reader
接口net/conn
分别实现了io.Reader
和io.Writer
接口(Conn 接口定义了 Read 和 Write 方法)
除此之外,io 包本身也有这两个接口的实现类型。如:
- 实现了
io.Reader
的类型:LimitedReader
、PipeReader
、SectionReader
- 实现了
io.Writer
的类型:PipeWriter
以上类型中,常用的类型有:os.File
、strings.Reader
、bufio.Reader/Writer
、bytes.Buffer
、bytes.Reader
。
io.ReaderAt 和 io.WriterAt 接口
io.ReaderAt 接口
// io.ReaderAt 接口包装了基本的 ReadAt 方法
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
从底层输入流的偏移量 off 位置读取最多len(p)
字节数据写入 p,返回读取的字节数 n 和遇到的任何错误 err。当ReadAt
方法返回的n < len(p)
时,它会返回一个非 nil 的错误来说明没有读取更多的字节的原因。在这方面,ReadAt
方法是比 Read 方法要严格的。
即使ReadAt
方法返回的n < len(p)
,在调用过程也会占用len(p)
个字节作为暂存空间。如果有部分可用数据,但不够len(p)
字节,ReadAt 方法会阻塞直到获取len(p)
个字节数据或者遇到错误。在这方面,ReadAt
方法和 Read 方法是不同的。如果 ReadAt 方法返回时到达输入流的结尾,返回的n == len(p)
,返回的 err 为 nil 或者io.EOF
。
如果ReadAt
方法是从某个有偏移量的底层输入流读取,ReadAt
方法既不应影响底层的偏移量,也不被它所影响。ReadAt
方法的调用者可以对同一输入流执行并行的ReadAt
调用。
✏️ 示例代码
package main
import (
"fmt"
"strings"
)
func main() {
reader := strings.NewReader("Go语言中文网")
p := make([]byte, 6)
n, err := reader.ReadAt(p, 2)
if err != nil {
panic(err)
}
fmt.Printf("Read %d bytes: %s\n", n, p) // Read 6 bytes: 语言
}
io.WriterAt 接口
// io.WriterAt 接口包装了基本的 WriteAt 方法
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
将len(p)
个字节数据从 p 中写入偏移量 off 位置的底层输出流中,返回写入的字节数 n 和遇到的任何错误 err。当WriteAt
方法返回的n < len(p)
时,它就必须返回一个非 nil 的错误。
如果WriteAt
方法写入的目标是某个有偏移量的底层输出流,WriteAt
方法既不应影响底层的偏移量,也不被它所影响。ReadAt
方法的调用者可以对同一输入流执行并行的WriteAt
调用。(前提是写入范围不重叠)
示例代码
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("writeAt.txt")
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("Golang中文社区——这里是多余hh!!!")
n, err := file.WriteAt([]byte("Go语言中文网"), 24)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes", n) // write 17 bytes
file.WriteString("hello world!!!")
}
// test.txt 文件内容:
// Golang中文社区——Go语言中文网!!!hello world!!!
io.ReaderFrom 和 io.WriteTo 接口
io.ReaderFrom 接口
// io.ReaderFrom 接口包装了基本的 ReadFrom 方法
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
从输入流 r 中读取数据,写入底层输出流,直到 EOF 或发生错误。其返回值 n 为读取的字节数,除io.EOF
之外,在读取过程中遇到的其它错误 err 也将被返回。
注意:ReadFrom
方法不会返回err == io.EOF
✌ 示例代码
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// test.txt 文件内容:hello world!!!
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// 调用 file 的读操作从文件中读取文件,并写入标准输出
writer := bufio.NewWriter(os.Stdout)
n, err := writer.ReadFrom(file)
if err != nil {
panic(err)
}
fmt.Printf("read %d bytes\n", n)
// 将缓存中的数据 flush 到控制台
writer.Flush()
}
// 控制台输出:
// hello world!!!
// read 15 bytes
io.WriteTo 接口
// io.WriterTo 接口包装了基本的 WriteTo 方法
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
从底层输入流中读取数据,写入输出流 w 中,直到没有数据可写或发生错误。其返回值 n 为写入的字节数,在写入过程中遇到的任何错误也将被返回。
✌ 示例代码
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
// 调用 r 的读操作读取数据,并写入到标准输出
r := bytes.NewReader([]byte("Go语言中文网\n"))
n, err := r.WriteTo(os.Stdout)
if err != nil {
panic(err)
}
fmt.Printf("write %d bytes\n", n)
}
// 控制台输出:
// Go语言中文网
// write 18 bytes
io.Seeker 接口
// io.Seeker 接口用于包装基本的读/写偏移量移动方法
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
设定下一次读/写的位置。offset 为相对偏移量,而 whence 决定相对位置:0 为相对文件开头,1 为相对当前位置,2 为相对文件结尾。返回新的偏移量(相对开头)和可能的错误。
移动到一个绝对偏移量为负数的位置会导致错误,移动到任何偏移量为正数的位置都是合法的,但其下一次I/O
操作的具体行为则要看底层的实现。
// whence 的值,在 io 包中定义了相应的常量,推荐使用这些常量
const (
SeekStart = 0 // seek relative to the origin of the file
SeekCurrent = 1 // seek relative to the current offset
SeekEnd = 2 // seek relative to the end
)
✍ 示例代码
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Go语言中文网")
fmt.Println(r.Len()) // 17
n, err := r.Seek(-6, io.SeekEnd)
if err != nil {
panic(err)
}
fmt.Println(n) // 11
c, _, _ := r.ReadRune()
fmt.Printf("%c\n", c) // 文
}
io.Closer 接口
// io.Closer 接口用于包装基本的关闭方法
type Closer interface {
Close() error
}
该接口比较简单,用于关闭数据流。文件 (os.File
)、归档(压缩包)、数据库连接、Socket 等需要手动关闭的资源都实现了io.Closer
接口。实际编程中,经常将 Close 方法的调用放在 defer 语句中。
示例代码
package main
import (
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
}
io.ByteReader、io.ByteWriter 和 io.ByteScanner 接口
// io.ByteReader 接口是基本的 ReadByte 方法的包装
type ByteReader interface {
ReadByte() (c byte, err error)
}
// io.RuneReader 接口是基本的 ReadRune 方法的包装
type ByteWriter interface {
WriteByte(c byte) error
}
// io.ByteScanner 接口在基本的 ReadByte 方法之外还添加了 UnreadByte 方法
type ByteScanner interface {
ByteReader
UnreadByte() error
}
io.ByteReader
和io.ByteWriter
接口用于读/写一个字节。在标准库中,有如下类型实现了io.ByteReader
或io.ByteWriter
接口:
bufio.Reader
和bufio.Writer
分别实现了io.ByteReader
和io.ByteWriter
接口bytes.Buffer
同时实现了io.ByteReader
和io.ByteWriter
接口bytes.Reader
实现了io.ByteReader
接口strings.Reader
实现了io.ByteReader
接口
io.ByteScanner
接口内嵌了io.ByteReader
接口之外还添加了UnreadByte
方法,该方法会还原最近一次读取操作读出的最后一个字节,相当于让读偏移量向前移动一个字节。注意,连续两次UnreadByte
方法调用而中间没有任何读取操作,会返回错误。
☕️ 示例代码
package main
import (
"bytes"
"fmt"
)
func main() {
var ch byte
fmt.Scanf("%c\n", &ch)
var b bytes.Buffer
// 写入一个字节
err := b.WriteByte(ch)
if err != nil {
panic(err)
}
fmt.Println("写入一个字节成功!准备读取该字节……")
// 读取一个字节
newCh, _ := b.ReadByte()
fmt.Printf("读取的字节:%c\n", newCh)
// 还原最近一次读取操作读出的最后一个字节
b.UnreadByte()
newCh2, _ := b.ReadByte()
fmt.Printf("再次读取的字节:%c\n", newCh2)
}
// 控制台输入:
// A
// 控制台输出:
// 写入一个字节成功!准备读取该字节……
// 读取的字节:A
// 再次读取的字节:A
io.RuneReader 和 io.RuneScanner 接口
// io.RuneReader 是基本的 ReadRune 方法的包装
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}
// io.RuneScanner 接口在基本的 ReadRune 方法之外还添加了 UnreadRune 方法
type RuneScanner interface {
RuneReader
UnreadRune() error
}
和io.ByteReader/io.ByteScanner
接口类似,不过io.RuneReader/io.RuneScanner
接口操作的是一个字符。ReadRune
方法读取单个UTF-8
编码的字符,返回其 rune 和该字符占用的字节数。如果没有有效的字符,会返回错误。
UnreadRune
方法还原前一次ReadRune
操作读取的 unicode 码值,相当于让读偏移量前移一个码值长度。注意,UnreadRune
方法调用前必须调用ReadRune
方法,UnreadRune
方法比UnreadByte
严格很多。
⭐️ 示例代码
package main
import (
"bytes"
"fmt"
)
func main() {
var ch rune
fmt.Scanf("%c\n", &ch)
var b bytes.Buffer
// 写入一个字符,WriteRune 方法是 bytes.Buffer 自定义的方法
b.WriteRune(ch)
fmt.Println("写入一个字符成功!准备读取该字符……")
// 读取一个字符
newCh, _, _ := b.ReadRune()
fmt.Printf("读取的字符:%c\n", newCh)
// 还原前一次 ReadRune 操作读取的字符
b.UnreadRune()
newCh2, _, _ := b.ReadRune()
fmt.Printf("再次读取的字符:%c\n", newCh2)
}
// 控制台输入:
// 您
// 控制台输出:
// 写入一个字符成功!准备读取该字符……
// 读取的字符:您
// 再次读取的字符:您
其它聚合接口
// io.ReadWriter 接口聚合了基本的读写操作
type ReadWriter interface {
Reader
Writer
}
// io.ReadCloser 接口聚合了基本的读取和关闭操作
type ReadCloser interface {
Reader
Closer
}
// io.ReadSeeker 接口聚合了基本的读取和移位操作
type ReadSeeker interface {
Reader
Seeker
}
// io.ReadWriteCloser 接口聚合了基本的读写和关闭操作
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// io.ReadWriteSeeker 接口聚合了基本的读写和移位操作
type ReadWriteSeeker interface {
Reader
Writer
Seeker
}
// io.WriteSeeker 接口聚合了基本的写入和移位操作
type WriteSeeker interface {
Writer
Seeker
}
// io.WriteCloser 接口聚合了基本的写入和关闭操作
type WriteCloser interface {
Writer
Closer
}
io 包常用函数
文件复制 io.Copy/io.CopyN
// 将 src 的数据拷贝到 dst,直到在 src 上到达 EOF 或发生错误。返回拷贝的字节数和遇到的第一个错误
// 对成功的调用,返回值 err 为 nil 而非 EOF,因为 Copy 定义为从 src 读取直到 EOF,它不会将读取到 EOF视为应报告的错误
// 如果 src 实现了 WriterTo 接口,会调用 src.WriteTo(dst) 进行拷贝;如果 dst 实现了 ReaderFrom 接口,会调用 dst.ReadFrom(src) 进行拷贝
func Copy(dst Writer, src Reader) (written int64, err error)
// 从 src 拷贝 n 个字节数据到 dst,直到在 src 上到达 EOF 或发生错误。返回复制的字节数和遇到的第一个错误
// 只有 err 为 nil 时,written 才会等于 n。如果 dst 实现了 ReaderFrom 接口,本函数会调用它实现拷贝
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
✏️ 示例代码
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开原始文件
originalFile, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer originalFile.Close()
// 创建新的文件作为目标文件
newFile, err := os.Create("./test_copy.txt")
if err != nil {
panic(err)
}
defer newFile.Close()
// 从源中复制字节到目标文件
writtenNum, err := io.Copy(newFile, originalFile)
if err != nil {
panic(err)
}
fmt.Printf("Copied %d bytes.", writtenNum)
// 将文件内容 flush 到硬盘中
err = newFile.Sync()
if err != nil {
panic(err)
}
}
读取至少 N 个字节 io.ReadAtLeast
// 从 r 至少读取 min 字节数据填充进 buf。函数返回写入的字节数和错误(如果没有读取足够的字节)
// 只有没有读取到字节时才可能返回 io.EOF;如果读取了有但不够的字节时遇到了 EOF,函数会返回ErrUnexpectedEOF
// 如果 min 比 buf 的长度还大,函数会返回 ErrShortBuffer。只有返回值 err 为 nil 时,返回值 n 才会不小于 min
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
示例代码
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开文件,只读
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
byteSlice := make([]byte, 512)
minBytes := 8
// io.ReadAtLeast() 在不能得到最小的字节的时候会返回错误,但已读的文件数据仍保留在 byteSlice 中
numBytesRead, err := io.ReadAtLeast(file, byteSlice, minBytes)
if err != nil {
panic(err)
}
fmt.Printf("Number of bytes read: %d\n", numBytesRead)
fmt.Printf("Data read: %s\n", byteSlice)
}
读取正好 N 个字节 io.ReadFull
// 从 r 精确地读取 len(buf) 字节数据填充进 buf。函数返回写入的字节数和错误(如果没有读取足够的字节)
// 只有没有读取到字节时才可能返回 io.EOF;如果读取了有但不够字节数时遇到了 EOF,函数会返回ErrUnexpectedEOF
// 只有返回值 err 为 nil 时,返回值 n 才会等于 len(buf)
func ReadFull(r Reader, buf []byte) (n int, err error)
✌ 示例代码
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开文件,只读
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// file.Read() 可以读取一个小文件到大的 byte slice 中,
// 但是 io.ReadFull() 在文件的字节数小于 byte slice 字节数的时候会返回错误
byteSlice := make([]byte, 2)
numBytesRead, err := io.ReadFull(file, byteSlice)
if err != nil {
panic(err)
}
fmt.Printf("Number of bytes read: %d\n", numBytesRead)
fmt.Printf("Data read: %s\n", byteSlice)
}
字符串写入 io.WriteString
// 将字符串 s 的内容写入 w 中,返回写入的字节数和遇到的任何错误。如果 w 已经实现了 WriteString 方法,函数会直接调用该方法
func WriteString(w Writer, s string) (n int, err error)
✍ 示例代码
package main
import (
"fmt"
"io"
"os"
)
func main() {
// 打开文件,只读
file, err := os.Create("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// 写入字符串
n, err := io.WriteString(file, "hello world!!!!")
if err != nil {
panic(err)
}
fmt.Printf("Write %d btyes\n", n)
}
合并多个输入流 io.MultiReader
// 返回一个将提供的 Readers 在逻辑上串联起来的 Reader 接口,它们依次被读取
// 当所有的输入流都读取完毕,Read 才会返回 EOF。如果 readers 中任一个返回了非 nil 非 EOF 的错误,Read 方法会返回该错误
func MultiReader(readers ...Reader) Reader
✍ 示例代码
package main
import (
"bytes"
"fmt"
"io"
"strings"
)
func main() {
readers := []io.Reader{
strings.NewReader("from strings reader!!!"),
bytes.NewBufferString("from bytes buffer!!!"),
}
reader := io.MultiReader(readers...)
data := make([]byte, 0, 128)
buf := make([]byte, 10)
for n, err := reader.Read(buf); err != io.EOF; n, err = reader.Read(buf) {
if err != nil {
panic(err)
}
data = append(data, buf[:n]...)
}
fmt.Printf("%s\n", data) // from strings reader!!!from bytes buffer!!!
}
合并多个输出流 io.MultiWriter
// 创建一个 Writer 接口,会将提供给其的数据同时写入 Writers 中的所有输出流
func MultiWriter(writers ...Writer) Writer
示例代码
package main
import (
"io"
"os"
)
func main() {
file, err := os.Create("./text.txt")
if err != nil {
panic(err)
}
defer file.Close()
writers := []io.Writer{
file,
os.Stdout,
}
writer := io.MultiWriter(writers...)
// 会同时在 text.txt 文件和控制台中输出:Go语言中文网
writer.Write([]byte("Go语言中文网"))
}
读取并自动写入 io.TeeReader
// 返回一个将其从 r 读取的数据写入 w 的 Reader 接口,所有通过该接口对 r 的读取都会执行对应的对 w 的写入
// 没有内部的缓冲,写入必须在读取完成前完成,写入时遇到的任何错误都会作为读取错误返回
func TeeReader(r Reader, w Writer) Reader
☕️ 示例代码
package main
import (
"io"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
r := io.TeeReader(file, os.Stdout)
// r 读取内容后,会自动写入到 os.Stdout,所以控制台会显示文件中内容
r.Read(make([]byte, 100))
}
io/ioutil 包常用函数
虽然 io 包提供了不少类型、方法和函数,但有时候使用起来不是那么方便,比如读取一个文件中的所有内容。为此,Go 语言在io/ioutil
包提供了一些常用、方便的I/O
操作函数。
接口类型转换 ioutil.NopCloser
// 用一个无操作的 Close 方法包装 r,返回一个 io.ReadCloser 接口。Close 方法什么也不做,只是返回 nil
func NopCloser(r io.Reader) io.ReadCloser
⭐️ 示例代码
package main
import (
"fmt"
"io/ioutil"
"strings"
)
func main() {
r := strings.NewReader("hello world")
nc := ioutil.NopCloser(r)
// Close 方法什么也不做,只是返回 nil
nc.Close()
p := make([]byte, 20)
n, err := nc.Read(p)
if err != nil {
panic(err)
}
fmt.Printf("read %d bytes:%s", n, p) // read 11 bytes:hello world
}
读取全部字节 ioutil.ReadAll
// 从 r 读取数据直到 EOF 或遇到 error,返回读取的数据和遇到的错误
// 成功的调用返回的 err 为 nil 而非 EOF。因为本函数定义为读取 r 直到 EOF,它不会将读取返回的 EOF 视为应报告的错误
func ReadAll(r io.Reader) ([]byte, error)
✏️ 示例代码
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
file, err := os.Open("./test.txt")
if err != nil {
panic(err)
}
defer file.Close()
// os.File.Read(), io.ReadFull() 和 io.ReadAtLeast() 在读取之前都需要一个固定大小的 byte slice
// 但 ioutil.ReadAll() 会读取 reader (这个例子中是file) 的每一个字节,然后把字节 slice 返回
data, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
fmt.Printf("Data as hex: %x\n", data)
fmt.Printf("Data as string: %s\n", data)
fmt.Println("Number of bytes read:", len(data))
}
读取目录 ioutil.ReadDir
// 读取 dirname 目录,并返回排好序的文件和子目录名
func ReadDir(dirname string) ([]os.FileInfo, error)
示例代码
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// 输出当前目录下的所有文件(包括子目录)
listAll(".", 0)
}
func listAll(path string, curHeight int) {
fileInfos, err := ioutil.ReadDir(path)
if err != nil {
fmt.Println(err)
return
}
for _, info := range fileInfos {
if info.IsDir() {
for tmpHeight := curHeight; tmpHeight > 0; tmpHeight-- {
fmt.Printf("|\t")
}
fmt.Println(info.Name(), "\\")
listAll(path+"/"+info.Name(), curHeight+1)
} else {
for tmpHeight := curHeight; tmpHeight > 0; tmpHeight-- {
fmt.Printf("|\t")
}
fmt.Println(info.Name())
}
}
}
快读文件 ioutil.ReadFile
io/ioutil
包提供ReadFile
函数可以处理打开文件、读取文件和关闭文件一系列的操作。如果需要简洁快速地读取文件到字节切片中,可以使用它。该方法定义如下:
// 从 filename 指定的文件中读取数据并返回文件的内容
// 成功的调用返回的 err 为 nil 而非 EOF。因为本函数定义为读取整个文件,它不会将读取返回的 EOF 视为应报告的错误
func ReadFile(filename string) ([]byte, error)
✌ 示例代码
package main
import (
"fmt"
"io/ioutil"
)
func main() {
// 读取文件到字节 slice 中
data, err := ioutil.ReadFile("./test.txt")
if err != nil {
panic(err)
}
fmt.Printf("Data read: %s\n", data)
}
快写文件 ioutil.WriteFile
和ReadFile
函数对应,io/ioutil
包提供WriteFile
函数可以处理创建或者打开文件、写入字节切片和关闭文件一系列的操作。如果需要简洁快速地写字节切片到文件中,可以使用它。该方法定义如下:
// 向 filename 指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件
func WriteFile(filename string, data []byte, perm os.FileMode) error
✍ 示例代码
package main
import (
"io/ioutil"
)
func main() {
// 向文件写入 "hello world!!!"
err := ioutil.WriteFile("./test.txt", []byte("hello world!!!"), 0666)
if err != nil {
panic(err)
}
}
创建临时目录 ioutil.TempDir
// 在 dir 目录里创建一个新的、使用 prfix 作为前缀的临时文件夹,并返回文件夹的路径
// 如果 dir 是空字符串,表明使用系统默认的临时目录(参见 os.TempDir 函数)中创建临时目录
// 不同程序同时调用该函数会创建不同的临时目录,调用本函数的程序有责任在不需要临时文件夹时摧毁它
func TempDir(dir, prefix string) (name string, err error)
示例代码
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
// 创建临时目录
name, err := ioutil.TempDir("F:", "temp")
if err != nil {
panic(err)
}
// 删除临时目录
defer os.Remove(name)
fmt.Println(name) // F:\temp2305919538
}
创建临时文件 ioutil.TempFile
// 在 dir 目录下创建一个新的、使用 prefix 为前缀的临时文件,以读写模式打开该文件并返回 os.File 指针
// 如果 dir 是空字符串,表明使用系统默认的临时目录(参见 os.TempDir 函数)中创建临时文件
// 不同程序同时调用该函数会创建不同的临时文件,调用本函数的程序有责任在不需要临时文件时摧毁它
func TempFile(dir, prefix string) (f *os.File, err error)
示例代码
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
// 创建并打开临时文件
f, err := ioutil.TempFile("F:", "temp")
if err != nil {
panic(err)
}
// 删除临时文件
defer func() {
f.Close()
os.Remove(f.Name())
}()
fmt.Println(f.Name()) // F:\temp2379571275
}