Go语言中的文件操作以及IO操作

目录

1.学习内容

1.file文件操作

1.Fileinfo接口

2.权限

3.打开模式

4.File操作

3.I/O操作

1.io包读取文件

2.io包写文件

3.复制文件

4.断点续传

5.bufio包

6.ioutil包

7.遍历文件夹

2.总结


1.学习内容

1.file文件操作

首先,file类是在os包中的,封装了底层的文件描述符和相关信息

1.Fileinfo接口

接口中定义了File信息相关的方法。

type FileInfo interface {
    Name() string       // 文件的名字(不含扩展名)
    Size() int64        // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同
    Mode() FileMode     // 文件的模式位
    ModTime() time.Time // 文件的修改时间
    IsDir() bool        // 等价于Mode().IsDir()
    Sys() interface{}   // 底层数据来源(可以返回nil)
}
package main
​
import (
   "fmt"
   "os"
)
​
func main() {
   //Fileinfo:文件信息
   file1, err := os.Stat("HighPart/FILE/a.test")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println("%T", file1)
   //获取文件名
   fmt.Println(file1.Name())
   //文件大小
   fmt.Println(file1.Size())
   //是否是目录
   fmt.Println(file1.IsDir()) //IsDirectory
   //修改时间
   fmt.Println(file1.ModTime())
   //权限
   fmt.Println(file1.Mode())
   //底层数据来源
   fmt.Println(file1.Sys())
​
}

运行显示

2.权限

至于操作权限perm,除非创建文件时才需要指定,不需要创建新文件时可以将其设定为0。可以用常量,也可以用数字,具体含义和Unix系统一致。

3.打开模式

文本打开模式:

4.File操作

package main
​
import (
   "fmt"
   "os"
   "path"
   "path/filepath"
)
​
func main() {
   /*文件操作
   1.路径:
      相对路径:relative
      绝对路径:absolute
   .当前目录
   ..上一层
   */
   //路径
   filename1 := "D:\\gocode\\HighPart\\FILE\\a"
   filename2 := "HighPart/FILE/a"
   //filepath.IsAbs()用来判断一个文件是不是绝对路径
   fmt.Println(filepath.IsAbs(filename1))
   fmt.Println(filepath.IsAbs(filename2))
   //filepath.Abs()用来返回绝对路径
   fmt.Println(filepath.Abs(filename1))
   fmt.Println(filepath.Abs(filename2))
   fmt.Println("获取父目录:", path.Join("."))
   //创建一层文件
   err := os.Mkdir("D:\\gocode\\HighPart\\FILE\\b", os.ModePerm)
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println("文件夹创建成功")
   //多重文件夹创建
   os.MkdirAll("D:\\gocode\\HighPart\\FILE\\a",os.ModePerm)
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println("多重文件夹创建成功")
   //创建文件:Creat  0666
   file1, err := os.Create("D:\\gocode\\HighPart\\FILE\\b.txt")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(file1.Name())
   fmt.Println("文件创建成功")
   file2, err := os.Create(filename2) //创建相对路径的文件,以工程为参照的
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(file2)
   //打开文件:让当前的程序与指定的文件之间建立连接
   //简单打开
   file3, err := os.Open(filename1)
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(file3)
   //按照指定的权限打开
   file4, err := os.OpenFile(filename1, os.O_RDONLY|os.O_WRONLY, os.ModePerm)
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(file4)
   //关闭文件
   //也可以利用defer 进行延迟关闭
   //defer file3.Close()
   //defer file4.Close()
   file3.Close()
   file4.Close()
   //删除文件或文件夹
   //使用os.Remove()是目录的话只能删除空的
   //要不然可以使用os.RemoveAll(),需要慎重使用
   //还有就是如果使用代码进行删除的话,不经过回收站
   err = os.Remove("D:\\gocode\\HighPart\\FILE\\b")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println("删除文件成功")
}

3.I/O操作

IO操作就是输入输出操作,用于读写数据,有些语言也被叫做流操作,是指数据通信的通道

GOlang标准库对IO的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范

1.io包读取文件

io包提供了对I/O原语的基本接口。本包的基本任务是包装这些原语已有的实现(如os包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加了一些相关的原语的操作。

因为这些接口和原语是对底层实现完全不同的低水平操作的包装,除非得到其它方面的通知,客户端不应假设它们是并发执行安全的。

Reader接口

type Reader interface {
    Read(p []byte) (n int, err error)
}

Reader接口用于包装基本的读取方法。

Read方法读取len(p)字节数据写入p。它返回写入的字节数和遇到的任何错误。即使Read方法返回值n < len(p),本方法在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len(p)字节,Read按惯例会返回可以读取到的数据,而不是等待更多数据。

当Read在读取n > 0个字节后遭遇错误或者到达文件结尾时,会返回读取的字节数。它可能会在该次调用返回一个非nil的错误,或者在下一次调用时返回0和该错误。一个常见的例子,Reader接口会在输入流的结尾返回非0的字节数,返回值err == EOF或err == nil。但不管怎样,下一次Read调用必然返回(0, EOF)。调用者应该总是先处理读取的n > 0字节再处理错误值。这么做可以正确的处理发生在读取部分数据后的I/O错误,也能正确处理EOF事件。

如果Read的某个实现返回0字节数和nil错误值,表示被阻碍;调用者应该将这种情况视为未进行操作。

package main
​
import (
   "fmt"
   "io"
   "os"
)
​
func main() {
   /*
      读取文件:
      Reader接口
   */
   //打开文件
   fmt.Println("123456")
   filename1 := "D:\\gocode\\HighPart\\FILE\\a.test"
   file1, err := os.Open(filename1)
   if err != nil {
      fmt.Println(err)
      return
   }
   //关闭文件
   defer file1.Close()
   //读取数据
   b := make([]byte, 8)
   for { //多次进行读取文件
      n, err := file1.Read(b)
      if n == 0 || err == io.EOF {
         fmt.Println()
         break
      } else {
         fmt.Println(n)
         //这个会打印出来所有,所以我们需要转换成字符串
         fmt.Println(b)
         fmt.Println(string(b))
      }
   }
​
}

2.io包写文件

write接口

type Writer interface {
    Write(p []byte) (n int, err error)
}

Writer接口用于包装基本的写入方法。

Write方法len(p) 字节数据从p写入底层的数据流。它会返回写入的字节数(0 <= n <= len(p))和遇到的任何导致写入提取结束的错误。Write必须返回非nil的错误,如果它返回的 n < len(p)。Write不能修改切片p中的数据,即使临时修改也不行。

package main
​
import (
   "fmt"
   "os"
)
​
func main() {
   //写数据
   filename1 := "D:\\gocode\\HighPart\\FILE\\b.txt"
   //打开文件
   //这里我们需要选择模式打开文件
   file1, err := os.OpenFile(filename1, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
   if err != nil {
      fmt.Println(err)
      return
   }
   //文件关闭
   defer file1.Close()
​
   a := "西安邮电大学\n"
   _, err = file1.WriteString(a)
   if err != nil {
      fmt.Println("write", err)
      return
   }
   b := []byte{'a', 'e', 'b'}
   _, err = file1.Write(b)
   if err != nil {
      fmt.Println("write", err)
      return
   }
}

3.复制文件

思路:我们可以利用读写操作进行复制文件

所以我们是有三种复制方法

package main
​
import (
   "fmt"
   "io"
   "io/ioutil"
   "os"
)
​
func Copy(filename1, filename2 string) {
   file1, err := os.Open(filename1)
   if err != nil {
      fmt.Println("open", err)
      return
   }
   defer file1.Close()
   file2, err := os.OpenFile(filename2, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
   if err != nil {
      fmt.Println("open", err)
      return
   }
   defer file2.Close()
   a := make([]byte, 5)
   for {
      n, err := file1.Read(a)
      if n == 0 || err == io.EOF {
         fmt.Println("拷贝完毕")
         return
      } else if err != nil {
         fmt.Println("read", err)
      }
      _, err = file2.Write(a[:n])
      if err != nil {
         fmt.Println("write", err)
         return
      }
   }
}
​
// 利用函数直接进行拷贝
func Copy2(filename1, filename2 string) (int64, error) {
   file1, err := os.Open(filename1)
   if err != nil {
      fmt.Println("open", err)
      return 0, err
   }
   defer file1.Close()
   file2, err := os.OpenFile(filename2, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
   if err != nil {
      fmt.Println("open", err)
      return 0, err
   }
   defer file2.Close()
   return io.Copy(file2, file1)
}
​
// 利用ioutil包下面的函数
func Copy3(filename1, filename2 string) (int, error) {
   b, err := ioutil.ReadFile(filename1)
   if err != nil {
      return 0, err
   }
   err = ioutil.WriteFile(filename2, b, 0666)
   if err != nil {
      return 0, err
   }
   return len(b), nil
​
}
func main() {
   filename1 := "C:\\Users\\Lenovo\\Desktop\\OIP-C.jpg"
   filename2 := "D:\\gocode\\HighPart\\FILE\\k.jpg"
   //我们自己定义一个函数进行拷贝
   //Copy(filename1, filename2)
   //n, err := Copy2(filename1, filename2)
   n, err := Copy3(filename1, filename2)
   fmt.Println(n)
   fmt.Println(err)
}

接下来看看源码

func Copy

func Copy(dst Writer, src Reader) (written int64, err error)

将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。

对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。

func CopyN

func CopyN(dst Writer, src Reader, n int64) (written int64, err error)

从src拷贝n个字节数据到dst,直到在src上到达EOF或发生错误。返回复制的字节数和遇到的第一个错误。

只有err为nil时,written才会等于n。如果dst实现了ReaderFrom接口,本函数很调用它实现拷贝。

func ReadFile

func ReadFile(filename string) ([]byte, error)

ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。

func WriteFile

func WriteFile(filename string, data []byte, perm os.FileMode) error

函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。

4.断点续传

type Seeker

type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}

Seeker接口用于包装基本的移位方法。

Seek方法设定下一次读写的位置:偏移量为offset,校准点由whence确定:0表示相对于文件起始;1表示相对于当前位置;2表示相对于文件结尾。Seek方法返回新的位置以及可能遇到的错误。

移动到一个绝对偏移量为负数的位置会导致错误。移动到任何偏移量为正数的位置都是合法的,但其下一次I/O操作的具体行为则要看底层的实现。

package main
​
import (
   "fmt"
   "os"
)
​
func main() {
   //使用seek
   filename1 := "D:\\gocode\\HighPart\\FILE\\a.test"
   file1, err := os.OpenFile(filename1, os.O_RDWR|os.O_CREATE, 0666)
   if err != nil {
      fmt.Println("open", err)
      return
   }
   b := make([]byte, 10)
   file1.Read(b)
   fmt.Println(string(b))
   file1.Seek(4, os.SEEK_SET)
   file1.Read(b)
   fmt.Println(string(b))
}

package main
​
import (
   "fmt"
   "io"
   "os"
   "strconv"
   "strings"
)
​
func main() {
   //断电续传
   filename1 := "C:\\Users\\Lenovo\\Desktop\\OIP-C.jpg"
   filename := filename1[strings.LastIndex(filename1, "/")+1:]
   filename2 := filename + "q.txt"
   file1, err := os.Open(filename1)
   if err != nil {
      fmt.Println("open", err)
   }
   file2, err := os.OpenFile(filename2, os.O_CREATE|os.O_WRONLY, 0666)
   if err != nil {
      fmt.Println("open", err)
   }
   file3, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0666)
   if err != nil {
      fmt.Println("open", err)
   }
   defer file1.Close()
   defer file2.Close()
   defer file3.Close()
   //先读取零时文件中的数据,在seek
   file3.Seek(0, io.SeekStart)
   b := make([]byte, 1024)
   n1, err := file3.Read(b)
   if err != nil {
      fmt.Println("read")
      return
   }
   countstr := string(b[:n1])
   count, err := strconv.ParseInt(countstr, 10, 64)
   //if err != nil {
   // fmt.Println(err)
   // return
   //}
   fmt.Println(count)
   //设置读写的位置
   file1.Seek(count, io.SeekStart)
   file2.Seek(count, io.SeekStart)
   date := make([]byte, 1024)
   n2 := -1            //读取文件位置
   n3 := -1            //写出文件位置
   total := int(count) //读取的总量
   //复制文件
   for {
      n2, err = file1.Read(date)
      if err == io.EOF || n2 == 0 {
         fmt.Println("文件复制")
         break
      }
      n3, err = file2.Write(date[:n2])
      total += n3
      //将复制的总量,储存到文件中
      file3.Seek(0, io.SeekStart)
      file3.WriteString(strconv.Itoa(total))
      fmt.Println(total)
      //if total > 80 {
      // panic("断电")
      //}
   }
​
}

5.bufio包

bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

func (*Reader) ReadLine

func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)

ReadLine是一个低水平的行数据读取原语。大多数调用者应使用ReadBytes('\n')或ReadString('\n')代替,或者使用Scanner。

ReadLine尝试返回一行数据,不包括行尾标志的字节。如果行太长超过了缓冲,返回值isPrefix会被设为true,并返回行的前面一部分。该行剩下的部分将在之后的调用中返回。返回值isPrefix会在返回该行最后一个片段时才设为false。返回切片是缓冲的子切片,只在下一次读取操作之前有效。ReadLine要么返回一个非nil的line,要么返回一个非nil的err,两个返回值至少一个非nil。

返回的文本不包含行尾的标志字节("\r\n"或"\n")。如果输入流结束时没有行尾标志字节,方法不会出错,也不会指出这一情况。在调用ReadLine之后调用UnreadByte会总是吐出最后一个读取的字节(很可能是该行的行尾标志字节),即使该字节不是ReadLine返回值的一部分。

func (*Reader) ReadBytes

func (b *Reader) ReadBytes(delim byte) (line []byte, err error)

ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

func (*Reader) ReadString

func (b *Reader) ReadString(delim byte) (line string, err error)

ReadString读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的字符串。如果ReadString方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadString方法返回的切片不以delim结尾时,会返回一个非nil的错误。

package pake
​
import (
   "bufio"
   "fmt"
   "os"
)
​
func Demo01() {
   //使用bufio:高效读写
   filename01 := "D:\\gocode\\HighPart\\pake\\a"
   file, err := os.Open(filename01)
   if err != nil {
      fmt.Println(err)
      return
   }
   defer file.Close()
   //创建Reader对象
   //b1 := bufio.NewReader(file)
   高效读取Read()
   //p := make([]byte, 1024)
   //n1, err := b1.Read(p)
   //fmt.Println(n1)
   //fmt.Println(string(p[:n1]))
   //ReadLine()
   //for {
   // data, _, err := b1.ReadLine()
   // if err == io.EOF {
   //    fmt.Println("打印完毕")
   //    break
   // }
   // if err != nil {
   //    fmt.Println("read()", err)
   //    return
   // }
   // //fmt.Println(flag)
   // fmt.Println(string(data))
   //}
   //data, flag, err := b1.ReadLine()
   //fmt.Println(flag)
   //fmt.Println(err)
   //fmt.Println(data)
   //fmt.Println(string(data))
   //ReadString()
   //s1, err := b1.ReadString('\n')
   //fmt.Println(err)
   //fmt.Println(s1)
   //s1, err = b1.ReadString('\n')
   //fmt.Println(err)
   //fmt.Println(s1)
   //s1, err = b1.ReadString('\n')
   //fmt.Println(err)
   //fmt.Println(s1)
   //for {
   // s1, err := b1.ReadString('\n')
   // if err == io.EOF {
   //    break
   // }
   // fmt.Println(s1)
   //}
   //ReadBytes()
   //data, err := b1.ReadBytes('\n')
   //fmt.Println(err)
   //fmt.Println(string(data))
   //进行带有空格的读
   s2 := ""
   b2 := bufio.NewReader(os.Stdin)
   s2, _ = b2.ReadString('\n')
   fmt.Println(s2)
}

func (*Writer) Write

func (b *Writer) Write(p []byte) (nn int, err error)

Write将p的内容写入缓冲。返回写入的字节数。如果返回值nn < len(p),还会返回一个错误说明原因。

package pake
​
import (
   "bufio"
   "fmt"
   "os"
)
​
func Demo02() {
   //写文件
   filename01 := "D:\\gocode\\HighPart\\pake\\b"
   file01, err := os.OpenFile(filename01, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
   if err != nil {
      fmt.Println(err)
      return
   }
   defer file01.Close()
   w1 := bufio.NewWriter(file01)
   //n, err := w1.WriteString("掌握噶如果")
   //fmt.Println(err)
   //fmt.Println(n)
   //w1.Flush() //刷新缓存区
   for i := 1; i <= 10; i++ {
      w1.WriteString(fmt.Sprintf("%d,乐此不起\n", i))
   }
   w1.Flush()
}

6.ioutil包

func ReadAll

func ReadAll(r io.Reader) ([]byte, error)

ReadAll从r读取数据直到EOF或遇到error,返回读取的数据和遇到的错误。成功的调用返回的err为nil而非EOF。因为本函数定义为读取r直到EOF,它不会将读取返回的EOF视为应报告的错误。

func ReadFile

func ReadFile(filename string) ([]byte, error)

ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。

func WriteFile

func WriteFile(filename string, data []byte, perm os.FileMode) error

函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。

func ReadDir

func ReadDir(dirname string) ([]os.FileInfo, error)

返回dirname指定的目录的目录信息的有序列表。

func TempDir

func TempDir(dir, prefix string) (name string, err error)

在dir目录里创建一个新的、使用prfix作为前缀的临时文件夹,并返回文件夹的路径。如果dir是空字符串,TempDir使用默认用于临时文件的目录(参见os.TempDir函数)。 不同程序同时调用该函数会创建不同的临时目录,调用本函数的程序有责任在不需要临时文件夹时摧毁它。

func TempFile

func TempFile(dir, prefix string) (f *os.File, err error)

在dir目录下创建一个新的、使用prefix为前缀的临时文件,以读写模式打开该文件并返回os.File指针。如果dir是空字符串,TempFile使用默认用于临时文件的目录(参见os.TempDir函数)。不同程序同时调用该函数会创建不同的临时文件,调用本函数的程序有责任在不需要临时文件时摧毁它。

package pake
​
func Demo04() {
   //ioutil包
   //读取文件中的所有数据,不需要开关文件
   //filename := "D:\\gocode\\HighPart\\pake\\b"
   //data, err := ioutil.ReadFile(filename)
   //fmt.Println(err)
   //fmt.Println(data)
   //fmt.Println(string(data))
   //写文件,没有的话会自动创建,有的话会清空
   //filename := "D:\\gocode\\HighPart\\pake\\b"
   //s1 := "刚发给甲方返回,官方电话"
   //err := ioutil.WriteFile(filename, []byte(s1), 0600)
   //fmt.Println(err)
   //ReadAll()
   //s2 := "fggf 带帅哥人噶韩国"
   //r1 := strings.NewReader(s2)
   //data, err := ioutil.ReadAll(r1)
   //fmt.Println(err)
   //fmt.Println(data)
   //fmt.Println(string(data))
   //读取一层文件的目录
   //dirName := "D:\\gocode\\HighPart\\pake"
   //filename, err := ioutil.ReadDir(dirName)
   //fmt.Println(err)
   fmt.Println(filename)
   //fmt.Printf("%T\n", filename)
   //for i := 0; i < len(filename); i++ {
   // fmt.Printf("%d %s %t\n", i, filename[i].Name(), filename[i].IsDir())
   //}
   //创建临时文件和临时目录
   //ioutil.TempDir()
   //ioutil.ReadFile()
   //需要进行删除
}

7.遍历文件夹

之前我们看了一下可以打印文件下目录的函数ioutil.ReadDir(),但是这个函数只能打印一层的文件,所以我们可以利用递归,进行多层的打印,下面就是算法的实现。

package pake

import (
   "fmt"
   "io/ioutil"
   "log"
)

func Demo03() {
   dirname := "D:/gocode/HighPart"
   listFile(dirname, 0)
}
func listFile(dirname string, level int) {
   //level用来记录当前递归的层次,生成带有层次感的空格
   s := "|--"
   for i := 0; i < level; i++ {
      s = "|  " + s
   }
   filename, err := ioutil.ReadDir(dirname)
   if err != nil {
      log.Fatal(err)
   }
   for _, fi := range filename {
      filename1 := dirname + "/" + fi.Name()
      fmt.Printf("%s%s\n", s, filename1)
      if fi.IsDir() {
         listFile(filename1, level+1)
      }
   }
}

2.总结

一周挤出来点时间,对一部分内容进行系统的总结,对学习有着很大的提升。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值