我一直在试图弄清楚如何简单地列出 Go 中单个目录中的文件和文件夹。
我找到了 filepath.Walk
,但它会自动进入我不想要的子目录。我所有的其他搜索都没有好转。
我确信这个功能是存在的,但是真的很难找到。如果有人知道我应该看哪里,请告诉我。谢谢。
您可以尝试使用 io/ioutil
包中的 ReadDir 函数。根据文档:
ReadDir 读取由 dirname 命名的目录并返回已排序的目录条目列表。
生成的切片包含 os.FileInfo
类型,它们提供列出的方法 here。这是一个列出当前目录中所有内容名称的基本示例(包括文件夹但没有特别标记 - 您可以使用 IsDir()
方法检查项目是否为文件夹):
package main
import (
"fmt"
"io/ioutil"
"log"
)
func main() {
files, err := ioutil.ReadDir("./")
if err != nil {
log.Fatal(err)
}
for _, f := range files {
fmt.Println(f.Name())
}
}
我们可以使用各种 golang 标准库函数获取文件系统上文件夹内的文件列表。
文件路径.Walk ioutil.ReadDir os.File.Readdir
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
)
func main() {
var (
root string
files []string
err error
)
root := "/home/manigandan/golang/samples"
// filepath.Walk
files, err = FilePathWalkDir(root)
if err != nil {
panic(err)
}
// ioutil.ReadDir
files, err = IOReadDir(root)
if err != nil {
panic(err)
}
//os.File.Readdir
files, err = OSReadDir(root)
if err != nil {
panic(err)
}
for _, file := range files {
fmt.Println(file)
}
}
使用 filepath.Walk
path/filepath 包提供了一种方便的方法来扫描目录中的所有文件,它会自动扫描目录中的每个子目录。
func FilePathWalkDir(root string) ([]string, error) {
var files []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
files = append(files, path)
}
return nil
})
return files, err
}
使用 ioutil.ReadDir
ioutil.ReadDir 读取由 dirname 命名的目录并返回按文件名排序的目录条目列表。
func IOReadDir(root string) ([]string, error) {
var files []string
fileInfo, err := ioutil.ReadDir(root)
if err != nil {
return files, err
}
for _, file := range fileInfo {
files = append(files, file.Name())
}
return files, nil
}
使用 os.File.Readdir
Readdir 读取与 file 关联的目录的内容,并按目录顺序返回最多 n 个 FileInfo 值的切片,这将由 Lstat 返回。对同一文件的后续调用将产生更多 FileInfos。
func OSReadDir(root string) ([]string, error) {
var files []string
f, err := os.Open(root)
if err != nil {
return files, err
}
fileInfo, err := f.Readdir(-1)
f.Close()
if err != nil {
return files, err
}
for _, file := range fileInfo {
files = append(files, file.Name())
}
return files, nil
}
基准测试结果。
https://i.stack.imgur.com/7SAu5.png
获取有关此 Blog Post 的更多详细信息
filepath.Walk
。此外,filepath.Walk
支持递归体面,而 os.File.Readdir
和 ioutil.ReadDir
不支持。
ns/op
= 每次操作纳秒,所以越小越好。但正如@xeoncross 所提到的,Walk 在并发环境中可能会更好,并且在具有大量文件的环境中使用的内存可能会少得多。
ioutil.ReadDir
的 Go 源代码:“从 Go 1.16 开始,os.ReadDir
是更有效和正确的选择:它返回 fs.DirEntry
列表而不是 fs.FileInfo
,并且在读取目录时出错。”完整的 ioutil
包函数应按文档替换为 io
或 os
包中的函数。
更简单的是,使用 path/filepath
:
package main
import (
"fmt"
"log"
"path/filepath"
)
func main() {
files, err := filepath.Glob("*")
if err != nil {
log.Fatal(err)
}
fmt.Println(files) // contains a list of all files in the current directory
}
Glob ignores file system errors such as I/O errors reading directories. The only possible returned error is ErrBadPattern, when pattern is malformed.
ioutil.ReadDir
是一个不错的发现,但如果您单击并查看源代码,您会发现它调用了方法 Readdir of os.File。如果您对目录顺序没问题并且不需要对列表进行排序,那么这个 Readdir 方法就是您所需要的。
从 Go 1.16 开始,您可以使用 os.ReadDir 函数。
func ReadDir(name string) ([]DirEntry, 错误)
它读取给定目录并返回一个 DirEntry
切片,其中包含按文件名排序的目录条目。
这是一个乐观函数,因此,当读取目录条目时发生错误时,它会尝试返回一个切片,其中包含错误前的文件名。
package main
import (
"fmt"
"log"
"os"
)
func main() {
files, err := os.ReadDir(".")
if err != nil {
log.Fatal(err)
}
for _, file := range files {
fmt.Println(file.Name())
}
}
感兴趣的:Go 1.17(2021 年第三季度)包括 fs.FileInfoToDirEntry()
:
func FileInfoToDirEntry(info FileInfo) DirEntry FileInfoToDirEntry 返回一个从 info 返回信息的 DirEntry。如果 info 为 nil,则 FileInfoToDirEntry 返回 nil。
背景
Go 1.16(2021 年第一季度)将在 CL 243908 和 CL 243914 的基础上提出 ReadDir
function,基于 FS
interface:
// An FS provides access to a hierarchical file system.
//
// The FS interface is the minimum implementation required of the file system.
// A file system may implement additional interfaces,
// such as fsutil.ReadFileFS, to provide additional or optimized functionality.
// See io/fsutil for details.
type FS interface {
// Open opens the named file.
//
// When Open returns an error, it should be of type *PathError
// with the Op field set to "open", the Path field set to name,
// and the Err field describing the problem.
//
// Open should reject attempts to open names that do not satisfy
// ValidPath(name), returning a *PathError with Err set to
// ErrInvalid or ErrNotExist.
Open(name string) (File, error)
}
这允许 "os: add ReadDir
method for lightweight directory reading":
请参阅 commit a4ede9f:
// ReadDir reads the contents of the directory associated with the file f
// and returns a slice of DirEntry values in directory order.
// Subsequent calls on the same file will yield later DirEntry records in the directory.
//
// If n > 0, ReadDir returns at most n DirEntry records.
// In this case, if ReadDir returns an empty slice, it will return an error explaining why.
// At the end of a directory, the error is io.EOF.
//
// If n <= 0, ReadDir returns all the DirEntry records remaining in the directory.
// When it succeeds, it returns a nil error (not io.EOF).
func (f *File) ReadDir(n int) ([]DirEntry, error)
// A DirEntry is an entry read from a directory (using the ReadDir method).
type DirEntry interface {
// Name returns the name of the file (or subdirectory) described by the entry.
// This name is only the final element of the path, not the entire path.
// For example, Name would return "hello.go" not "/home/gopher/hello.go".
Name() string
// IsDir reports whether the entry describes a subdirectory.
IsDir() bool
// Type returns the type bits for the entry.
// The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method.
Type() os.FileMode
// Info returns the FileInfo for the file or subdirectory described by the entry.
// The returned FileInfo may be from the time of the original directory read
// or from the time of the call to Info. If the file has been removed or renamed
// since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist).
// If the entry denotes a symbolic link, Info reports the information about the link itself,
// not the link's target.
Info() (FileInfo, error)
}
src/os/os_test.go#testReadDir()
说明了它的用法:
file, err := Open(dir)
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
}
defer file.Close()
s, err2 := file.ReadDir(-1)
if err2 != nil {
t.Fatalf("ReadDir %q failed: %v", dir, err2)
}
Ben Hoyt 指出 in the comments 到 Go 1.16 os.ReadDir
:
os.ReadDir(path string) ([]os.DirEntry, error),你可以直接调用它而无需 Open dance。因此,您可能可以将其缩短为 os.ReadDir,因为这是大多数人会调用的具体函数。
请参阅 commit 3d913a9(2020 年 12 月):
os: add ReadFile, WriteFile, CreateTemp (was TempFile), MkdirTemp (was TempDir) from io/ioutil io/ioutil 是一个定义不明确的助手集合。提案 #40025 将通用 I/O 助手移到了 io。此提案 #42026 的 CL 将特定于操作系统的帮助程序移至 os,从而弃用了整个 io/ioutil 包。
os.ReadDir 返回 []DirEntry,与 ioutil.ReadDir 的 []FileInfo 形成对比。 (提供返回 []DirEntry 的助手是此更改的主要动机之一。)
os.ReadDir(path string) ([]os.DirEntry, error)
,您无需 Open
即可直接调用它。因此,您可以将其缩短为 os.ReadDir
,因为这是大多数人会调用的具体函数。
根据您的描述,您可能想要的是 os.Readdirnames。
func (f *File) Readdirnames(n int) (names []string, err error) Readdirnames 读取与文件关联的目录的内容,并按目录顺序返回目录中最多 n 个文件名的切片。对同一文件的后续调用将产生更多名称。 ... 如果 n <= 0,Readdirnames 在单个切片中返回目录中的所有名称。
片段:
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
names, err := file.Readdirnames(0)
if err != nil {
return err
}
fmt.Println(names)
归功于 SquattingSlavInTracksuit 的 comment;如果可以的话,我会建议将他们的评论推广到答案。
使用 Readdirnames
递归打印目录中所有文件的完整示例
package main
import (
"fmt"
"os"
)
func main() {
path := "/path/to/your/directory"
err := readDir(path)
if err != nil {
panic(err)
}
}
func readDir(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
names, _ := file.Readdirnames(0)
for _, name := range names {
filePath := fmt.Sprintf("%v/%v", path, name)
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
return err
}
fmt.Println(filePath)
if fileInfo.IsDir() {
readDir(filePath)
}
}
return nil
}