暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Golang避坑系列 - 文件操作

Golang开发 2021-08-19
513


头条真实面试题

超大文件读取的方案?

     1. 流式处理

    2. 分片处理

    func ReadFile(filePath string, handle func(string)) error {
    f, err := os.Open(filePath)
    defer f.Close()
    if err != nil {
    return err
    }
    buf := bufio.NewReader(f)


    for {
    line, err := buf.ReadLine("\n")
    line = strings.TrimSpace(line)
    handle(line)
    if err != nil {
    if err == io.EOF{
    return nil
    }
    return err
    }
    return nil
    }
    }
      func ReadBigFile(fileName string, handle func([]byte)) error {
      f, err := os.Open(fileName)
      if err != nil {
      fmt.Println("can't opened this file")
      return err
      }
      defer f.Close()
      s := make([]byte, 4096)
      for {
      switch nr, err := f.Read(s[:]); true {
      case nr < 0:
      fmt.Fprintf(os.Stderr, "cat: error reading: %s\n", err.Error())
      os.Exit(1)
      case nr == 0: // EOF
      return nil
      case nr > 0:
      handle(s[0:nr])
      }
      }
      return nil
      }


      文件读取

      读写参数

      文件打开模式:

        const (
        O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
        O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
        O_RDWR int = syscall.O_RDWR // 读写模式打开文件
        O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
        O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
        O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在
        O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O
        O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
        )


        权限控制:

          r ——> 004
          w ——> 002
          x ——> 001


          os.File 封装了文件相关操作

            type File
            File代表一个打开的文件对象。


            func Create(name string) (file *File, err error)
            Create采用模式0666(任何人都可读写,不可执行)创建一个名为name的文件,如果文件已存在会截断它(为空文件)。如果成功,返回的文件对象可用于I/O;对应的文件描述符具有O_RDWR模式。如果出错,错误底层类型是*PathError。


            func Open(name string) (file *File, err error)
            Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。


            func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
            OpenFile是一个更一般性的文件打开函数,大多数调用者都应用OpenCreate代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。


            func NewFile(fd uintptr, name string) *File
            NewFile使用给出的Unix文件描述符和名称创建一个文件。


            func Pipe() (r *File, w *File, err error)
            Pipe返回一对关联的文件对象。从r的读取将返回写入w的数据。本函数会返回两个文件对象和可能的错误。


            func (f *File) Name() string
            Name方法返回(提供给Open/Create等方法的)文件名称。


            func (f *File) Stat() (fi FileInfo, err error)
            Stat返回描述文件f的FileInfo类型值。如果出错,错误底层类型是*PathError。


            func (f *File) Fd() uintptr
            Fd返回与文件f对应的整数类型的Unix文件描述符。


            func (f *File) Chdir() error
            Chdir将当前工作目录修改为f,f必须是一个目录。如果出错,错误底层类型是*PathError。


            func (f *File) Chmod(mode FileMode) error
            Chmod修改文件的模式。如果出错,错误底层类型是*PathError。


            func (f *File) Chown(uid, gid int) error
            Chown修改文件的用户ID和组ID。如果出错,错误底层类型是*PathError。


            func (f *File) Readdir(n int) (fi []FileInfo, err error)
            Readdir读取目录f的内容,返回一个有n个成员的[]FileInfo,这些FileInfo是被Lstat返回的,采用目录顺序。对本函数的下一次调用会返回上一次调用剩余未读取的内容的信息。
            如果n>0,Readdir函数会返回一个最多n个成员的切片。这时,如果Readdir返回一个空切片,它会返回一个非nil的错误说明原因。如果到达了目录f的结尾,返回值err会是io.EOF。
            如果n<=0,Readdir函数返回目录中剩余所有文件对象的FileInfo构成的切片。此时,如果Readdir调用成功(读取所有内容直到结尾),它会返回该切片和nil的错误值。如果在到达结尾前遇到错误,会返回之前成功读取的FileInfo构成的切片和该错误。


            func (f *File) Readdirnames(n int) (names []string, err error)
            Readdir读取目录f的内容,返回一个有n个成员的[]string,切片成员为目录中文件对象的名字,采用目录顺序。对本函数的下一次调用会返回上一次调用剩余未读取的内容的信息。
            如果n>0,Readdir函数会返回一个最多n个成员的切片。这时,如果Readdir返回一个空切片,它会返回一个非nil的错误说明原因。如果到达了目录f的结尾,返回值err会是io.EOF。
            如果n<=0,Readdir函数返回目录中剩余所有文件对象的名字构成的切片。此时,如果Readdir调用成功(读取所有内容直到结尾),它会返回该切片和nil的错误值。如果在到达结尾前遇到错误,会返回之前成功读取的名字构成的切片和该错误。


            func (f *File) Truncate(size int64) error
            Truncate改变文件的大小,它不会改变I/O的当前位置。如果截断文件,多出的部分就会被丢弃。如果出错,错误底层类型是*PathError。


            func (f *File) Read(b []byte) (n int, err error)
            Read方法从f中读取最多len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。


            func (f *File) ReadAt(b []byte, off int64) (n int, err error)
            ReadAt从指定的位置(相对于文件开始位置)读取len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。当n<len(b)时,本方法总是会返回错误;如果是因为到达文件结尾,返回值err会是io.EOF。


            func (f *File) Write(b []byte) (n int, err error)
            Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。


            func (f *File) WriteString(s string) (ret int, err error)
            WriteString类似Write,但接受一个字符串参数。


            func (f *File) WriteAt(b []byte, off int64) (n int, err error)
            WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。


            func (f *File) Seek(offset int64, whence int) (ret int64, err error)
            Seek设置下一次读/写的位置。offset为相对偏移量,而whence决定相对位置:0为相对文件开头,1为相对当前位置,2为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。


            func (f *File) Sync() (err error)
            Sync递交文件的当前内容进行稳定的存储。一般来说,这表示将文件系统的最近写入的数据在内存中的拷贝刷新到硬盘中稳定保存。


            func (f *File) Close() error
            Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。



            读取栗子

            os.Open || os.OpenFile
              package main


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


              func main() {
              // file, err := os.Open("/tmp/test")
              file, err := os.OpenFile("/tmp/test", os.O_CREATE|os.O_WRONLY, 0666)
              if err != nil {
              fmt.Println("Open file error: ", err)
              return
              }
              defer file.Close() //关闭文件


              reader := bufio.NewReader(file) //带缓冲区的读写
              for {
              str, err := reader.ReadString('\n') // 循环读取一行
              if err != nil {
              fmt.Println("read string failed, err: ", err)
              return
              }
              fmt.Println("read string is %s: ", str)
              }
              }


              readline

                package main


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


                func main() {
                file, err := os.Open("C:/test.log")
                if err != nil {
                fmt.Println(err)
                return
                }
                defer file.Close()
                reader := bufio.NewReader(file)
                var line []byte
                for {
                data, prefix, err := reader.ReadLine()
                if err == io.EOF {
                break
                }


                line = append(line, data...)
                if !prefix {
                fmt.Printf("data:%s\n", string(line))
                line = line[:]
                        }
                }
                }



                读取整个文件栗子

                "io/ioutil" 包实现了读取整个文件功能

                  package main


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


                  func main() {
                  fileName := "/tmp/test"


                  file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
                  if err != nil {
                  fmt.Println("Open file error: ", err)
                  return
                  }
                  defer file.Close()


                  buf, err := ioutil.ReadAll(file)
                  //buf, err := ioutil.ReadFile(fileName)
                  if err != nil {
                  fmt.Fprintf(os.Stderr, "File Error: %s\n", err)
                  return
                  }
                  fmt.Printf("%s\n", string(buf))
                  }



                  读取压缩文件栗子

                  "compress/*" 包实现压缩文件功能。

                  "compress/gzip" 包实现了gzip格式压缩文件的读写

                    package main


                    import (
                    "bufio"
                    "compress/gzip"
                    "fmt"
                    "os"
                    )


                    func main() {
                    fileName := "/tmp/test.log.gz"


                    var r *bufio.Reader


                    fi, err := os.Open(fileName)
                    if err != nil {
                    fmt.Println("error", err)
                    os.Exit(1)
                    }


                    fz, err := gzip.NewReader(fi)
                    if err != nil {
                    fmt.Println("error", err)
                    return
                    }


                    r = bufio.NewReader(fz)
                    for {
                    line, err := r.ReadString('\n')
                    if err != nil {
                    fmt.Println("Done reading file")
                    return
                    }
                    fmt.Println(line)
                    }
                    }



                    文件写入

                    file.WriteString || file.Write

                      package main


                      import (
                      "fmt"
                      "os"
                      )


                      func main() {
                      fileName := "/tmp/test_write"


                      file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0755)
                      if err != nil {
                      fmt.Println("error", err)
                      os.Exit(1)
                      }
                      defer file.Close()


                          fileString := "Good day."
                      file.Seek(0, 2) // 最后增加
                      file.WriteString(fileString)
                      //file.Write([]byte(fileString))
                      }


                      bufio.Writer.WriteString

                      带缓冲的写,最后要将缓冲中的数据写入下层的io.Writer接口(Flush方法)

                        package main


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


                        func main() {
                        fileName := "/tmp/test_write"


                        file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0755)
                        if err != nil {
                        fmt.Println("error", err)
                        os.Exit(1)
                        }
                        defer file.Close()


                        fileWrite := bufio.NewWriter(file)
                        fileString := "good.\n"
                        for i := 0; i < 10; i++ {
                        fileWrite.WriteString(fileString)
                        }
                        fileWrite.Flush()
                        }


                        拷贝文件栗子

                        从一个文件拷贝到另一个文件

                          package main


                          import (
                          "fmt"
                          "io"
                          "os"
                          )


                          func CopyFile(dstName, srcName string) (writeen int64, err error) {
                          src, err := os.Open(dstName)
                          if err != nil {
                          fmt.Println(err)
                          return
                          }
                          defer src.Close()


                          dst, err := os.OpenFile(srcName, os.O_CREATE|os.O_WRONLY, 0644)
                          if err != nil {
                          fmt.Println(err)
                          return
                          }
                          defer dst.Close()


                          return io.Copy(dst, src)
                          }


                          func main() {
                          CopyFile("/tmp/test", "/tmp/test_copy1")
                          fmt.Println("copy done.")
                          }


                          栗子

                          判断文件或文件夹是否存在

                            func PathExists(path string) (bool, error) {
                            /*
                            判断文件或文件夹是否存在
                            如果返回的错误为nil,说明文件或文件夹存在
                            如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
                            如果返回的错误为其它类型,则不确定是否在存在
                            */
                            _, err := os.Stat(path)
                            if err == nil {
                            return true, nil
                            }
                            if os.IsNotExist(err) {
                            return false, nil
                            }
                            return false, err
                            }


                            文章转载自Golang开发,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                            评论