網站首頁 編程語言 正文
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
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-09-30 Docker容器Consul部署概述_docker
- 2022-06-17 C語言深入講解函數參數的使用_C 語言
- 2022-06-06 elementUI基礎的引入和使用
- 2022-04-06 關于Redis數據庫三種持久化方案介紹_Redis
- 2022-05-13 this.$route.params獲取不到
- 2022-08-14 C#生成比較短的Token字符串_C#教程
- 2022-11-28 C語言中getchar()函數的用法小結_C 語言
- 2022-10-07 VsCode使用EmmyLua插件調試Unity工程Lua代碼的詳細步驟_C#教程
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支