Golang递归获取目录下所有文件方法实例
作者:恋喵大鲤鱼 发布时间:2024-04-25 15:19:00
1.问题
如果我想获取一个目录下的所有文件列表,使用 Golang 该如何实现呢?
比如有个目录 dir 结构如下:
tree dir
dir
├── bar.txt
├── foo.txt
└── subdir
└── baz.txt
那么如何获取 dir 目录下的所有文件路径呢?
dir/foo.txt
dir/bar.txt
dir/subdir/baz.txt
2.io/ioutil
标准库 io/ioutil 包提供了一个函数 ReadDir()
可以获取指定目录下的所有内容,按文件名排序,返回 []fs.FileInfo
切片来描述目录中的所有内容。
func ReadDir(dirname string) ([]fs.FileInfo, error)
利用 ioutil.ReadDir()
我们可以获取目录中的所有文件吗?
// ListDir lists all the file or dir names in the specified directory.
// Note that ListDir don't traverse recursively.
func ListDir(dirname string) ([]string, error) {
infos, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
names := make([]string, len(infos))
for i, info := range infos {
names[i] = info.Name()
}
return names, nil
}
我们来测试一下:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
names, _ := ListDir("dir")
fmt.Printf("names:%v\n", names)
}
运行输出:
names:[bar.txt foo.txt subdir]
可见 ioutil.ReadDir() 并不会递归获取子目录的内容。
3.递归获取
如果想递归获子目录的内容,该如何实现呢?
我们可以递归的调用我们自己的函数,来递归遍历子目录。
// GetDirAllFilePaths gets all the file paths in the specified directory recursively.
func GetDirAllFilePaths(dirname string) ([]string, error) {
// Remove the trailing path separator if dirname has.
dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
infos, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
paths := make([]string, 0, len(infos))
for _, info := range infos {
path := dirname + string(os.PathSeparator) + info.Name()
if info.IsDir() {
tmp, err := GetDirAllFilePaths(path)
if err != nil {
return nil, err
}
paths = append(paths, tmp...)
continue
}
paths = append(paths, path)
}
return paths, nil
}
我们来测试一下:
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
paths, _ := GetDirAllFilePaths("dir/")
for _, path := range paths {
fmt.Println(path)
}
}
运行输出:
dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
哇,看起来大功告成。但果真如此吗?
4.包含符号链接的情况
如果我们此时在目录 dir 中加入一个符号链接,指向另外一个目录,那结果会如何呢?
tree dir
dir
├── bar.txt
├── foo.txt
├── subdir
│ └── baz.txt
└── zipln -> ../ziptree zip
zip
└── qux.txt
还是运行调用 GetDirAllFilePaths(),我们得到的结果如下:
dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
dir/zipln
可见,当子目录为符号链接时,我们并没有访问链接指向的目标文件。
我们改变一下实现,当子目录是符号链接时,读取目标目录下的文件。
// GetDirAllFilePathsFollowSymlink gets all the file paths in the specified directory recursively.
func GetDirAllFilePathsFollowSymlink(dirname string) ([]string, error) {
// Remove the trailing path separator if dirname has.
dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
infos, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
paths := make([]string, 0, len(infos))
for _, info := range infos {
path := dirname + string(os.PathSeparator) + info.Name()
realInfo, err := os.Stat(path)
if err != nil {
return nil, err
}
if realInfo.IsDir() {
tmp, err := GetDirAllFilePathFollowSymlink(path)
if err != nil {
return nil, err
}
paths = append(paths, tmp...)
continue
}
paths = append(paths, path)
}
return paths, nil
}
我们来测试一下:
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
paths, _ := GetDirAllFilePathsFollowSymlink("dir/")
for _, path := range paths {
fmt.Println(path)
}
}
运行输出:
dir/bar.txt
dir/foo.txt
dir/subdir/baz.txt
dir/zipln/qux.txt
perfect,这就是我们想要的效果。
5.同时返回目录的路径
有时,我们还需要目录路径,即获取指定目录下的文件和子目录的路径。比如在对一个目录进行压缩时会需要。
还是以上文 dir 目录为例,我们想要的结果是:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
dir/zipln/qux.txt
我们只要稍微改造 GetDirAllFilePaths 和 GetDirAllFilePathsFollowSymlink 即可,在遍历时把当前的目录加入结果集。
并更名 GetDirAllFilePaths 为 GetDirAllEntryPaths,GetDirAllFilePathsFollowSymlink 为 GetDirAllEntryPathsFollowSymlink,因为条目(Entry)比文件(File)语义更符合函数的功能,因为不仅可以获取文件,也可以获取目录的路径。
// GetDirAllEntryPaths gets all the file or dir paths in the specified directory recursively.
// Note that GetDirAllEntryPaths won't follow symlink if the subdir is a symbolic link.
func GetDirAllEntryPaths(dirname string, incl bool) ([]string, error) {
// Remove the trailing path separator if dirname has.
dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
infos, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
paths := make([]string, 0, len(infos))
// Include current dir.
if incl {
paths = append(paths, dirname)
}
for _, info := range infos {
path := dirname + string(os.PathSeparator) + info.Name()
if info.IsDir() {
tmp, err := GetDirAllEntryPaths(path, incl)
if err != nil {
return nil, err
}
paths = append(paths, tmp...)
continue
}
paths = append(paths, path)
}
return paths, nil
}
// GetDirAllEntryPathsFollowSymlink gets all the file or dir paths in the specified directory recursively.
func GetDirAllEntryPathsFollowSymlink(dirname string, incl bool) ([]string, error) {
// Remove the trailing path separator if dirname has.
dirname = strings.TrimSuffix(dirname, string(os.PathSeparator))
infos, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
paths := make([]string, 0, len(infos))
// Include current dir.
if incl {
paths = append(paths, dirname)
}
for _, info := range infos {
path := dirname + string(os.PathSeparator) + info.Name()
realInfo, err := os.Stat(path)
if err != nil {
return nil, err
}
if realInfo.IsDir() {
tmp, err := GetDirAllEntryPathsFollowSymlink(path, incl)
if err != nil {
return nil, err
}
paths = append(paths, tmp...)
continue
}
paths = append(paths, path)
}
return paths, nil
}
我们测试一下。
func main() {
fmt.Println("not follow symlink:")
paths, _ := GetDirAllEntryPaths("dir/", true)
for _, path := range paths {
fmt.Println(path)
}
fmt.Println("\nfollow symlink:")
paths, _ = GetDirAllEntryPathsFollowSymlink("dir/", true)
for _, path := range paths {
fmt.Println(path)
}
}
运行输出:
not follow symlink:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/ziplnfollow symlink:
dir
dir/bar.txt
dir/foo.txt
dir/subdir
dir/subdir/baz.txt
dir/zipln
dir/zipln/qux.txt
6.go-huge-util
以上函数已放置开源库 go-huge-util,可 import 直接使用。
package main
import (
"github.com/dablelv/go-huge-util/file"
)
func main() {
// 获取目录下所有文件和子目录名称(不会递归)。
names, _ := file.ListDir("dir")
// 递归获取目录下所有文件路径(不解析符号链接)
paths, _ := file.GetDirAllEntryPaths("dir", false)
// 递归获取目录下所有文件和目录路径(不解析符号链接)
paths, _ = file.GetDirAllEntryPaths("dir", true)
// 递归获取目录下所有文件路径(解析符号链接)
paths, _ = file.GetDirAllEntryPathsFollowSymlink("dir", false)
// 递归获取目录下所有文件与目录路径(解析符号链接)
paths, _ = file.GetDirAllEntryPathsFollowSymlink("dir/", true)
}
欢迎大家 Star & PR。
参考文献
io/ioutil - Go Packages
来源:https://blog.csdn.net/K346K346/article/details/128024386


猜你喜欢
- 描述sin()返回的x弧度的正弦值。语法以下是sin()方法的语法:importmath math.sin(x)注意:sin()是不能直接访
- 使用Python3和Opencv识别一张标准的答题卡。大致的过程如下:1.读取图片2.利用霍夫圆检测,检测出四个角的黑圆位置,从确定四个角的
- 前言编写条件分支代码是编码过程中不可或缺的一部分。如果用道路来做比喻,现实世界中的代码从来都不是一条笔直的高速公路,而更像是由无数个岔路口组
- 1.由于设置了slave的配置信息,mysql在数据库data目录下生成master.info,所以如有要修改相关slave的配置要先删除该
- 目录1.数据库主从分类:2.mysql主从介绍由来3.主从作用4.主从复制原理5.主从复制配置(数据一致时)5.1主从服务器分别安装mysq
- 本文实例为大家分享了python实现日历效果的具体代码,供大家参考,具体内容如下一、代码编程过程1、根据年月日算出星期几def get_we
- 可以使用python中的sys模块的getrefcount()方法来获取对象引用的个数。具体可以看以下的实例:import sys # 首先
- pandas中常用的一件事情就是对特定条件进行搜索,那么这里介绍使用pandas搜索方式,本案例使用的pandas是anaconda中的,可
- 我们在平常的系统开发中常常会遇到像无限级分类这样的树型结构数据,现提供一个可用的数据库存储过程,可以完成树型结构数据的排序。环境:windo
- Unittestunittest大家应该都不陌生。它作为一款博主在5-6年前最常用的单元测试框架,现在正被pytest,nose慢慢蚕食。渐
- 前言相信在日常生活中,平常大家聚在一起总会聊聊天,特别是女生(有冒犯到doge)非常喜欢聊星座,这个男生什么星座呀,那个男生什么星座呀…今天
- 写程序经常需要用到从文件或者标准输入中按行读取信息,这里汇总一下。方便使用1. C++ 读取文件#include<stdio
- 目录step1 搭建框架step2 填充搭建好了的Promise框架总结step1 搭建框架1. 首先我们需要在这里放置一个promise函
- python最值与下标最大值的下标winner = np.argmax(scores)多个最大值的下标(np.argwhere返回数组中非0
- /** * 得到XML文件属性的集合对象 * @param x
- CentOS6.9安装Mysql5.7,供大家参考,具体内容如下一、上传安装包二、建立用户以及mysql的目录1、建立一个mysql的组输入
- 依赖模块xlwt下载:pip install xlwt后台模块view.py# 导出Excel文件def export_excel(requ
- 本文实例讲述了MySQL 多表关联一对多查询实现取最新一条数据的方法。分享给大家供大家参考,具体如下:MySQL 多表关联一对多查询取最新的
- 一、开头匹配从字符串开头开始匹配返回匹配对象;如果找不到匹配,则为Noneimport reprint(re.match('飞兔小哥
- 什么是爬虫?网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息