美文网首页Go语言实践Go
Go语言文件处理(下)

Go语言文件处理(下)

作者: 帅气的昵称都有人用了 | 来源:发表于2019-05-07 15:07 被阅读0次

    读写文件

    我们在读写文件中最为常见的操作有:复制文件,编辑,跳转,替换等

    复制文件

    先来看复制文件是如何操作的:

    package main
    
    import (
      "io"
      "os"
      "log"
    
    )
    
    var (
      newFile *os.File
      fileInfo os.FileInfo
      err error
      path = "test/test2/"
      fileName = "demo"
      filePath = path + fileName
    )
    
    func main() {
      originalFile, err := os.Open(filePath)
      if err != nil {
        log.Fatal(err)
      }
      defer originalFile.Close()
      // 创建新的文件作为目标文件
      newFile, err := os.Create(filePath + "_copy")
      if err != nil {
        log.Fatal(err)
      }
      defer newFile.Close()
      // 从源文件中复制字节到目标文件
      bytesWritten, err := io.Copy(newFile, originalFile)
      if err != nil {
        log.Fatal(err)
      }
      log.Printf("The file has been copyed, size is %d bytes.", bytesWritten)
      // 将文件内容flush到硬盘中
      err = newFile.Sync()
      if err != nil {
        log.Fatal(err)
      }
    }
    /** The result is:
    2019/05/07 13:46:12 open test/test2/demo: no such file or directory
     */
    

    因为我这里并没有这个文件,因此返回的是no such file or directory的这行错误。
    在我们复制操作中,我们需要注意几个地方:

    Create函数执行后需要Close()函数来关闭并回收资源
    调用io包中的复制函数之后文件内容并没有真正保存在文件中,使用Sync()函数同步之后才真正的保存到硬盘中。

    跳转函数

    跳转到文件指定位置是复杂的文件操作基础,在Go语言中,我们使用的是Seek()函数:

    package main
    
    import (
      "fmt"
      "log"
      "os"
    )
    
    var (
      newFile *os.File
      err error
      path = "test/test2/"
      fileName = "demo"
      filePath = path + fileName
    )
    
    func main() {
      file, _ := os.Open(filePath)
      defer file.Close()
    
      // 偏离位置,可以是正数也可以是负数
      var offset int64 = 5
      // 用来计算offset的初始位置
      // 0 = 文件开始位置
      // 1 = 当前位置
      // 2 = 文件结尾处
      whence := 0
    
      newPosition, err := file.Seek(offset, whence)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Println("Move to the position 5: ", newPosition)
      // 从当前位置回退2字节
      newPosition, err = file.Seek(-2, 1)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Println("The new position is: ", newPosition)
      // 使用下面的方法得到当前位置
      newPosition, err = file.Seek(0, 1)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Println("The current position is: ", newPosition)
      // 转到文件开始处
      newPosition, err = file.Seek(0, 0)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Println("Move to the starting position: ", newPosition)
    }
    

    我们可以通过几个简单的例子发现,Seek()和我们鼠标的光标非常类似,就是可以移动到任意位置,然后进行相应的操作。

    写入函数

    写入函数有大致三种:

    // 写入byte类型的信息到文件
    func (file *FIle) Write(b []byte) (n int, err Error) 
    // 在制定位置开始写入byte类型的信息
    func (file *File) WriteAt(b []byte, off int64) (n int, err Error) 
    // 写入string信息到文件
    func (fiel *FIle) WriteString(s string) (ret int, err Error)
    

    还是来看一下实例:

    package main
    
    import (
      "log"
      "os"
    )
    
    var (
      newFile *os.File
      err error
      path = "test/test2/"
      fileName = "demo"
      filePath = path + fileName
    )
    
    func main() {
      file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
      if err != nil {
        log.Fatal(err)
      }
      defer file.Close()
      file.Write([]byte("Write byte. \r\n"))
      file.WriteString("Write string. \r\n")
      file, err = os.Open(filePath)
      if err != nil {
        log.Fatal(err)
      }
      buf := make([]byte, 1024)
      for {
        n, _ := file.Read(buf)
        if 0 == n {
          break
        }
        os.Stdout.Write(buf[:n])
      }
      file.Close()
    }
    

    权限控制

    查看文件、文件夹的权限实例如下:

    package main
    
    import (
      "log"
      "os"
    )
    
    var (
      newFile *os.File
      err error
      path = "test/test2/"
      fileName = "demo"
      filePath = path + fileName
    )
    
    func main() {
      file, err := os.OpenFile(filePath, os.O_WRONLY, 0666)
      if err != nil && os.IsPermission(err) {
        log.Fatal("Wrong: Cannot write.")
      } else if os.IsNotExist(err) {
        log.Fatal("File not exist.")
      } else {
        log.Fatal(err)
      }
      file.Close()
      file, err = os.OpenFile(filePath, os.O_RDONLY, 0666)
      if err != nil && os.IsPermission(err) {
        log.Fatal("Wrong: Cannot read.")
      } else if os.IsNotExist(err) {
        log.Fatal("File not exist.")
      } else {
        log.Fatal(err)
      }
      file.Close()
    }
    

    在Go语言中自然也可以改变文件的权限,初此之外还可以改变文件的拥有者以及时间戳等内容。修改命令与Linux中的chmodchown命令类似。

    func main() {
      err := os.Chmod(filePath, 0777)
      if err != nil {
        log.Println(err)
      }
      
      err = os.Chown(filePath, os.Getuid(), os.Getgid())
      if err != nil {
        log.Fatal(err)
      }
      
      // 查看文件信息
      fileInfo, err = os.Stat(filePath)
      if err != nil {
        if os.IsNotExist(err) {
          log.Fatal("File not exist.")
        }
        log.Fatal(err)
      }
      fmt.Println("The time is: ", fileInfo.ModTime())
      // 改变时间戳
      twoDaysFromNow := time.Now().Add(48 * time.Hour)
      lastAccessTime := twoDaysFromNow
      lastModifyTIme := twoDaysFromNow
      err = os.Chtimes(filePath, lastAccessTime, lastModifyTIme)
      if err != nil {
        log.Println(err)
      }
    }
    

    文件链接

    我们先来区分一下硬链接和软连接的区别:
    若一个 inode 号对应多个文件名,则称这些文件为硬链接。硬链接可以通过命令linkln进行创建。由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性:

    文件有相同的 inodedata block
    只能对已存在的文件进行创建;
    不能交叉文件系统进行硬链接的创建;
    不能对目录进行创建,只可对文件创建;
    删除一个硬链接文件并不影响其他有相同 inode 号的文件。

    而软链接与硬链接不同,若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接。
    而且软链接相比起硬链接会更简单一些:

    软链接有自己的文件属性及权限等;
    可对不存在的文件或目录创建软链接;
    软链接可交叉文件系统;
    软链接可对文件或目录创建;
    创建软链接时,链接计数 i_nlink 不会增加;
    删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。

    func main() {
      hardlink := filePath + "hl"
      err := os.Link(filePath, hardlink)
      if err != nil {
         log.Fatal(err)
      }
      fmt.Println("Create a hardlink.")
      
      softlink := filePath + "sl"
      err = os.Symlink(fileName, softlink)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Println("Create a softlink.")
      
      /** Lstat 返回一个文件信息,但当文件是一个软链接时,它返回软链接的信息,而不是引用的文件的信息*/
      // Symlink 在 Windows 中不工作
      fileInfo, err := os.Lstat(softlink)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Printf("Link information: %+v", fileInfo)
      
      // 改变软链接的拥有者不会影响原始文件
      err = os.Lchown(softlink, os.Getuid(), os.Getgid())
      if err != nil {
        log.Fatal(err)
      }
    }
    

    其中的os.Lstat()的函数名中可以看出这是一个关于软链接的函数,它是用于查看软链接自己的属性,使用os.Stat()函数会获取软链接指向的原文件信息。
    我们看一下os.Link()os.Symlink()的函数定义:

    func Symlink(oldname, newname string) error
    func Link(oldname, newname string) error
    

    我们发现两个函数的参数是完全一样的,但是,我们要注意,在Symlink()函数中,我们传递的是fileName,而在Link()函数中,我们传递的是filePath
    这是因为硬链接是从根目录开始创建的,而软链接则是根据文件的相对位置而创建的。

    相关文章

      网友评论

        本文标题:Go语言文件处理(下)

        本文链接:https://www.haomeiwen.com/subject/iyfeoqtx.html