包ioutil实现一些I/O实用程序函数
** ReadAll 方法**
外部调用方法, 封装了 readAll, 设置 缓冲片大小为512字节,传参由 2个减少至了一个,
从r 读取器 读取,直到出现错误或EOF并返回它读取的数据。 成功的调用返回errnil,而不是errEOF
因为ReadAll定义为从src读取直到EOF,所以它不会将EOF from read视为要报告的错误。
func ReadAll(r io.Reader) ([]byte, error) {
return readAll(r, bytes.MinRead) // MinRead = 512
// MinRead是Buffer.ReadFrom传递给读取调用的最小片大小。 只要缓冲区中至少有MinRead字节 > r字节,ReadFrom就不会增长底层缓冲区
}
** readAll 内部读取 方法**
从r读取数据直到EOF或遇到error,返回读取的数据和可能的错误。成功的调用返回的err为nil而非EOF。因为本函数定义为读取r直到EOF,它不会将读取返回的EOF视为应报告的错误。
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
var buf bytes.Buffer
// 如果缓冲区溢出,我们将得到 bytes.ErrTooLarge.作为错误返回。其他的恐慌仍然存在
defer func() {
e := recover()
if e == nil {
return
}
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
err = panicErr
} else {
panic(e)
}
}()
if int64(int(capacity)) == capacity {
buf.Grow(int(capacity))
}
_, err = buf.ReadFrom(r)
return buf.Bytes(), err
}
ReadAll 应用
// 1、open打开文件进行读取,返回*File类型,该类型实现了io.Reader 接口
// 2、 作为 io.Reader 类型传参, 读取该文件,返回出[]byte
// 该方法 内:将 *File类型的file文件操作句柄 转成了 实现了 Closer关闭接口的 具体 nopCloser 结构体实例,然后被当成 io.ReadCloser 接口来返回
// 返回的 nopCloser结构体 本身是io.Reader 实现,又定义了close方法继而实现 Closer关闭接口,所以组合成了 定义的反参: io.ReadCloser 接口
func main() {
file, err := os.Open("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
checkErr(err)
fileBytes1, err := ioutil.ReadAll(file)
checkErr(err)
fmt.Println(string(fileBytes1))
}
** ReadFile 方法**
ReadFile 从filename指定的文件中读取数据并返回文件的内容。对err的判断和ReadAll一样。
func ReadFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
var n int64 = bytes.MinRead
if fi, err := f.Stat(); err == nil {
// 作为readAll的初始容量,在Size为零的情况下使用Size+加一点额外的容量,以避免在Read填满缓冲区后进行另一次分配
// readAll调用将廉价地读入其分配的内部缓冲区。
// 如果大小不正确,我们要么在末端浪费一些空间,要么根据需要重新分配,但在绝大多数常见的情况下,我们会得到正确的结果
if size := fi.Size() + bytes.MinRead; size > n {
n = size
}
}
return readAll(f, n)
}
ReadFile应用
func main() {
file, err := os.Open("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
checkErr(err)
// ReadFile 函数直接可以传文件 参数
fileBytes2, err := ioutil.ReadFile("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
checkErr(err)
fmt.Println(string(fileBytes2))
}
WriteFile 方法
函数向filename指定的文件中写入数据。如果文件不存在将按给出的perm权限创建文件,否则在写入数据之前清空文件。
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
}
_, err = f.Write(data)
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
WriteFile 应用
WriteFile 以权限将 图片数据 写入新 文件
func main() {
pictureFile, err := ioutil.ReadFile("C:/GoWorks/src/go_task/hello-word/io/out/picture.jpg")
checkErr(err)
ioutil.WriteFile("C:/GoWorks/src/go_task/hello-word/io/out/picture1.jpg", pictureFile, 0777)
}
ReadDir 方法
读取dirname目录内的所有文件信息,注意此序列有序
func ReadDir(dirname string) ([]os.FileInfo, error) {
f, err := os.Open(dirname)
if err != nil {
return nil, err
}
list, err := f.Readdir(-1)
f.Close()
if err != nil {
return nil, err
}
sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
return list, nil
}
ReadDir 应用
返回目录里文件,[]os.FileInfo类型
func main() {
dir, err := ioutil.ReadDir("C:/GoWorks/src/go_task/hello-word/io/out")
checkErr(err)
for i, v := range dir {
fmt.Println(i, v)
fmt.Println(i, v.Name())
}
}
nopCloser 结构体
nopCloser也是一个读取器,只不过是个 其字段就直接包含io.Reader读取器接口的结构体
/且该结构体还 带有Close()方法,虽然没有做操作,但是实现了 Closer 接口
type nopCloser struct {
io.Reader
}
func (nopCloser) Close() error { return nil }
返回一个ReadCloser,其中包含一个 no-op Close方法,该方法包装提供的读取器r。
func NopCloser(r io.Reader) io.ReadCloser {
return nopCloser{r}
}
nopCloser 应用
该方法 内:将 *File类型的file文件操作句柄 转成了 实现了 Closer关闭接口的 具体 nopCloser 结构体实例,然后被当成 io.ReadCloser 接口来返回
返回的 nopCloser结构体 本身是io.Reader 实现,又定义了close方法继而实现 Closer关闭接口,所以组合成了 定义的反参: io.ReadCloser 接口
func main() {
file, err := os.Open("C:/GoWorks/src/go_task/hello-word/io/out/file1.txt")
checkErr(err)
// file作为 io.Reader 类型传参, 返回出一个 ReadCloser接口
/
readCloser := ioutil.NopCloser(file)
TemFile 方法
TempFile在dir目录中创建一个新的临时文件, 打开文件进行读写,并返回os.File结果
文件名是通过采用模式并在末尾添加一个随机字符串来生成的。 如果模式包含“”,则随机字符串将替换最后一个“*”
同时调用TempFile的多个程序不会选择同一个文件。
调用者可以使用f.Name()来查找文件的路径名。当不再需要时,调用者有责任删除文件。
func TempFile(dir, pattern string) (f *os.File, err error) {
// 1、如果dir是空字符串,TempFile将使用临时文件的默认目录(请参阅临时目录).
if dir == "" {
dir = os.TempDir()
}
// 2、根据 pattern 进行分割 ,分割出前缀和后缀
prefix, suffix, err := prefixAndSuffix(pattern)
if err != nil {
return
}
nconflict := 0
// 循环 10000次 创建出 一个文件
for i := 0; i < 10000; i++ {
// 3、根据目录 创建新路径
name := filepath.Join(dir, prefix+nextRandom()+suffix)
// 4、打开该 目录下并 创建文件,读写方式
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
// 5、如果该文件存在,且循环了10次以上,则 给随机数 上锁,并重置随机源继续 创建操作
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randmu.Unlock()
}
continue
}
break
}
return
}
内部方法prefixAndSuffix
按最后一个通配符“”分割模式,如果适用,返回前缀作为“”之前的部分,后缀作为“*”之后的部分。
func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
if strings.ContainsRune(pattern, os.PathSeparator) {
err = errPatternHasSeparator
return
}
if pos := strings.LastIndex(pattern, "*"); pos != -1 {
prefix, suffix = pattern[:pos], pattern[pos+1:]
} else {
prefix = pattern
}
return
}
TempFile 应用
创建临时文件
func main() {
tempFile, err := ioutil.TempFile("C:/GoWorks/src/go_task/hello-word/io/in", "temp*.jpg")
checkErr(err)
fmt.Println("已创建临时文件:目录", tempFile.Name())
}
TempDir方法
TempDir在dir目录中创建一个新的临时目录
通过采用模式并在末尾应用随机字符串来生成目录名。如果模式包含“”,则随机字符串将替换最后一个“”。TempDir返回新目录的名称。
源码和 TemFile类似
func TempDir(dir, pattern string) (name string, err error) {
// 1、如果dir是空字符串,TempDir将使用临时文件的默认目录(请参阅临时目录).
if dir == "" {
dir = os.TempDir()
}
// 2、根据 pattern 进行分割 ,分割出前缀和后缀
prefix, suffix, err := prefixAndSuffix(pattern)
if err != nil {
return
}
nconflict := 0
for i := 0; i < 10000; i++ {
try := filepath.Join(dir, prefix+nextRandom()+suffix)
err = os.Mkdir(try, 0700)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
rand = reseed()
randmu.Unlock()
}
continue
}
if os.IsNotExist(err) {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return "", err
}
}
if err == nil {
name = try
}
break
}
return
}
TemDir 应用
func main() {
tempDir, err := ioutil.TempDir("C:/GoWorks/src/go_task/hello-word/io/out", "temp")
checkErr(err)
fmt.Println("已创建文件夹,目录:" + tempDir)
// 写入数据到临时文件
_, err = tempFile.Write(pictureFile)
//关闭文件流,不关将无法删除
tempFile.Close()
}