网络人

GO语言上手实践:OS文件处理操作

Kwok: 2020-10-09 19:10:48 点击:12 评论: 0

GO语言标准库提供了强大的文件处理系统函数。我们借助导入os和io包里的方法就可以对系统里的文件进行各类的管理操作。

一、基本操作,打开、读取、关闭。

import (
	"fmt"
	"io"
	"os"
)
func main() {
	//file接收文件对象,实际上Open返回的是一个文件指针,也叫文件句柄
	file, err := os.Open("文件路径") //只读方式打开当前目录下的main.go文件
	if err != nil {
		fmt.Println("文件打出出错!, 原因:", err)
		return
	}
	defer file.Close() //当函数退出时关闭文件,要及时关闭file句柄,否则会出现内存泄露
	//下面通过一个for循环读取文件的全部内容
	var tmp = make([]byte, 2048) //make一个长度2048的byte切片临时变量备用
	var content []byte           //初始化一个conntent切片接收文件内容
	for {
		//Read方法从file中读取最多len(tmp)字节数据并写入tmp。它返回读取的字节数交给n和可能遇到的任何错误交给err。
		n, err := file.Read(tmp) //每次读取len(tmp)=2048个字节
		if err == io.EOF {       //文件终止标志是读取0个字节且返回值err为io.EOF
			fmt.Println("文件读完了")
			break //读完了退出for
		}
		if err != nil {
			fmt.Println("读取文件出错了, 原因:", err)
			break //出错了也退出for
		}
		content = append(content, tmp[:n]...)//将文件每次读取的内容追加到content切片里
	}
	fmt.Printf("读取了%d字节数据n", len(content)) //打印content的数据大小
	fmt.Println(string(content)) //content是一个byte切片,需要转为string输出文件内容
}
//为了防止文件忘记关闭,我们通常使用defer注册文件关闭语句。

读取直到获得EOF标记,因此我们为err == io.EOF添加了特定检查

二、带缓冲区的方式Reader读取文件

上面演示了GO语言直接读取文件,有时候文件非常的大,比较耗资源的时候我们就需要使用带缓冲的方式读取文件了。GO源代码里defaultBufSize默认大小是4096个字节。

接上面的代码继续演示:

reader := bufio.NewReader(file) //return NewReaderSize(rd, defaultBufSize)返回带缓冲的新Reader文件句柄和缓冲大小
for {
	str, err := reader.ReadString('n') //读到换行就结束
	if err == io.EOF {
		break //退出循环
	}
	fmt.Print(str)//输出读取到的内容
}
fmt.Println("文件读取结束...")

 三、小文件一次性读完

有的时候我们的文件不大,可以使用ioutil.ReadFile一次性把文件读取到内存中,这个包把打开、关闭封闭好了,我们直接使用即可:

/*
	源代码:func ReadFile(filename string)([]byte,error)
	下面代码里没有打开、关闭操作,直接封装到ReadFile内部的。
	这种方式只需要文件不大的情况,否则读取大文件的时候会非常占内存
*/
content, err := ioutil.ReadFile("文件路径") //content接收到byte切片,err接收文件错误
if err != nil {
	fmt.Println("读取文件出错:", err)
} else {
	fmt.Print(string(content))
}

四、文件创建与写入内容

上面介绍了文件打开的各种方式,下面将介绍一下文件打开后对文件的一些修改操作。上面使用的os.Open函数对文件打开,这是一种只读的安全模式,不会影响到文件,下面我们将会使用os.OpenFile函数,这是一个多参数函数,需要指定文件的操作模式,权限控制等(windows下无效)。

//OpenFilei源代码
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

注意:此时不能用os.Open()打开,而要用os.OpenFile()打开。

name指定一个文件路径,fiag 是文件的打开模式,可以组合使用“|”隔开。

file, err := os.OpenFile("文件路径", os.O_WRONLY|os.O_CREATE, 0666) //使用os.O_WRONLY只写模式并且os.O_CREATE如果文件不存在则创建,权限设置为0666(UNIX/LINUX下有效)
if err != nil {
	fmt.Println("文件打开失败:", err)
	return
}
defer file.Close() //及时关闭file句柄
file.Write([]byte("网络人的网址是:www.neter8.com")) //写入字节切片数据
file.WriteString("hello 网络人")                //直接写入字符串数据

下面附上打开模式的源代码。

const (
    O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
    O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
    O_RDWR   int = syscall.O_RDWR   // 读写模式打开文件
    O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
    O_CREATE int = syscall.O_CREAT  // 如果不存在将创建一个新文件
    O_EXCL   int = syscall.O_EXCL   // 和O_CREATE配合使用,文件必须不存在
    O_SYNC   int = syscall.O_SYNC   // 打开文件用于同步I/O
    O_TRUNC  int = syscall.O_TRUNC  // 如果可能,打开时清空文件
)

 使用缓冲bufio.NewWriter写入文件:

//接上面的代码defer file.Close()
writer :=bufio.NewWriter(file) //将writer设置为缓冲句柄
writer.WriteString("这是将要写入的字符串")//将内容写入到缓冲
writer.Flush()//将缓冲里的内容写入到磁盘文件中(真实写入)

上面是直接写的string数据,我们要使用ioutil.WriteFile写入切片byte数据:

str:="待写入的内容"
err := ioutil.WriteFile("文件路径", []byte(str), 0666)//将str转为切片方式写入到文件,如果出错将返回err
if err != nil {
   fmt.Println("文件写入出错,原因:", err)
   return
}

使用os.Stat()函数判断文件是否存在:

//os.Stat会返回一个文件的相关信息

fileInfo, err := os.Stat("文件路径")
if err != nil {
	fmt.Println("文件读取出错:",err)
}
fmt.Println(fileInfo.Name())    //文件名字.扩展名
fmt.Println(fileInfo.IsDir())   //false  判断是否是目录
fmt.Println(fileInfo.ModTime()) //2020-10-09 16:59:36.8832788 +0800 CST   文件的修改时间
fmt.Println(fileInfo.Size())    //3097  文件大小
fmt.Println(fileInfo.Mode())    // -rw-rw-rw-  读写属性
fmt.Println(fileInfo.Sys())     //&{32 {2160608986 30778972} {2160608986 30778972} {1375605524 30780234} 0 3097}

/*可以看到上面os.Stat的相关功能,如果这些功能我们可以自己写一个检测文件或者文件夹是否存在的函数*/

func PathExist(path string) (bool, error) {
	_, err := os.Stat(path)//只接收Stat返回的错误信息
	if err == nil {
		return true, nil//无错、文件存在
	}
	//读取出错了,文件可能存在,但无法读取。IsNotExist会根据错误原因返回一个真实存在与否的BOOL值
	if os.IsNotExist(err) {
		//返回一个false,就算文件存在。这个文件也可能不能使用。
		return false, nil	
}
	return false, err//文件不存在并返回实际错误
}
/*
IsExist(err)  // 返回false IsExist(nil) = false
!IsNotExist(err)  // 返回true go会认为文件存在不是error,所以当文件存在时,err为nil
!IsNotExist(nil) = !false = true
*/

FileInfo具有以下方法:

Name() string       //返回文件名
Size() int64        //返回文件的字节长度
Mode() FileMode     //文件模式位
ModTime() time.Time //修改时间
IsDir() bool        //是否是目录
Sys()   interface{} //底层数据源

五、文件的拷贝copy

GO语言内置了copy(dst writer,src Reader)(written int64,err error)函数,第一个参数传入目标路径的writer,第2个参数传入源文件的Reader,直接使用很麻烦,我们需要自己编写一个函数来实现输入源文件,目标路径的拷贝操作。

func CopyFile(srcName , dstName string) (written int64, err error) {
    src, err := os.Open(srcName)// 以读方式打开源文件得到Reader
    if err != nil {
        fmt.Printf("打开%s失败, 原因:%v.n", srcName, err)
        return
    }
    defer src.Close()//延时关闭源文件
    dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
    //以只写|不存在就创建的方式打开dstName目标文件,得到Writer
    if err != nil {
        fmt.Printf("打开或创建%s失败, 原因:%v.n", dstName, err)
        return
    }
    defer dst.Close()//延时关闭目标文件
    return io.Copy(dst, src) //调用io.Copy()传目录与源文件的句柄实现拷贝
}

 调用方法:

func main() {
    _, err := CopyFile("源文件路径.jpg", "目标路径.jpeg")
    if err != nil {
        fmt.Println("复制文件出错,原因:", err)
        return
    }
    fmt.Println("复制完成!")
}

io.Copy的内部实现是有缓冲的,在上面说到缓冲主要用于大文件的读写操作,所以我们在使用http.get()方法创建一个远程的请求后,后面可使用ioutil.WriteFile()等将请求内容直接写到文件中。下载的文件太大就会把机器的内存占满,造成内存溢出。所以我们就要使用Copy方法实现一个远程下载文件的函数:

func DownFile(url,outSrc string) {
    resp ,err := http.Get(url)//返回一个Reader(resp.Body)
    if err != nil {
        fmt.Fprint(os.Stderr ,"下载文件出错:" , err)
    }​
    defer resp.Body.Close()
    out, err := os.Create(outSrc)//创建一个文件,如果文件已存在,会将文件清空。
    wt :=bufio.NewWriter(out)//得到一个Writer
    defer out.Close()
    n, err :=io.Copy(wt, resp.Body)//按文件流缓冲复制
    if err != nil {
        panic(err)
    }
    wt.Flush()//刷新到磁盘上
}

六、文件删除与创建

删除文件很简单的哇~

//文件的创建,Create会根据传入的文件名创建文件,默认权限是0666
file,err:=os.Create("neter8.com")
if err!=nil{
   fmt.Println("文件创建失败,原因:",err)
}
defer file.Close()

//使用OpenFile清空或者创建一个文件,本文上面代码演示已有用到
f,err:=os.OpenFile("文件路径",os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
if err!=nil {
    fmt.Println("清空或者创建文件失败,原因:",err)
}

os.Mkdir("./benba",0666)//创建一级目录和设置权限
os.MkdirAll("./上级目录/下级目录",0655)//创建多级目录和并设置权限


err := os.Remove("文件路径") //删除目录或者文件,如果出错,会返回*PathError底层类型的错误。
if err != nil {
	fmt.Println("删除失败") 
} else {
	fmt.Println("删除成功")
}

os.RemoveAll("./benba")//删除当前目录及下载所有文件,它会尝试删除所有东西,除非遇到错误并返回。如果path指定的对象不存在,RemoveAll会返回nil而不返回错误。
本文地址:http://m.neter8.com/go/80.html
标签:GO文件操作

本站推荐阅读

热门点击文章