Go-文件目录 *** 作分类详解(创建、打开、关闭、读取、写入、判断等)

Go-文件目录 *** 作分类详解(创建、打开、关闭、读取、写入、判断等),第1张

目录

创建

创建文件(可读写)

存在清空

更多模式

创建目录

单个目录

递归创建多个目录

写入文件

写入字节

写入字符串

指定位置写入

 带缓冲的写入

快速写入

读取

读取文件

读取最多n字节

读取最少n字节

正好读取缓冲区长度的字节

使用缓冲区

使用scanner

快速读取整个文件

文件信息

读取目录

单层目录

递归读取所有目录

判断文件或目录是否存在

总结

参考


go的文件目录相关 *** 作散布在os、bufio、io包中,本篇文章分类进行总结,有余力的朋友可以再看一遍三个标准库。最后,博主选取了几个函数发布在了gitee上。

创建 创建文件(可读写) 存在清空

func Create(name string) (file *File, err error)

Create采用模式0666(任何人都可读写,不可执行)创建一个名为name的文件,如果文件已存在会截断它(即清空文件)。如果成功,返回的文件对象可用于I/O;对应的文件描述符具有O_RDWR模式。如果出错,错误底层类型是*PathError。

func CreateFile(filePath string) error {
	_,err := os.Create(filePath)
	if err != nil{
		log.Fatal("这是什么玩意:",filePath)
		return err
	}
	return nil
}
	_ = CreateFile("./file.txt")
	_ = CreateFile(".//?/file.txt")

结果

2021/05/06 16:43:16 这是什么玩意:.//?/file.txt
exit status 1

更多模式

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

OpenFile是一个更一般性的文件打开函数,它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果 *** 作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。 

func OpenCreate(filePath string,perm os.FileMode) error {
	_,err := os.OpenFile(filePath,os.O_CREATE,perm)
	if err != nil{
		log.Fatal("这是什么玩意:",filePath)
		return err
	}
	return nil
}
	_ = OpenCreate("./file.txt",0666)
	_ = OpenCreate(".//?/file.txt",0666)

结果  

2021/05/06 16:50:41 这是什么玩意:.//?/file.txt
exit status 1

创建目录 单个目录

func Mkdir(name string, perm FileMode) error

Mkdir使用指定的权限和名称创建一个目录。如果出错,会返回*PathError底层类型的错误。 

//创建单个目录
func MkDir(dirPath string,perm os.FileMode) error {
	err := os.Mkdir(dirPath,perm)
	if err != nil{
		log.Fatal("目录已存在或不能按此目录创建:",dirPath)
		return err
	}
	return nil
}
	_ = MkDir("./lady", 0666)
	_ = MkDir("./lady", 0666)

结果  

2021/05/06 17:06:43 目录已存在或不能按此目录创建:./lady
exit status 1

递归创建多个目录

func MkdirAll(path string, perm FileMode) error

MkdirAll使用指定的权限和名称创建一个目录,包括任何必要的上级目录,并返回nil,否则返回错误。权限位perm会应用在每一个被本函数创建的目录上。如果path指定了一个已经存在的目录,MkdirAll不做任何 *** 作并返回nil。 

func MkDirs(dirPath string,perm os.FileMode) error {
	err := os.MkdirAll(dirPath,perm)
	if err != nil{
		log.Fatal("这是什么玩意:",dirPath)
		return err
	}
	return nil
}
	_ = MkDirs("./lady/killer/9", 0666)
	_ = MkDirs("./lady/killer、?9", 0666)

结果  

2021/05/06 17:08:17 这是什么玩意:./lady/killer、?9
exit status 1

写入文件 写入字节

func (f *File) Write(b []byte) (n int, err error)

Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。 

func WriteBytes(filePath string,content []byte) error {
	_ = OpenCreate(filePath,0666)
	file,err := os.OpenFile(filePath,os.O_WRONLY,0666)
	if err != nil{
		return err
	}
	defer file.Close()
	_,err = file.Write(content)
	if err != nil{
		return err
	}
	return nil
}
	_ = WriteBytes("./file.txt", []byte("lady_killer9"))

结果 

写入字符串

func (f *File) WriteString(s string) (ret int, err error)

WriteString类似Write,但接受一个字符串参数。

func WriteStrings(filePath string,content string) error {
	_ = OpenCreate(filePath,0666)
	file,err := os.OpenFile(filePath,os.O_WRONLY,0666)
	if err != nil{
		return err
	}
	defer file.Close()
	_,err = file.WriteString(content)
	if err != nil{
		return err
	}
	return nil
}

结果

注意:或许你以为是清空,或是追加,但都不是,它只是从头覆盖

OpenFile的第二个参数如下

// 必须指定O_RDONLY、O_WRONLY或O_RDWR中的一个。
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  // 清空

清空后写

file,err := os.OpenFile(filePath,os.O_WRONLY|os.O_TRUNC,0666)

追加写

file,err := os.OpenFile(filePath,os.O_WRONLY|os.O_APPEND,0666)
指定位置写入

func (f *File) WriteAt(b []byte, off int64) (n int, err error)

WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。

func WriteAt(filePath string,content []byte, at int64) error {
	_ = OpenCreate(filePath,0666)
	file,err := os.OpenFile(filePath,os.O_WRONLY,0666)
	if err != nil{
		return err
	}
	defer file.Close()
	_,err = file.WriteAt(content,at)
	if err != nil{
		return err
	}
	return nil
}
	_ = WriteAt("./file.txt",[]byte("writeat"),10)
	_ = WriteAt("./file.txt",[]byte("writeat"),20)

 带缓冲的写入
func WriteWithBuffer(filePath string)  {
	file,err := os.OpenFile(filePath,os.O_CREATE|os.O_WRONLY,0666)
	if err != nil{
		fmt.Println(err)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	defer writer.Flush()
	for i:=0;i<10;i++{
		_, _ = writer.WriteString("lady_killer" + string(i) + "\r\n")
	}
}
	WriteWithBuffer("./file.txt",[]string{"lady_killer7","lady_killer8","lady_killer9"})

结果

快速写入

func WriteFile(name string, data []byte, perm FileMode) error

WriteFile将数据写入命名文件,必要时创建该文件。如果文件不存在,WriteFile使用perm权限创建它(在umask之前);否则WriteFile会在写入之前将其截断,而不更改权限。

类似Python的open("filename",'w'),还是很常用的。我正准备写个包来利用OpenFile写个上述功能的函数,这样就没必要写flag参数了,没想到go官方写了,直接看看源代码吧

func WriteFile(name string, data []byte, perm FileMode) error {
	f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
	if err != nil {
		return err
	}
	_, err = f.Write(data)
	if err1 := f.Close(); err1 != nil && err == nil {
		err = err1
	}
	return err
}

使用OpenFile,存在清空,不存在创建,通过Write写入字节,手动Close,没有使用defer,效率更高。要是我写的话,估计就是defer+recover了,不会考虑效率。

io/ioutil的WriteFile只是套了一层而已(go 1.16):

func WriteFile(filename string, data []byte, perm fs.FileMode) error {
   return os.WriteFile(filename, data, perm)
}
读取 读取文件 读取最多n字节

func (f *File) Read(b []byte) (n int, err error)

Read方法从f中读取最多len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。

func ReadnBytes(filePath string,n int) ([]byte,error) {
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
		return nil,err
	}
	defer file.Close()

	byteSlice := make([]byte, n)
	_, err = file.Read(byteSlice)
	if err != nil {
		log.Fatal(err)
		return nil,err
	}
	return byteSlice,nil
}
	data,_ := ReadnBytes("./file.txt",20)
	fmt.Printf(" data:%c,len:%v\n",data,len(data))

结果

 data:[l a d y _ k i l l e r 7
 l a d y _ k],len:20

读取最少n字节

io里面的

func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)

ReadAtLeast从r至少读取min字节数据填充进buf。函数返回写入的字节数和错误(如果没有读取足够的字节)。只有没有读取到字节时才可能返回EOF;如果读取了但不够时遇到了EOF,函数会返回ErrUnexpectedEOF。 如果min比buf的长度还大,函数会返回ErrShortBuffer。只有返回值err为nil时,返回值n才会不小于min。

func ReadAtLeast(filePath string,n int) ([]byte,error){
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
		return nil,err
	}
	byteSlice := make([]byte, 512)
	_, err = io.ReadAtLeast(file, byteSlice, n)
	if err != nil {
		log.Fatal(err)
		return nil,err
	}
	return byteSlice,nil
}
	data,_ := ReadAtLeast("./file.txt",99)
	fmt.Printf(" data:%c,len:%v",data,len(data))

结果

2021/05/07 15:51:08 unexpected EOF
exit status 1

这个函数的使用场景就在于被读文件不够时会报错unexpected EOF

正好读取缓冲区长度的字节

func ReadFull(r Reader, buf []byte) (n int, err error)

ReadFull从 r 精确地读取len(buf)字节数据填充进buf。函数返回写入的字节数和错误(如果没有读取足够的字节)。只有没有读取到字节时才可能返回EOF;如果读取了但不够时遇到了EOF,函数会返回ErrUnexpectedEOF。 只有返回值err为nil时,返回值n才会等于len(buf)。

这个函数可以不学,看源代码:

func ReadFull(r Reader, buf []byte) (n int, err error) {
	return ReadAtLeast(r, buf, len(buf))
}

就是至少读取缓冲区长度的字节数

func ReadExactly(filePath string,n int) ([]byte,error){
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
		return nil,err
	}
	byteSlice := make([]byte, n)
	_, err = io.ReadFull(file, byteSlice)
	if err != nil {
		log.Fatal(err)
		return nil,err
	}
	return byteSlice,nil
}
	data,_ := ReadExactly("./file.txt",18)
	fmt.Printf(" data:%c,len:%v",data,len(data))

结果

 data:[l a d y _ k i l l e r 7
 l a d y],len:18

使用缓冲区

func NewReader(rd io.Reader) *Reader

NewReader创建一个具有默认大小缓冲、从r读取的*Reader。'

func OpenWithBuffer(filepath string) ([]string,error){
	file,err := os.Open(filepath)
	if err != nil{
		return nil,err
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	res := make([]string,1)
	for {
		line,err := reader.ReadString('\n')
		if err == io.EOF{
			break
		}
		if err != nil{
			return nil,err
		}
		res = append(res,line)
	}
	return res,nil
}
	data,_ := OpenWithBuffer("./file.txt")
	fmt.Printf(" data:%v,len:%v",data,len(data))

 结果

 data:[ lady_killer7
 lady_killer8
 lady_killer9
],len:4

使用scanner

func NewScanner(r io.Reader) *Scanner

NewScanner创建并返回一个从r读取数据的Scanner,默认的分割函数是ScanLines。 

func ReadWithScanner(filepath string) ([]string,error) {
	file,err := os.Open(filepath)
	if err != nil{
		return nil,err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	res := make([]string,1)
	for scanner.Scan(){
		res = append(res,scanner.Text())
	}
	return res,nil
}
	data,_ := ReadWithScanner("./file.txt")
	fmt.Printf(" data:%s,len:%v",data,len(data))

结果

 data:[ lady_killer7 lady_killer8 lady_killer9],len:4

快速读取整个文件

func ReadFile(filename string) ([]byte, error)

ioutil的ReadFile 从filename指定的文件中读取数据并返回文件的内容。

func ReadFile(filepath string) ([]byte,error) {
	file,err := os.Open(filepath)
	if err != nil{
		return nil,err
	}
	defer file.Close()
	fileCon,err :=  ioutil.ReadFile(filepath)
	if err != nil{
		return nil,err
	}
	return fileCon,nil
}
	data,_ := ReadFile("./file.txt")
	fmt.Printf(" data:%c,len:%v",data,len(data))

结果

 data:[l a d y _ k i l l e r 7
 l a d y _ k i l l e r 8
 l a d y _ k i l l e r 9
],len:42

文件信息

 func Stat(name string) (fi FileInfo, err error)

Stat返回一个描述name指定的文件对象的FileInfo。如果指定的文件对象是一个符号链接,返回的FileInfo描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,返回的错误值为*PathError类型。

func DisplayFile(filePath string)  {
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("文件名:", fileInfo.Name())
	fmt.Println("文件大小(字节):", fileInfo.Size())
	fmt.Println("权限:", fileInfo.Mode())
	fmt.Println("上次修改时间:", fileInfo.ModTime())
	fmt.Println("是否为目录: ", fileInfo.IsDir())
	fmt.Printf("系统接口类型: %T\n", fileInfo.Sys())
	fmt.Printf("系统信息: %+v\n\n", fileInfo.Sys())
}

结果

文件名: file.txt
文件大小(字节): 42
权限: -rw-rw-rw-
上次修改时间: 2021-05-07 10:53:58.0182636 +0800 CST
是否为目录:  false
系统接口类型: *syscall.Win32FileAttributeData
系统信息: &{FileAttributes:32 CreationTime:{LowDateTime:3090933060 HighDateTime:30884445} LastAccessTime:{LowDateTime:656163335 HighDateTime:30886628} LastWriteTime:{LowDateTime:969748588 HighDateTime:30884588}
 FileSizeHigh:0 FileSizeLow:42}

读取目录 单层目录

func (f *File) Readdir(n int) (fi []FileInfo, err error)

Readdir读取目录f的内容,返回一个有n个成员的[]FileInfo,这些FileInfo是被Lstat返回的,采用目录顺序。对本函数的下一次调用会返回上一次调用剩余未读取的内容的信息。

如果n>0,Readdir函数会返回一个最多n个成员的切片。这时,如果Readdir返回一个空切片,它会返回一个非nil的错误说明原因。如果到达了目录f的结尾,返回值err会是io.EOF。

如果n<=0,Readdir函数返回目录中剩余所有文件对象的FileInfo构成的切片。此时,如果Readdir调用成功(读取所有内容直到结尾),它会返回该切片和nil的错误值。如果在到达结尾前遇到错误,会返回之前成功读取的FileInfo构成的切片和该错误。

我们按照常见的排序方式进行排序

//-----------------------------读取目录-----------------------
func readDir(dirname string, name,size,time bool) ([]fs.FileInfo, error){
	f, err := os.Open(dirname)
	if err != nil {
		return nil, err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	if name == false && size == false && time == false{
		return nil,errors.New("you must choose a way for ordering")
	}
	if name{
		sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
	} else if size{
		sort.Slice(list, func(i, j int) bool { return list[i].Size() < list[j].Size() })
	} else if time{
		sort.Slice(list, func(i, j int) bool {return list[i].ModTime().Before(list[j].ModTime())})
	}
	return list, nil
}

//通过名字排序
func ReadDirByName(dirname string) ([]fs.FileInfo,error) {
	return readDir(dirname,true,false,false)
}

//通过大小排序
func ReadDirBySize(dirname string) ([]fs.FileInfo,error) {
	return readDir(dirname,false,true,false)
}

//通过最后修改时间排序
func ReadDirByTime(dirname string)  ([]fs.FileInfo,error)  {
	return readDir(dirname,false,false,true)
}
	lists,_ := ReadDirByName("../../")
	fmt.Println("------按照名字排序--------")
	for _,lst := range lists{
		fmt.Print(lst.Name()," ")
	}
	fmt.Println()
	fmt.Println("------按照大小排序--------")
	lists,_ = ReadDirBySize("../../")
	for _,lst := range lists{
		fmt.Print(lst.Name()," ")
	}
	fmt.Println()
	fmt.Println("------按照最后修改时间排序--------")
	lists,_ = ReadDirByTime("../../")
	for _,lst := range lists{
		fmt.Print(lst.Name()," ")
	}
	fmt.Println()

递归读取所有目录
//从指定目录,递归查找某些后缀文件,白名单和黑名单,若白名单、黑名单重复,黑名单优先
//参数
//dirPath:指定目录
//files:文件列表
//targets:后缀白名单
//except:后缀黑名单
//返回错误
func GetAllFiles(dirPath string,files *[]string,targets *[]string,except *[]string) error{
	file,err := os.Stat(dirPath)
	if err != nil{
		return err
	}else{
		if !file.IsDir(){
			return errors.New("dirPath is not a directory")
		}
	}
	entry,err := ioutil.ReadDir(dirPath)
	if err != nil{
		return err
	}
	sep := string(os.PathSeparator)
	// 遍历指定目录
	for _,f := range entry{
		tmp := dirPath+sep+f.Name()
		//标记是否不需要保存
		var flag  = false
		for _,suffix := range *except {
			if strings.HasSuffix(tmp,suffix){
				flag = true
				break
			}
		}
		if flag{
			continue
		}
		//-------目录,递归遍历---------
		if f.IsDir(){
			err = GetAllFiles(tmp,files,targets,except)
			if err != nil{
				return err
			}
		}else {
			for _,suffix := range *targets {
				if strings.HasSuffix(tmp,suffix){
					*files = append(*files,tmp)
					break
				}
			}
		}
	}
	return nil
}

这个我感觉用的比较多,打了个包,看后面总结。测试了下,应该问题不大。

判断文件或目录是否存在

利用前面提到的Stat和os的IsNotExist函数。

func CheckFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}
	ok := CheckFileIsExist("./file.txt")
	fmt.Println("./file.txt存在?",ok)
	ok = CheckFileIsExist("../lady_killer9")
	fmt.Println("../lady_killer9存在?",ok)

结果

./file.txt存在? true
../lady_killer9存在? false

总结 创建文件,只需要读写,不需要执行,使用Create,更多模式使用OpenFile创建目录推荐MkdirAll只读文件,可以使用Open,更多模式使用OpenFile写入文件,写入字符串和写入字节记一个即可,字节和字符串可以转换,磁盘I/O是很耗费时间的,写入很多时可以采用缓冲写入,快速写入使用WriteFile,不存在会创建读取目录可按照自定义排序方式返回

我将判断目录/文件是否存在和获取指定目录的所有文件打了个包,放到了gitee上

gitee:https://gitee.com/frankyu365/dirfile

参考

Go标准库-os

Go标准库-bufio

Go-标准库-io

working-files-go

更多Go相关内容:Go-Golang学习总结笔记

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。如果您感觉有所收获,自愿打赏,可选择支付宝18833895206(小于),您的支持是我不断更新的动力。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/995731.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存