文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么使用Golang递归获取目录下所有文件

2023-07-05 06:47

关注

这篇文章主要讲解了“怎么使用Golang递归获取目录下所有文件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用Golang递归获取目录下所有文件”吧!

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 mainimport ("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 mainimport ("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 -> ../zip

tree 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 mainimport ("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/zipln

follow 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 mainimport ("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)}

感谢各位的阅读,以上就是“怎么使用Golang递归获取目录下所有文件”的内容了,经过本文的学习后,相信大家对怎么使用Golang递归获取目录下所有文件这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯