GO-文件操作
三种文件操作比较
ioutil
bufio
os.File
当文件较小(KB 级别)时,ioutil > bufio > os。
当文件大小比较常规(MB 级别)时,三者差别不大,但 bufio 又是已经显现出来。
当文件较大(GB 级别)时,bufio > os > ioutil。
ioutil
ioutil.ReadFile读
//读
func Ioutil_read() {
file, _ := ioutil.ReadFile("./write.txt")
fmt.Println(string(file))
}
ioutil.WriteFile 写
//写
func Ioutil_write() {
ioutil.WriteFile("./write.txt",[]byte("aaaa\nbbb\ncccc\n啦啦啦啦"),0777)
}
ioutil.ReadAll 读
// 比较浪费内存,解决方法,百度查询
file,_ := os.Open("./write.txt")
b,_:=ioutil.ReadAll(file)
fmt.Println(string(b))
ioutil.ReadDir 查看路径下目录信息
func Ioutil_ReadDir() {
dir,_:=ioutil.ReadDir("./")
for _,file := range dir{
fmt.Println(file.Name()) //文件名字
fmt.Println(file.Size()) //文件大小
fmt.Println(file.IsDir()) //判断是否为目录
fmt.Println(file.Mode()) //查看读写权限-rw-r--r--
fmt.Println(file.ModTime()) //文件创建时间
}
}
ioutil.TempDir 创建临时目录
// 创建临时目录,以jeff开头,eg:jeff0123755
func Ioutil_Tempdir() {
path,_ := ioutil.TempDir("./","jeff")
fmt.Println(path) //返回临时目录路径
}
ioutil.TempFile 创建临时文件
// 创建临时文件,以jeff开头,eg:jeff067576
func Ioutil_file() {
//以读写模式打开该文件并返回os.File指针
path,_:=ioutil.TempFile("./","jeff")
fmt.Println(path) //返回os.File指针
}
os.file
方法
os.Create() //创建文件,如果有该文件则会清空文件
os.Open() // 只读方式打开文件
os.OpenFile(文件名,打开方式,打开权限)
Write() //写入
Read() //读取
os.OpenFile()
//os.O_WRONLY|os.O_CREATE|os.O_EXCL 如果已经存在,则失败
//os.O_WRONLY|os.O_CREATE 如果已经存在,从头覆盖写
//os.O_WRONLY|os.O_CREATE|os.O_APPEND 如果已经存在,在尾部追加写
OpenFile( )这个函数有三个参数,第一个参数表示打开文件的路径,第二个参数表示模式,常见的模式有
O_RDONLY(只读模式),O_WRONLY(只写模式),O_RDWR(可读可写模式),O_APPEND(追加模式)。
第三个参数,表示权限,取值范围(0-7)
表示如下:
0:没有任何权限
1:执行权限(如果是可执行文件,是可以运行的)
2:写权限
3:写权限与执行权限
4:读权限
5:读权限与执行权限
6:读权限与写权限
7:读权限,写权限,执行权限
fp,err := os.OpenFile("D:/a.txt",os.O_RDWR,6)
if err!=nil {
fmt.Println("打开文件失败")
}
fp.WriteString("hello")
fp.WriteAt([]byte("hello"),25)
defer fp.Close()
创建文件
将数据存储到文件之前,先要创建文件。GO语言中提供了一个Create( )函数专门创建文件.
该函数在创建文件时,首先会判断要创建的文件是否存在,如果不存在,则创建,如果存在,会先将文件中已有的数据清空。
同时,当文件创建成功后,该文件会默认的打开,所以不用在执行打开操作,可以直接向该文件中写入数据。
创建文件的步骤:
(1)导入“os”包,创建文件,读写文件的函数都在该包
(2)指定创建的文件存放路径以及文件名。
(3)执行Create( )函数,进行文件创建
(4)关闭文件
具体代码:
package main
import (
"fmt"
"os"
)
func main() {
//os.Create(文件名) 文件名 可以写绝对路径和相对路径
//返回值 文件指针 错误信息
fp,err := os.Create("./a.txt")
if err!=nil{
//文件创建失败
/*
1.路径不存在
2.文件权限
3.程序打开文件上限
*/
fmt.Println("文件创建失败")
return
}
//读写文件
defer fp.Close()
//关闭文件
//如果打开文件不关闭 造成内存的浪费 程序打开文件的上限
//fp.Close()
}
写入数据三种方式
第一种-WriteString( )函数
WriteString( )方法默认返回两个参数,第一个参数,指的是写入文件的数据长度,第二个参数记录的是错误信息
WriteString( )方法默认写到文件中的数据是不换行的。如果想换行,可以采用如下的方式:
//\n不会换行 原因 在windows文本文件中换行\r\n 回车 在linux中换行\n
fp.WriteString("hello world\r\n")
fp.WriteString("性感荷官在线发牌")
文件打开以后,可以向文件中写数据,可以使用WriteString( )方法。
//\反斜杠 转义字符
//在写路径时可以使用/正斜杠代替\反斜杠
fp,err := os.Create("D:/a.txt")
if err!=nil{
//文件创建失败
/*
1.路径不存在
2.文件权限
3.程序打开文件上限
*/
fmt.Println("文件创建失败")
return
}
//写文件
//\n不会换行 原因 在windows文本文件中换行\r\n 回车 在linux中换行\n
fp.WriteString("hello world\r\n")
fp.WriteString("性感荷官在线发牌")
defer fp.Close()
//关闭文件
//如果打开文件不关闭 造成内存的浪费 程序打开文件的上限
//fp.Close()
第二种-Write( )函数
在这里要注意的是,使用Write( )函数写数据时,参数为字节切片,所以需要将字符串转换成字节切片。该方法返回的也是写入文件数据的长度
fp,err := os.Create("D:/a.txt")
if err!=nil{
//文件创建失败
/*
1.路径不存在
2.文件权限
3.程序打开文件上限
*/
fmt.Println("文件创建失败")
return
}
//写操作
//slice := []byte{'h','e','l','l','o'}
//count,err1 := fp.Write(slice)
count,err1 := fp.Write([]byte("性感老王在线授课"))
if err1!=nil {
fmt.Println("写入文件失败")
return
}else {
fmt.Println(count)
}
defer fp.Close()
第三种-WriteAt( )函数
在指定的位置写入数据,以下程序中Seek( )函数返回值存储到变量n中,值为文件末尾的位置。WriteAt( )也返回的是写入的数据长度。
fp,err := os.Create("D:/a.txt")
if err!=nil{
//文件创建失败
/*
1.路径不存在
2.文件权限
3.程序打开文件上限
*/
fmt.Println("文件创建失败")
return
}
//写操作
//获取光标流位置'
//获取文件起始到结尾有多少个字符
//count,_:=fp.Seek(0,os.SEEK_END)
count,_:=fp.Seek(0,io.SeekEnd)
fmt.Println(count)
//指定位置写入
fp.WriteAt([]byte("hello world"),count)
fp.WriteAt([]byte("hahaha"),0)
fp.WriteAt([]byte("秀儿"),19)
defer fp.Close()
读取文件
Read 读取文件
如果文件已经存在,并且也已经有数据了,那么可以直接读取该文件中的内容。
读取文件的基本流程如下:
(1)打开要读取的文件
(2)对文件进行读取
(3)关闭文件
在向文件中写数据的时候,使用的是Write,那么读取文件中的数据,使用的是Read.
关于Read( )函数的使用如下:
package main
import (
"fmt"
"io"
"os"
)
func main() {
//打开文件
fp, err := os.Open("D:/a.txt")
if err != nil {
fmt.Println("err=", err)
return
}
buf := make([]byte, 1024*2) //2k大小
//n代表从文件读取内容的长度
n, err1 := fp.Read(buf)
if err1 != nil && err1 != io.EOF {
fmt.Println("err1=", err1)
return
}
fmt.Println("buf=", string(buf[:n]))
//关闭文件
defer fp.Close()
}
Open( )是打开文件,与OpenFile( )的区别是,Open( )只有读的权限
在使用Read( )函数读取文件中的内容时,需要一个切片类型,而定义切片时类型为字符数组,将文件中的内容保存在切片中,同时除了对其判断是否出错时以外,还要判断是否到文件末尾(这里需要导入io包)。
Read( )函数返回的是从文件中读取的数据的长度。最后,输出切片中存储的文件数据,注意,读取的是从最开始到整个数据长度,因为有可能存储到切片中的数据达不到切片的总长度(也是切片时2k,但是从文件中读取的数据有可能只有1k)
按行读取
上面我们是将文件的内容全部读取出来,然后存放在切片中,我们也可以每次只读取一行数据。
这需要用到bufio包中的ReadBytes函数。具体如下:
1:打开文件
fp, err := os.Open("D:/a.txt")
if err != nil {
fmt.Println("打开文件失败", err)
return
}
2:创建缓冲区
在使用ReadBytes( )函数读取数据时,需要用到缓冲区,所谓缓冲区就是存储数据的区域,也就是先将从文件中读取的数据存储在该区域内,然后在将区域中的数据取出来,写到磁盘上。提供缓冲区的原因是:
为了缓和 CPU 与磁盘设备之间速度不匹配矛盾。文件缓冲区是用以暂时存放读写期间的文件数据而在内存区预留的一定空间。
//创建文件缓冲区
r := bufio.NewReader(fp)
3:循环读取文件中的内容,直到文件末尾位置。
for {
//遇到'\n'结束读取,但是'\n'也读取进入
buf,err := r.ReadBytes('\n')
fmt.Println("buf = ",string(buf))
if err != nil {
if err == io.EOF {
break
}
fmt.Println("err=",err)
}
}
在使用ReadBytes( )函数时,传递的参数是‘\n’,表示遇到’\n’就结束,所以使用了死循环(每循环一次,读取一行数据),只有到文件末尾了,才退出整个循环。最后,将读取的数据打印出来,注意ReadBytes( )返回的是字节切片,所以在打印时要转换成字符串。
4:最后关闭文件
//关闭文件
defer fp.Close()
现在我们已经完成了文件的创建,读取,以及将数据保存到文件的操作,在对文件操作时,我们需要指定文件的路径。
关于路径,有两种情况:
第一:相对路径,所谓相对路径指的是文件相对于应用程序的路径。例如:上面我们一只使用的a.txt,这个文件,该文件存放的位置与可执行文件存储的路径是一样的。
第二:绝对路径:指的是通过给定的这个路径直接能在我的电脑中找到这个文件。例如:D:\Info.txt,
- 建议我们以后在开发中使用相对路径
文件操作案例
文件拷贝,将已有的文件复制一份,同时重新命名。
基本的思路:
(1)让用户输入要拷贝的文件的名称(源文件)以及目的文件的名称
(2)创建目的文件
(3)打开源文件,并且读取该文件中的内容
(4)将从源文件中读取的内容写到目的文件中。
var srcFileName string
var dstFileName string
fmt.Printf("请输入源文件名称:")
fmt.Scan(&srcFileName)
fmt.Println("请输入目的文件名称:")
fmt.Scan(&dstFileName)
if srcFileName == dstFileName {
fmt.Println("源文件和目的文件名字不能相同")
return
}
//只读方式打开源文件
sF,err1 := os.Open(srcFileName)
if err1 != nil {
fmt.Println("err1=",err1)
return
}
//新建目的文件
dF,err2 := os.Create(dstFileName)
if err2 != nil{
fmt.Println("err2=",err2)
return
}
//操作完毕,需要关闭文件
defer sF.Close()
defer dF.Close()
//核心处理,从源文件读取内容,往目的文件写,读多少写多少
buf := make([]byte,4*1024)//4k大小临时缓冲区
for{
n,err := sF.Read(buf)//从源文件读取内容,每次读取一部分
if err != nil{
fmt.Println("err=",err)
if err == io.EOF{//文件读取完毕
break
}
}
//往目的文件写,读多少写多少
dF.Write(buf[:n])
}
bufio包
https://studygolang.com/pkgdoc
Scanner对象
file, err := os.Open("./Atest/1.txt")
if err != nil {
return
}
sc := bufio.NewScanner(file) //创建Scanner对象
sc.Scan()
//扫描位置这一行是否有值,并位置移动到下一行
func (s *Scanner) Scan() bool
sc.Bytes()
//Bytes方法返回最近一次Scan调用生成的值,返回字节
func (s *Scanner) Bytes() []byte
sc.Text()
//Bytes方法返回最近一次Scan调用生成的值,返回字符串
func (s *Scanner) Text() string
bufio.Split
fd.Split(bufio.ScanLines) //一行一行读取
fd.Split(bufio.ScanWords) //空格或者\n分割读取
支持自己构建
//文件
func demo2() {
file, err := os.Open("./Atest/1.txt")
if err != nil {
return
}
fd := bufio.NewScanner(file)
fd.Split(split)
count := 0
for fd.Scan() {
count++
fmt.Println(fd.Text())
}
fmt.Println(count)
}
//字符串
func demo3() {
re := strings.NewReader("a:b:c:d")
fd := bufio.NewScanner(re)
fd.Split(split)
count := 0
for fd.Scan() {
count++
fmt.Println(fd.Text())
}
fmt.Println(count)
}
构建split
//分隔符改为:
func split(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, ':'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, dropCR(data[0:i]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
return 0, nil, nil
}
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
eg:文件一行一行读取
func demo1() {
file, err := os.Open("./Atest/1.txt")
if err != nil {
return
}
sc := bufio.NewScanner(file)
for sc.Scan() {
fmt.Println(string(sc.Bytes()))
}
}
ra.WriteString写入文件
//os.O_WRONLY|os.O_CREATE|os.O_EXCL 如果已经存在,则失败
//os.O_WRONLY|os.O_CREATE 如果已经存在,从头覆盖写
//os.O_WRONLY|os.O_CREATE|os.O_APPEND 如果已经存在,在尾部追加写
func demo1() {
file, err := os.OpenFile("./Atest/1.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 7)
if err != nil {
return
}
ra := bufio.NewWriter(file)
a, err := ra.WriteString("jeff")
ra.Flush()
}
写入文件--包--tools/tool_file
package main
import (
"fmt"
"github.com/xbitgo/core/tools/tool_file"
)
func main() {
u := "测试内容" //这里可以用模板
//写入文件
err := tool_file.WriteFile("./golang/test2.go", []byte(u))
if err != nil {
fmt.Println("err:", err.Error())
return
}
}
追加写入文件
// 写入日志
func writeLog(content string) {
filePath := "./log.txt"
// 打开文件(以追加模式)
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("打开文件失败:", err.Error())
return
}
// 写入内容
_, err = file.WriteString(content)
if err != nil {
fmt.Println("写入文件失败:", err.Error())
return
}
}
文件按照指定大小切割
// 文件切割
func FileSplit(content []byte) chan *FileObj {
contentLength := len(content)
chunkSize := 20 * 1024 * 1024 // 20MB
numChunks := (contentLength + chunkSize - 1) / chunkSize // 分片数量
fileChan := make(chan *FileObj, 1)
number := 1
for i := 0; i < numChunks; i++ {
start := i * chunkSize
end := (i + 1) * chunkSize
if end > contentLength {
end = contentLength
}
chunk := content[start:end]
fileObj := FileObj{
Number: number,
File: chunk,
}
fileChan <- &fileObj
number++
}
return fileChan
}
func main(){
fileResp, err := http.Get(req.Data.MaterialUrl)
if err != nil {
return nil, err
}
defer fileResp.Body.Close()
fileData, err := io.ReadAll(fileResp.Body)
fileChan := FileSplit(fileData)
}