
本文共 5484 字,大约阅读时间需要 18 分钟。
文件操作
作为后端语言,Go
通过os
包提供了对文件的操作。
同时,使用bufio
与ioutil
也可以进行文件操作,三者均有自身的优劣势结合不同的需求使用不同的包来进行操作,将会让你的事半功倍。
文件分为普通文件和二进制文件,使用二进制文件时应该按照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
的切片,返回一个int
与error
,其中int
是已读取的字节数。
以下是基本使用:
package mainimport ( "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 mainimport ( "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 mainimport ( "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
切片,返回一个以读取字节数的int
和error
。
writeString()
则接收一个string
字符串,返回的内容同上。
package mainimport ( "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 mainimport ( "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 mainimport ( "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 mainimport ( "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!")}
发表评论
最新留言
关于作者
