日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Golang遞歸獲取目錄下所有文件方法實例_Golang

作者:戀喵大鯉魚 ? 更新時間: 2023-06-19 編程語言

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 -> ../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 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/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 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

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新