Go 文件操作

文件操作

   作为后端语言,Go通过os包提供了对文件的操作。

   同时,使用bufioioutil也可以进行文件操作,三者均有自身的优劣势结合不同的需求使用不同的包来进行操作,将会让你的事半功倍。

   文件分为普通文件和二进制文件,使用二进制文件时应该按照byte进行读取。

OpenFile

   在os包中,有一个方法名为OpenFile(),它可以用指定模式来打开一个文件对象,并且会返回一个文件句柄。

func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	...
}

  

   name:文件路径

   flag:打开文件的模式

   perm:权限,一个八进制数。r(读)04,w(写)02,x(执行)01。

   下表是flag所支持的模式选项,如果要添加多种模式,使用|进行分割。

模式含义
os.O_WRONLY 只写
os.O_CREATE 创建文件
os.O_RDONLY 只读
os.O_RDWR 读写
os.O_TRUNC 清空
os.O_APPEND 追加

  

读取文件

内容准备

   下面有一个test.txt文件,里面存储了一些歌曲名称,专辑名称,作者名称等信息。

海阔天空 | Words & Music Final Live | Beyond
红豆 | 唱游 | 王菲
我可以抱你吗 | 重拾女人心 | 张惠妹
味道 | 味道 | 辛晓琪
独角戏 | 如果云知道 | 许茹芸

   打开文件时,可以使用os.Open()方法进行打开,它其实是基于os.OpenFile()的一个封装。

   默认是以只读的方式进行打开。返回一个文件句柄与错误对象。

func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

file.Read

   当获取到文件句柄后,可使用Read()方法对其进行读取。

func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

   这是一个文件句柄的方法,接收一个byte的切片,返回一个interror,其中int是已读取的字节数。

   以下是基本使用:

package main

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

func readTxt() {
	// 打开文件,只读方式
	file, err := os.Open("./file.txt")
	if err != nil {
		fmt.Println("打开文件出错:", err)
		return
	}
	defer file.Close() // 关闭文件

	var content []byte
	var temp = make([]byte, 128) // 创建容纳读取文件的一个变量,byte()类型

	for {
		// n 以读取的字节数
		n, err := file.Read(temp) // 使用文件句柄,开始读取。 读取的内容放入temp变量中
		if err == io.EOF {        // 抛出EOF错误代表文件读取完毕
			fmt.Println("文件读取完毕")
			break
		}
		if err != nil {
			fmt.Println("读取文件出错:", err)
			return
		}
		content = append(content, temp[:n]...) // 使用展开语法,将其内容进行展开
	}
	fmt.Println(string(content)) // []byte转换为string
}

func main() {
	readTxt()
}

bufio

   bufio是一个第三方的包,基于file做了一层封装,支持更多的功能。

   并且它具有缓冲区,能够让读写更加迅速。

   使用NewReader()方法,放入文件句柄,返回一个读取对象,然后再进行指定的方法读取该对象。

   提供的读取方法如下:

方法描述
Read 接收一个byte切片,返回以读取字节数
ReadSlice 返回一个切片,byte类型
ReadByte 返回byte字符串
ReadBytes 返回一个切片,byte类型
ReadRune 返回一个rune字符串
ReadString 返回一个string的字符串
ReadLine 以行进行读取,返回一个byte切片,并且会返回一个布尔值判断是否读取完毕

   其实用ReadString就能满足大部分需求了。

package main

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

func readTxt() {
	// 打开文件,只读方式
	file, err := os.Open("./file.txt")
	if err != nil {
		fmt.Println("打开文件出错:", err)
		return
	}
	defer file.Close() // 关闭文件

	var content string
	reader := bufio.NewReader(file) // 放入文件句柄,返回读取对象
	for {
		line, err := reader.ReadString('\n') // 以字符 \n 作为分割,即每次读取一行
		if err == io.EOF {
			fmt.Println("文件读取完毕")
			break
		}
		if err != nil {
			fmt.Println("读取文件时出错")
			return
		}
		content += line
	}
	fmt.Println(content) // 拼接内容
}

func main() {
	readTxt()
}

ioutil

   该包位于os/ioutil中,使用其ReadFile()可快速的读取完整个文件。

package main

import (
	"fmt"
	"io/ioutil"
)

func readTxt() {
	content, err := ioutil.ReadFile("./file.txt")
	if err != nil {
		fmt.Println("读取文件错误:", err)
		return
	}
	fmt.Println(string(content))
}

func main() {
	readTxt()
}

写入文件

write&writeString

   write()接收一个byte切片,返回一个以读取字节数的interror

   writeString()则接收一个string字符串,返回的内容同上。

package main

import (
	"fmt"
	"os"
)

func writeTxt() {
	// 创建文件,只写
	file, err := os.OpenFile("newFile.txt", os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("打开文件时出错", err)
		return
	}
	defer file.Close()

	// 写入文件
	str := "第一行内容\n"
	file.Write([]byte(str)) // 写入字节切片
	file.WriteString("第二行内容\n")
}

func main() {
	writeTxt()
}

bufio.NewWriter

   bufio具有缓冲区,所以读写更加迅速。

   下面是基本使用,其实关于写入对象的写入文件的方法还有很多,这里不再一一例举。

package main

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

func writeTxt() {
	// 创建文件,只写,清空
	file, err := os.OpenFile("newFile.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
	if err != nil {
		fmt.Println("打开文件时出错", err)
		return
	}
	defer file.Close()

	// 写入文件
	writer := bufio.NewWriter(file) // 放入文件句柄,获得一个写入对象
	writer.WriteString("一行新数据")
	writer.Flush() // 缓存刷新到磁盘
}

func main() {
	writeTxt()
}

ioutil.WriteFile

   该方法接收一个byte的切片,我们可以直接放入byte切片然后进行写入。

package main

import (
	"fmt"
	"io/ioutil"
)

func writeTxt() {
	str := "新数据哦"
	// 直接写入,清空之前的,会创建新文件
	err := ioutil.WriteFile("./newFile.txt", []byte(str), 0666)
	if err != nil {
		fmt.Println("write file failed, err:", err)
		return
	}
}

func main() {
	writeTxt()
}

指针偏移

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

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

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

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

   以下是一个操纵示例,将最后一行内容进行覆盖修改。

海阔天空 | Words & Music Final Live | Beyond
红豆 | 唱游 | 王菲
我可以抱你吗 | 重拾女人心 | 张惠妹
味道 | 味道 | 辛晓琪
独角戏 | 如果云知道 | 许茹芸

   操纵过程:

package main

import (
	"fmt"
	"os"
)

func writeTxt() {
	// 创建文件,只写
	file, err := os.OpenFile("file.txt", os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("打开文件时出错", err)
		return
	}
	defer file.Close()

	// 设置指针移动
	file.Seek(-40, 2)

	// 写入文件
	str := "全新歌曲 | 全新专辑 | 新歌手\n"
	file.Write([]byte(str)) // 写入字节切片
}

func main() {
	writeTxt()
}

   最后结果:

海阔天空 | Words & Music Final Live | Beyond
红豆 | 唱游 | 王菲
我可以抱你吗 | 重拾女人心 | 张惠妹
味道 | 味道 | 辛晓琪
全新歌曲 | 全新专辑 | 新歌手  // 进行覆盖

拷贝文件

   借助io.Copy()实现一个拷贝文件函数。

// CopyFile 拷贝文件函数
func CopyFile(dstName, srcName 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() {
	_, err := CopyFile("dst.txt", "src.txt")
	if err != nil {
		fmt.Println("copy file failed, err:", err)
		return
	}
	fmt.Println("copy done!")
}
posted @ 2020-10-09 12:30  云崖先生  阅读(710)  评论(0编辑  收藏  举报