前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2.Go语言之文件操作学习记录.md

2.Go语言之文件操作学习记录.md

作者头像
全栈工程师修炼指南
发布2022-09-29 16:17:09
4570
发布2022-09-29 16:17:09
举报
文章被收录于专栏:全栈工程师修炼之路

[TOC]

Go 语言文件操作

本章将主要介绍使用Go语言进行文件的读写相关操作。

Q: 什么是文件?

计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件。

0x00 基本操作

1.文件打开与关闭

描述: 我们可以采用os包中的Open()函数打开一个文件,返回一个*File和一个err。然后对得到的文件实例调用Close()函数就能够关闭文件。

示例演示

代码语言:javascript
复制
package main

import (
  "fmt"
  "os"
  "unsafe"
)

func main() {
  //1.以默认的方式打开demo.txt文件
  file, err := os.Open("./demo.txt")
  if err != nil {
    fmt.Println("open file failed!, err:", err)
    return
  }
  fmt.Printf("Type : %T, Pointer address: %p, Size of %d\n", file, file, unsafe.Sizeof(file))

  //2.关闭打开的文件(非常重要)
  //方式1
  // file.Close()
  //方式2
  defer file.Close()
}

执行结果: Type : *os.File, Pointer address: 0xc00000e028, Size of 8

Tips:为了防止文件忘记关闭,我们通常使用defer注册文件关闭语句。

2.文件读取操作

描述: 我们可以多同种方式实现文件读取。

方式1.file.Read()方法定义如下, 它接收一个字节切片,返回读取的字节数和可能的具体错误,读到文件末尾时会返回0io.EOF。 举个例子:

代码语言:javascript
复制
func (f *File) Read(b []byte) (n int, err error)

方式2.使用bufioNewReader方法进行行文件读取,它是在file的基础上封装了一层API,支持更多的功能。

代码语言:javascript
复制
func (b *Reader) ReadString(delim byte) (string, error) {
  full, frag, n, err := b.collectFragments(delim)
  // Allocate new buffer to hold the full pieces and the fragment.
  var buf strings.Builder
  buf.Grow(n)
  // Copy full pieces and fragment in.
  for _, fb := range full {
    buf.Write(fb)
  }
  buf.Write(frag)
  return buf.String(), err
}

方式3.使用io/ioutilReadFile方法读取整个文件, 其只需要将文件名作为参数传入。

代码语言:javascript
复制
func ReadFile(filename string) ([]byte, error) {
  return os.ReadFile(filename)
}

示例1.基础使用

代码语言:javascript
复制
func demo2() {
  // 1.文件打开与在函数退出时关闭打开的文件句柄。
  file, err := os.Open("./demo.txt")
  if err != nil {
    fmt.Println("open file failed!, err:", err)
    return
  }
  defer file.Close()

  // 2.使用Read方法进行读取数据。
  tmp := make([]byte, 128)     // 临时存放读取的数据空间
  count, err := file.Read(tmp) // 每次读128Byte存入tmp中并返回字节数
  if err == io.EOF {
    fmt.Println("END-文件已读完")
    return
  } else if err != nil {
    fmt.Println("read file failed, err:", err)
    return
  }
  // 3.输出读取的文件内容(默认最多只能读取128Byte)
  fmt.Printf("读取类型 %T,共读取了 %d 字节数据,读取的内容: %v \n", tmp, count, string(tmp[:count])) // 如不转换则返回字节编码
}

执行结果:

代码语言:javascript
复制
读取类型 []uint8,共读取了 72 字节数据,读取的内容: GoLang - 一门非常Nice的语言!
成年人的世界真的好难!

示例2.使用for循环读取文件中的所有数据。

代码语言:javascript
复制
func demo3() {
  // 1.文件打开与在函数退出时关闭打开的文件句柄。
  file, err := os.Open("./demo.txt")
  if err != nil {
    fmt.Println("open file failed!, err:", err)
    return
  }
  defer file.Close()

  // 2.使用Read方法进行读取数据。
  var content []byte
  tmp := make([]byte, 128) // 临时存放读取的数据空间
  // 循环读取文件
  for {
    count, err := file.Read(tmp) // 每次读128Byte存入tmp中并返回字节数
    if err == io.EOF {
      fmt.Println("#END-文件已读完---")
      break
    }
    if err != nil {
      fmt.Println("read file failed, err:", err)
      return
    }
    content = append(content, tmp[:count]...)
  }

  // 3.输出读取的文件内容
  fmt.Printf("读取的内容: %v \n", string(content)) // 如不转换则返回字节编码
}

执行结果:

代码语言:javascript
复制
#END-文件已读完---
读取的内容: GoLang - 一门非常Nice的语言!
成年人的世界真的好难!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!

示例3.bufio读取文件

代码语言:javascript
复制
func demo4() {
  // 1.文件打开与在函数退出时关闭打开的文件句柄。
  file, err := os.Open("./demo.txt")
  if err != nil {
    fmt.Println("open file failed!, err:", err)
    return
  }
  defer file.Close()

  // 2.使用bufio按行读取示例
  reader := bufio.NewReader(file)
  count := 0
  // 循环读取文件
  for {
    count++
    line, err := reader.ReadString('\n') // 按行执行
    if err == io.EOF {
      if len(line) != 0 {
        fmt.Printf("最后%d行 - %s", count, line)
      }
      fmt.Println("# 文件读取完成!")
      break
    }
    if err != nil {
      fmt.Println("read file failed, err:", err)
      return
    }
    fmt.Printf("第%d行 - %s", count, line)
  }
}

执行结果:

代码语言:javascript
复制
第1行 - GoLang - 一门非常Nice的语言!
第2行 - 成年人的世界真的好难!
第3行 - GoLang - 一门非常Nice的语言!
第4行 - GoLang - 一门非常Nice的语言!
第5行 - GoLang - 一门非常Nice的语言!
第6行 - GoLang - 一门非常Nice的语言!
第7行 - GoLang - 一门非常Nice的语言!
第8行 - GoLang - 一门非常Nice的语言!
第9行 - GoLang - 一门非常Nice的语言!

示例4.ioutil读取整个文件

代码语言:javascript
复制
func demo5() {
  content, err := ioutil.ReadFile("./demo.txt")
  if err != nil {
    fmt.Println("read file failed, err:", err)
    return
  }
  fmt.Println("整文读取:")
  fmt.Println(string(content))
}

执行结果:

代码语言:javascript
复制
整文读取:
GoLang - 一门非常Nice的语言!
成年人的世界真的好难!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!
GoLang - 一门非常Nice的语言!

3.文件写入操作

描述: 我们可以使用os.OpenFile()函数能够以指定模式打开文件,从而实现文件写入相关功能。

语法示例:

代码语言:javascript
复制
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
  ...
}

参数解析:

name:要打开的文件名

flag:打开文件的模式,模式有以下几种:

代码语言:javascript
复制
os.O_WRONLY 	#只写
os.O_CREATE 	#创建文件
os.O_RDONLY 	#只读
os.O_RDWR 	  #读写
os.O_TRUNC 	  #清空
os.O_APPEND 	#追加

perm: 文件权限为八进制数r(读)04,w(写)02,x(执行)01,主要在Linux系统上需要进行设置。

文件写入方式 针对于文件的写入我们可以采用如下几种方法。

方法0.Fprintf() 向文件中写入数据。

方法1.Write() 写入字节切片数据

代码语言:javascript
复制
func (f *File) Write(b []byte) (n int, err error) {}

方法2.WriteString() 写入字符串数据,可以看到其底层还是调用的Write方法,将字符串强转为字节切片。

代码语言:javascript
复制
func (f *File) WriteString(s string) (n int, err error) {
  return f.Write([]byte(s))
}

方法3.bufio.NewWriter() 返回一个文件句柄指针,我可以向该句柄的临时空间进行写入字符串,最后刷新缓存将其中数据写入到文件之中。

代码语言:javascript
复制
// NewWriter returns a new Writer whose buffer has the default size.
func NewWriter(w io.Writer) *Writer {
  return NewWriterSize(w, defaultBufSize)
}

方法4.ioutil.WriteFile() - 写入字符串数据

示例0.利用Fprintf进行字符串写入

代码语言:javascript
复制
func demo0() {
  // 1.标准输入
  fmt.Fprint(os.Stdin, "向标准输入写入内容")
  // 2.标准输出
  fmt.Fprint(os.Stdout, "向标准输出写入内容")
  // 3.写入文件打开
  file, err := os.OpenFile("./Fprint.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Println("open file erro", err)
    return
  }
  // 4.带换行写入
  fmt.Fprintln(file, "2.我是写入的字符串: 我喜爱网络安全技术")
  // 5.不带换行写入
  fmt.Fprintf(file, "1.我是写入的字符串:%v", "我喜爱网络安全技术")
}

执行结果:

代码语言:javascript
复制
DAP server listening at: 127.0.0.1:39039
向标准输出写入内容
➜  demo2 ls -alh Fprint.txt && cat Fprint.txt
-rw-r--r-- 1 weiyigeek weiyigeek 110 9月  17 01:40 Fprint.txt
2.我是写入的字符串: 我喜爱网络安全技术
1.我是写入的字符串:我喜爱网络安全技术%

示例1.写入字符切片或者字符串

代码语言:javascript
复制
func demo1() {
  // 1.打开需要写的文件,如果文件不存在就创建并晴空内容,对于该文件只写,其权限为0644.
  file, err := os.OpenFile("write.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Println("Open write file error:", err)
    return
  }
  defer file.Close()

  // 2.方式1以字节切片数据写入。
  var str0 = []byte{'a', 'b', 'c', ' '}
  str0 = append(str0, 'o', 'p', 'f', '\n')
  file.Write(str0)

  // 3.方式2以字符串数据写入。
  var str1 string = "Hello , Go - 我是要写入的字符串。"
  file.WriteString(str1)
}

执行结果:

代码语言:javascript
复制
➜  demo2 ls -alh write.txt && cat write.txt
-rw-r--r-- 1 weiyigeek weiyigeek 51 9月  16 09:05 write.txt
abc opf
Hello , Go - 我是要写入的字符串。%

示例2.利用bufio.NewWriter()方法写入字符串。

代码语言:javascript
复制
func demo2() {
  // 1.打开需要写的文件,如果文件不存在就创建并晴空内容,对于该文件只写,其权限为0644.
  file, err := os.OpenFile("write.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Println("Open write file error:", err)
    return
  }
  defer file.Close()

  // 2.实例化得到一个文件句柄的缓冲区
  writer := bufio.NewWriter(file)
  for i := 0; i < 10; i++ {
    // 3.将数据先写入缓存
    line := fmt.Sprintf("第%d行,hello go,bufio.NewWriter 写入!\n", i+1)
    writer.WriteString(line)
  }
  // 4.将缓存中的内容写入文件
  writer.Flush()
}

执行结果:

代码语言:javascript
复制
➜  demo2 ls -alh write.txt && cat write.txt
-rw-r--r-- 1 weiyigeek weiyigeek 451 9月  16 09:37 write.txt
第1行,hello go,bufio.NewWriter 写入!
第2行,hello go,bufio.NewWriter 写入!
第3行,hello go,bufio.NewWriter 写入!
第4行,hello go,bufio.NewWriter 写入!
第5行,hello go,bufio.NewWriter 写入!
第6行,hello go,bufio.NewWriter 写入!
第7行,hello go,bufio.NewWriter 写入!
第8行,hello go,bufio.NewWriter 写入!
第9行,hello go,bufio.NewWriter 写入!
第10行,hello go,bufio.NewWriter 写入!

示例3.从终端中输入信息并写入到文件之中。

代码语言:javascript
复制
func demo3() {
  var msg0, msg1, msg string
  // 方式1.终端获取字符串(以空格和换行结束)
  fmt.Println("# 请输入你要向文件写入的字符串:")
  fmt.Scanln(&msg0)

  // 方式2.采用NewReader获取终端输入(以指定字符为截止符)
  fmt.Print("# 请输入你要向文件写入的字符串:")
  reader := bufio.NewReader(os.Stdin)
  msg1, _ = reader.ReadString('\n')

  // 字符串拼接(推荐方式)
  var build strings.Builder
  build.WriteString(msg0)
  build.WriteString(msg1)
  msg = build.String()

  err := ioutil.WriteFile("./WriteFile.txt", []byte(msg), 0644)
  if err != nil {
    fmt.Println("open file faild:", err)
    return
  }
}

执行结果:

代码语言:javascript
复制
➜  demo2 go run filewrite.go  
# 请输入你要向文件写入的字符串:
WeiyiGeek
# 请输入你要向文件写入的字符串:This is a string
➜  demo2 ls -alh WriteFile.txt && cat WriteFile.txt 
-rw-r--r-- 1 weiyigeek weiyigeek 26 9月  17 02:14 WriteFile.txt
WeiyiGeekThis is a string

0x01 基本实践

文件内容展示

(1) 实现一个类似cat命令 使用文件操作相关知识,模拟实现linux平台cat命令的功能。

代码语言:javascript
复制
package main

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

// cat命令实现函数
func cat(r *bufio.Reader) {
  for {
    // 注意是字符,按照每行进行读取
    buf, err := r.ReadBytes('\n')
    if err == io.EOF {
      // 退出之前将已读到的内容输出
      fmt.Fprintf(os.Stdout, "%s", buf)
      break
    }
    fmt.Fprintf(os.Stdout, "%s", buf)
  }
}

func main() {
  // 解析命令行参数
  flag.Parse()
  // 如果没有参数默认从标准输入读取内容
  if flag.NArg() == 0 {
    fmt.Printf("未指定文件将从标准输入读取内容:")
    stdin := bufio.NewReader(os.Stdin)
    // 以换行符为结束
    buf, _ := stdin.ReadBytes('\n')
    // 将终端输入字符串向终端输出
    fmt.Fprintf(os.Stdout, "\n%s", buf)
  }
  // 依次读取每个指定文件(可多个文件)的内容并打印到终端
  for i := 0; i < flag.NArg(); i++ {
    f, err := os.Open(flag.Arg(i))
    if err != nil {
      fmt.Fprintf(os.Stdout, "reading from %s failed, err:%v\n", flag.Arg(i), err)
      continue
    }
    cat(bufio.NewReader(f))
  }
}

执行结果:

代码语言:javascript
复制
➜  demo3 hostname > {1..6}.txt
➜  demo3 go build -o cat    
➜  demo3 ./cat                  
未指定文件将从标准输入读取内容:This is os.stdio My name is WeiyiGeek
This is os.stdio My name is WeiyiGeek
➜  demo3 ./cat 1.txt 2.txt 3.txt 
ubuntu-pc
ubuntu-pc
ubuntu-pc

文件读取拷贝

(1) 实现文件的拷贝 描述: 我们可以借助io.Copy()实现一个拷贝文件函数。

代码语言:javascript
复制
package main

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

// CopyFile 拷贝文件函数
func CopyFile(srcName, dstName string) (written int64, err error) {
  // 以读方式打开源文件
  src, err := os.Open(srcName)
  if err != nil {
    fmt.Printf("open %s failed, err:%v.\n", srcName, err)
    return
  }
  defer src.Close()
  // 以写|创建的方式打开目标文件
  dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
  if err != nil {
    fmt.Printf("open %s failed, err:%v.\n", dstName, err)
    return
  }
  defer dst.Close()
  return io.Copy(dst, src) //调用io.Copy()拷贝内容
}
func main() {
  // 解析命令行参数
  flag.Parse()
  // 如果没有传递两个参数则显示使用说明
  if flag.NArg() != 2 {
    fmt.Println("Usage: ./copy src.file dst.file")
    return
  }
  // 从命令行中获取要操作的文件
  fmt.Printf("正在复制从 %s 文件内容,到目标文件 %s 中.....", flag.Arg(0), flag.Arg(1))
  _, err := CopyFile(flag.Arg(0), flag.Arg(1))
  if err != nil {
    fmt.Println("copy file failed, err:", err)
    return
  }
  fmt.Println("copy done!")
}

执行结果:

代码语言:javascript
复制
echo "Hello Weiyigeek, 你喜欢做什么" > hello.txt
➜  demo4 go build -o copy              
➜  demo4 ./copy          
Usage: ./copy src.file dst.file
➜  demo4 ./copy hello.txt copyhello.txt
正在复制从 hello.txt 文件内容,到目标文件 copyhello.txt 中.....copy done!
➜  demo4 cat copyhello.txt 
Hello Weiyigeek, 你喜欢做什么

文件定位插入

(1) 文件内容定位读取 描述:我们可以利用官方提供的os标准库中的type File struct{}结构体中的func (*File) Seek方法来进行,文件内容定位和光标插入等操作。

语法说明:

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

例如,文本文件 tmp.txt 其内容为如下,我们想对其weiyi后插入指定字符并对源文件进行覆盖:

代码语言:javascript
复制
tee tmp.txt <<'EOF'
weiyihobby
computer
EOF

代码演示:

代码语言:javascript
复制
package main

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

func testInsert() {
  // 源文件
  file, err := os.OpenFile("./tmp.txt", os.O_RDONLY, 0644)
  if err != nil {
    fmt.Printf("Open tmp.txt file failed, err: %v", err)
    return
  }

  // 临时文件
  filetmp, err := os.OpenFile("./tmp.tmp", os.O_CREATE|os.O_TRUNC|os.O_APPEND|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Printf("Open tmp.tmp file failed, err: %v", err)
    return
  }

  // 光标的开始位置
  file.Seek(0, 0)
  // 读取光标后的五个字符
  var ret [5]byte
  n, err := file.Read(ret[:]) // weiyigeek
  if err != nil {
    fmt.Printf("file.Read failed, err: %v", err)
    return
  }
  fmt.Printf("# read %d character: %s", n, ret[:n])
  filetmp.Write(ret[:n])

  // 写入要插入的字符串。
  str := "Geek"
  filetmp.WriteString(str)

  // 将剩下的字符串读取并写入到临时文件中
  var tmp [128]byte
  for {
    n, err := file.Read(tmp[:]) //每次读128Byte存入tmp中并返回字节数
    if err == io.EOF {
      filetmp.Write(tmp[:n])
      break
    } else if err != nil {
      fmt.Println("read file failed, err:", err)
      return
    }
    filetmp.Write(tmp[:n])
  }

  // 将临时文件重命名
  os.Rename("./tmp.tmp", "./tmp.txt")

  // 关闭源文件
  defer file.Close()
  defer filetmp.Close()
}

func main() {
  fmt.Println("# 指定文件内容插入演示")
  testInsert()
}

执行结果:

代码语言:javascript
复制
# 指定文件内容插入演示
# read 5 character: weiyi
$cat tmp.txt
weiyiGeekhobby
computer
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Go 语言文件操作
  • 0x00 基本操作
    • 1.文件打开与关闭
      • 2.文件读取操作
        • 3.文件写入操作
        • 0x01 基本实践
          • 文件内容展示
            • 文件读取拷贝
              • 文件定位插入
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档