Golang文件操作
文件操作
- 文件的概念
文件是数据源(保存数据的地方)的一种,文件最主要的作用就是保存数据. - 输入流和输出流
- 流:数据在数据源(文件)和程序(内存)之间经历的路径
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
- os.File
os.File 封装所有文件相关操作,File 是一个结构体
type File
type File struct {
// 内含隐藏或非导出字段
}
//File代表一个打开的文件对象。
- 文件的基本函数
- 新建文件
func Create(filename string) (file *File, err Error)
根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的。
func NewFile(fd uintptr, filename string) *File
根据文件描述符创建相应的文件,返回一个文件对象
- 打开文件
func Open(filename string) (file *File, err error)
Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。
该方法打开一个名称为filename的文件,但是是只读方式,内部实现其实调用了OpenFile。
func OpenFile(filename string, flag int, perm uint32) (file *File, err Error)
OpenFile主要用来指定参数(os.O_APPEND|os.O_CREATE|os.O_WRONLY)以及文件权限(0666)来打开文件,如果打开成功返回的文件对象将被用作I/O操作,flag是打开的方式,只读、读写等,perm是权限
3. 写文件
func (file *File) Write(b []byte) (n int, err Error)
写入长度为b字节切片到文件f中,返回写入字节号和错误信息。当n不等于len(b)时,将返回非空的err
func (file *File) WriteAt(b []byte, off int64) (n int, err Error)
在off偏移量出向文件f写入长度为b的字节
func (file *File) WriteString(s string) (ret int, err Error)
类似于Write方法,但是写入内容是字符串而不是字节切片
bufio.NewWriter(w io.Writer) *Writer
举例:
writer := bufio.NewWriter(file)
// 使用for循环写入内容
for i := 0; i < 3; i++ {
_, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error)
if err != nil {
fmt.Printf("文件写入出错:%s", err)
}
}
缓冲方式写入
err = ioutil.WriteFile(filePath, content, 0666)
if err != nil {
fmt.Printf("写入文件出错:%v", err)
return
}
使用ioutil.WriteFile方式写入文件,是将[]byte内容写入文件,如果content字符串中没有换行符的话,默认就不会有换行符,如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。
4. 读文件
func (file *File) Read(b []byte) (n int, err Error)
从文件对象中读取长度为b的字节,返回当前读到的字节数以及错误信息。因此使用该方法需要先初始化一个符合内容大小的空的字节列表。读取到文件的末尾时,该方法返回0,io.EOF
func (file *File) ReadAt(b []byte, off int64) (n int, err Error)
从文件的off偏移量开始读取长度为b的字节。返回读取到字节数以及错误信息。当读取到的字节数n小于想要读取字节的长度len(b)的时候,该方法将返回非空的error。当读到文件末尾时,err返回io.EOF
bufio.NewReader(rd io.Reader) *Reader
举例:
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n') // 读到一个换行符就结束
if err == io.EOF { // io.EOF表示文件的末尾
break
}
}
缓冲方式读取(默认缓冲区4096字节)
func ReadFile(filename string) ([]byte, error)
举例:
content, err := ioutil.ReadFile(filePath)
if err != nil {
// log.Fatal(err)
fmt.Printf("读取文件出错:%v", err)
}
使用 io/ioutil.ReadFile 方法一次性将文件读取到内存中
5. 判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用 os.Stat() 函数返回的错误值进行判断:
- 如果返回的错误为 nil,说明文件或文件夹存在;
- 如果返回的错误类型使用 os.IsNotExist() 判断为 true,说明文件或文件夹不存在;
3.如果返回的错误为其他类型,则不确定是否存在。
- 关闭文件
func (f *File) Close() error
Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。
7. 拷贝文件
func Copy(dst Writer, src Reader) (written int64, err error)
举例:
// 将 srcFilePath 拷贝到 dstFilePath
func CopyFile(dstFilePath string, srcFilePath string) (written int64, err error) {
// 打开srcFilePath
srcFile, err := os.Open(srcFilePath)
if err != nil {
fmt.Printf("打开文件出错:%s\n", err)
return
}
defer srcFile.Close()
// 通过 bufio/NewReader,传入 srcFile,获取到 reader
reader := bufio.NewReader(srcFile)
// 打开dstFilePath
dstFile, err := os.OpenFile(dstFilePath, os.O_WRONLY | os.O_CREATE, 0666)
if err != nil {
fmt.Printf("打开文件出错:%s\n", err)
return
}
defer dstFile.Close()
// 通过 bufio/NewWriter,传入 dstFile,获取到 writer
writer := bufio.NewWriter(dstFile)
return io.Copy(writer, reader)
}
func main() {
srcFilePath := "e:/a.mp4"
dstFilePath := "f:/b.mp4"
_, err := CopyFile(dstFilePath, srcFilePath)
if err != nil {
fmt.Printf("拷贝文件出错:%s", err)
}
fmt.Println("拷贝文件完成")
}
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
- 删除文件
func Remove(name string) Error
调用该函数就可以删除文件名为name的文件
9. 断点续传
package main
import (
"os"
"io"
"net/http"
"net/url"
"strconv"
)
func main() {
f, err := os.OpenFile("./chrome_installer.exe", os.O_RDWR|os.O_CREATE, 0666) //O_RDWR|O_CREATE,也就是文件不存在的情况下就建一个空文件,因为windows下还有BUG,如果使用这个O_CREATE,就会直接清空文件,所以windows不用这个标志,你自己事先建立好文件。
if err != nil {
panic(err)
}
stat, err := f.Stat() //获取文件状态
if err != nil {
panic(err)
}
f.Seek(stat.Size(), 0) //把文件指针指到文件末,当然你说为何不直接用 O_APPEND 模式打开,没错是可以。我这里只是试验。
//f.Seek(int64(os.O_APPEND), 0)
url1 := "http://dl.google.com/chrome/install/696.57/chrome_installer.exe"
var req http.Request
req.Method = "GET"
//req.UserAgent = UA //客户端信息字符串,不过我加上UserAgent,一直报错,不知道怎么回事,暂时不用它
req.Close = true
req.URL, err = url.Parse(url1)
if err != nil {
panic(err)
}
header := http.Header{}
header.Set("Range", "bytes="+strconv.FormatInt(stat.Size(), 10)+"-")
req.Header = header
resp, err := http.DefaultClient.Do(&req)
if err != nil {
panic(err)
}
written, err := io.Copy(f, resp.Body)
if err != nil {
panic(err)
}
println("written: ", written)
}
- 常用操作
- 遍历一个目录
func main() {
fmt.Println("请输入一个目录的路径:")
var path string
_, _ = fmt.Scan(&path)
// 打开目录
f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
if err != nil {
fmt.Printf("Open file failed:%s.\n", err)
return
}
defer f.Close()
// 读取目录
info, err := f.Readdir(-1) // -1 表示读取目录中所有目录项
// 遍历返回的切片
for _, fileInfo := range info {
if fileInfo.IsDir() {
fmt.Printf("%s是一个目录\n", fileInfo.Name())
} else {
fmt.Printf("%s是一个文件\n", fileInfo.Name())
}
}
}
- 统计一个文件中含有的英文、数字、空格以及其他字符数量。
// 定义一个结构体,用于保存统计结果
type CharCount struct {
AlphaCount int // 记录英文个数
NumCount int // 记录数字的个数
SpaceCount int // 记录空格的个数
OtherCount int // 记录其它字符的个数
}
func main() {
// 思路: 打开一个文件, 创一个 reader
// 每读取一行,就去统计该行有多少个 英文、数字、空格和其他字符
// 然后将结果保存到一个结构体
filePath := "e:/a.txt"
file, err := os.Open(filePath)
if err != nil {
fmt.Printf("打开文件出错:%s\n", err)
return
}
defer file.Close()
// 定义一个 CharCount 实例
var count CharCount
//创建一个Reader
reader := bufio.NewReader(file)
// 开始循环的读取文件的内容
for {
line, err := reader.ReadString('\n')
if err == io.EOF { // 读到文件末尾就退出
break
}
// 遍历每一行(line),进行统计
for _, v := range line {
switch {
case v >= 'a' && v <= 'z':
fallthrough // 穿透
case v >= 'A' && v <= 'Z':
count.AlphaCount++
case v >= '0' && v <= '9':
count.NumCount++
case v == ' ' || v == '\t':
count.SpaceCount++
default :
count.OtherCount++
}
}
}
// 输出统计的结果看看是否正确
fmt.Printf("字符的个数为:%v\n数字的个数为:%v\n空格的个数为:%v\n其它字符个数:%v\n",
count.AlphaCount, count.NumCount, count.SpaceCount, count.OtherCount)
}