Go语言基础之文件操作
1 文件的基本介绍
文件,对于我们并不陌生,文件是数据源(保存数据的地方)的一种。文件最主要的作用就是保存数据,它既可以保存一张图片,也可以保存视频、声音等。
1.1 输入流和输出流
文件程序中是以流的形式来操作的:
- 流:数据在数据源(文件)和程序(内存)之间经历的路径;
- 输入流:数据从数据源(文件)到程序(内存)的路径;
- 输出流:数据从程序(内存)到数据源(文件)的路径。
1.2 os包
os包中的File封装所有文件相关操作,其中File是一个结构体。下面列出File结构体的方法以及操作文件相关的函数:
type File
func Create(name string) (file *File, err error)
func Open(name string) (file *File, err error)
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
func NewFile(fd uintptr, name string) *File
func Pipe() (r *File, w *File, err error)
func (f *File) Name() string
func (f *File) Stat() (fi FileInfo, err error)
func (f *File) Fd() uintptr
func (f *File) Chdir() error
func (f *File) Chmod(mode FileMode) error
func (f *File) Chown(uid, gid int) error
func (f *File) Readdir(n int) (fi []FileInfo, err error)
func (f *File) Readdirnames(n int) (names []string, err error)
func (f *File) Truncate(size int64) error
func (f *File) Read(b []byte) (n int, err error)
func (f *File) ReadAt(b []byte, off int64) (n int, err error)
func (f *File) Write(b []byte) (n int, err error)
func (f *File) WriteString(s string) (ret int, err error)
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
func (f *File) Seek(offset int64, whence int) (ret int64, err error)
func (f *File) Sync() (err error)
func (f *File) Close() error
2 文件相关操作
2.1 打开和关闭文件
打开和关闭文件使用到的函数和方法:
func Open(name string) (file *File, err error)
func (f *File) Close() error
案例演示:
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("d:/test.txt")
if err != nil {
fmt.Println("open file err=", err)
}
fmt.Printf("file=%v", file) //file就是一个指针 *File
//关闭文件
err = file.Close()
if err != nil {
fmt.Println("close file err=", err)
}
}
2.2 读文件操作的三种方式
使用file.Read
方法读取文件
func (f *File) Read(b []byte) (n int, err error)
该方法接收一个字节切片,返回读取的字节数和可能的错误,读到文件末尾时会返回0
和io.EOF
。示例如下:
func main() {
// 只读方式打开当前目录下的main.go文件
file, err := os.Open("./main.go")
if err != nil {
fmt.Println("open file failed!, err:", err)
return
}
defer file.Close()
// 使用Read方法读取数据
var tmp = make([]byte, 128)
n, err := file.Read(tmp)
if err == io.EOF {
fmt.Println("文件读完了")
return
}
if err != nil {
fmt.Println("read file failed, err:", err)
return
}
fmt.Printf("读取了%d字节数据\n", n)
fmt.Println(string(tmp[:n]))
}
使用for循环读取文件中的所有数据:
func main() {
// 只读方式打开当前目录下的main.go文件
file, err := os.Open("./main.go")
if err != nil {
fmt.Println("open file failed!, err:", err)
return
}
defer file.Close()
// 循环读取文件
var content []byte
var tmp = make([]byte, 128)
for {
n, err := file.Read(tmp)
if err == io.EOF {
fmt.Println("文件读完了")
break
}
if err != nil {
fmt.Println("read file failed, err:", err)
return
}
content = append(content, tmp[:n]...)
}
fmt.Println(string(content))
}
带缓冲区的方式读写文件。使用到的方法及函数有:
os.Open()
file.Close()
bufio.NewReader()
reader.ReadString
代码演示:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("d:/test.txt")
if err != nil {
fmt.Println("open file err=", err)
}
// 当函数退出时,要及时关闭file句柄,否则会有内存泄露
defer file.Close()
// 创建一个 *Reader,是带缓冲的
reader := bufio.NewReader(file)
// 循环读取文件的内容
for {
str, err := reader.ReadString('\n') // 读到一个换行就结束
if err == io.EOF { // io.EOF表示文件的末尾
break
}
// 输出内容
fmt.Print(str)
}
fmt.Println("文件读取结束...")
}
一次性将整个文件读入到内存中(这种方式适用于文件不大的情况)。相关方法和函数:
ioutil.ReadFile()
代码演示:
package main
import (
"fmt"
"io/ioutil"
)
// 一次性将文件内容读取到内存中
func main() {
file := "d:/test.txt"
content, err := ioutil.ReadFile(file)
if err != nil {
fmt.Println("read file err=%v", err)
}
// 把读取到的内容显示到终端
// content的类型是[]byte
fmt.Printf("%v", string(content))
// 在这里不需要显示的打开和关闭文件,因为,文件的Open
// 和Close被封装到ReadFile函数内部
}
2.3 写文件操作
os.OpenFile()
函数能够以指定模式打开文件,从而实现文件写入相关功能。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {}
其中:
name
:要打开的文件名;
flag
:打开文件的模式,有以下模式:
模式 | 含义 |
---|---|
os.O_WRONLY |
只写 |
os.O_CREATE |
创建文件 |
os.O_RDONLY |
只读 |
os.O_RDWR |
读写 |
os.O_TRUNC |
清空 |
os.O_APPEND |
追加 |
prem
:文件权限,和Linux中文件的权限控制相同。
2.3.1 Write和WriteString
func main() {
file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("open file failed, err:", err)
return
}
defer file.Close()
str := "hello 世界"
file.Write([]byte(str)) //写入字节切片数据
file.WriteString("hello 小王子") //直接写入字符串数据
}
2.3.2 bufio.NewWriter
func main() {
file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("open file failed, err:", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
for i := 0; i < 10; i++ {
writer.WriteString("hello世界\n") //将数据先写入缓存
}
writer.Flush() //将缓存中的内容写入文件
}
2.3.3 ioutil.WriteFile
func main() {
str := "hello 沙河"
err := ioutil.WriteFile("./xx.txt", []byte(str), 0666)
if err != nil {
fmt.Println("write file failed, err:", err)
return
}
}
2.4 练习
- 借助
io.copy()
实现一个拷贝文件函数:
package main import (
"fmt"
"os"
"io"
"bufio"
)
//自己编写一个函数,接收两个文件路径 srcFileName dstFileName
func CopyFile(dstFileName string, srcFileName string) (written int64, err error{
srcFile, err := os.Open(srcFileName)
if err != nil {
fmt.Printf("open file err=%v\n", err)
}
defer srcFile.Close()
//通过 srcfile ,获取到 Reader
reader := bufio.NewReader(srcFile)
//打开 dstFileName
dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY | os.O_CREATE, 0666)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
//通过 dstFile, 获取到 Writer
writer := bufio.NewWriter(dstFile) defer dstFile.Close()
return io.Copy(writer, reader)
}
func main() {
//将 d:/flower.jpg 文件拷贝到 e:/abc.jpg
//调用 CopyFile 完成文件拷贝
srcFile := "d:/flower.jpg"
dstFile := "e:/abc.jpg"
_, err := CopyFile(dstFile, srcFile)
if err == nil {
fmt.Printf("拷贝完成\n")
} else {
fmt.Printf("拷贝错误 err=%v\n", err)
}
}
- 统计一个文件中含有的英文、数字、空格及其它字符数量:
package main import (
"fmt"
"os"
"io"
"bufio"
)
//定义一个结构体,用于保存统计结果
type CharCount struct {
ChCount int // 记录英文个数
NumCount int // 记录数字的个数
SpaceCount int // 记录空格的个数
OtherCount int // 记录其它字符的个数
}
func main() {
//思路: 打开一个文件, 创一个 Reader
//每读取一行,就去统计该行有多少个 英文、数字、空格和其他字符
//然后将结果保存到一个结构体
fileName := "e:/abc.txt"
file, err := os.Open(fileName)
if err != nil {
fmt.Printf("open file err=%v\n", err)
return
}
defer file.Close()
//定义个 CharCount 实例
var count CharCount
//创建一个 Reader
reader := bufio.NewReader(file)
//开始循环的读取 fileName 的内容
for {
str, err := reader.ReadString('\n')
if err == io.EOF { //读到文件末尾就退出
break
}
//为了兼容中文字符, 可以将 str 转成 []rune
str = []run(str)
//遍历 str ,进行统计
for _, v := range str {
switch {
case v >= 'a' && v <= 'z':
fallthrough //穿透
case v >= 'A' && v <= 'Z':
count.ChCount++
case v == ' ' || v == '\t':
count.SpaceCount++
case v >= '0' && v <= '9':
count.NumCount++
default :
count.OtherCount++
}
}
}
//输出统计的结果看看是否正确
fmt.Printf("字符的个数为=%v 数字的个数为=%v 空格的个数为=%v 其它字符个数=%v",
count.ChCount, count.NumCount, count.SpaceCount,count.OtherCount)
}
- 实现Linux平台
cat
命令功能
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
)
// cat命令实现
func cat(r *bufio.Reader) {
for {
buf, err := r.ReadBytes('\n') //注意是字符
if err == io.EOF {
break
}
fmt.Fprintf(os.Stdout, "%s", buf)
}
}
func main() {
flag.Parse() // 解析命令行参数
if flag.NArg() == 0 {
// 如果没有参数默认从标准输入读取内容
cat(bufio.NewReader(os.Stdin))
}
// 依次读取每个指定文件的内容并打印到终端
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))
}
}