Go中的列表目录

Beh*_*ree 168 go

我一直试图找出如何简单地在Go中的单个目录中列出文件和文件夹.

我发现了filepath.Walk,但它会自动进入子目录,这是我不想要的.我所有的其他搜索都没有变得更好.

我确信这个功能存在,但它真的很难找到.如果有人知道我应该在哪里看,请告诉我.谢谢.

Roc*_*key 328

您可以尝试使用包中的ReadDir函数io/ioutil.根据文档:

ReadDir读取dirname命名的目录,并返回已排序的目录条目列表.

生成的切片包含os.FileInfo类型,这些类型提供此处列出的方法.这是一个基本示例,列出了当前目录中所有内容的名称(包含文件夹但未特别标记 - 您可以使用该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())
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您只想要目录内容的 **名称** 并且速度至关重要,请注意使用 [Readdirnames](https://golang.org/src/os/dir.go?s=1898:1960 #L34) 速度快*几个数量级*(对我来说快了大约 20 倍) (19认同)
  • @SquattingSlavInTracksuit - 这只是一个数量级:P (6认同)
  • 从 go 1.16 开始,io/ioutil 已被弃用。你应该使用 os.ReadDir() 而不是 ioutil.ReadDir() (4认同)
  • @SquattingSlavInTracksuit:我将您的评论提升为答案,因为我当时没有评论权限。如果你愿意回答并获得荣誉,LMK。 (2认同)

Fat*_*lan 71

更简单,使用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
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,`Glob忽略文件系统错误,例如读取目录的I/O错误.当模式格式错误时,唯一可能返回的错误是ErrBadPattern (7认同)
  • 在使用之前一定要了解Glob的功能.https://golang.org/pkg/path/filepath/#Glob (2认同)

man*_*and 57

我们可以使用各种golang标准库函数获取文件系统上文件夹内的文件列表.

  1. filepath.Walk
  2. ioutil.ReadDir
  3. 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)
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 使用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
}
Run Code Online (Sandbox Code Playgroud)
  1. 使用ioutil.ReadDir

ioutil.ReadDir 读取由dirname命名的目录,并返回按filename排序的目录条目列表.

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
}
Run Code Online (Sandbox Code Playgroud)
  1. 使用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
}
Run Code Online (Sandbox Code Playgroud)

基准测试结果.

基准分数

获取此博客帖子的更多详细信息

  • 这里最完整的答案。值得注意的是,此基准测试没有报告内存使用或分配。更快的实现可能使用更多的内存。测试仪计算机上的CPU内核数量也可能损害/帮助并发的`filepath.Walk`。此外,`filepath.Walk`支持递归体面,而`os.File.Readdir`和ioutil.ReadDir`不支持。 (3认同)
  • 来自 `ioutil.ReadDir` 的 Go 源代码:“从 Go 1.16 开始,`os.ReadDir` 是一个更高效、更正确的选择:它返回一个 `fs.DirEntry` 列表,而不是 `fs.FileInfo`,并且它如果读取目录过程中出现错误,则返回部分结果。” 根据文档,完整的“ioutil”包函数应替换为“io”或“os”包中的函数。 (2认同)

Son*_*nia 22

ioutil.ReadDir是一个很好的发现,但如果你点击并查看源代码,你会看到它调用os.File的Readdir方法.如果你对目录顺序没问题并且不需要排序列表,那么这个Readdir方法就是你所需要的.


Jac*_*ski 13

根据您的描述,您可能想要的是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)
Run Code Online (Sandbox Code Playgroud)

感谢SquattingSlavInTracksuit评论; 如果可以的话,我会建议将他们的评论提升为答案。


Von*_*onC 9

从 Go 1.16 开始,您可以使用os.ReadDir函数。

func ReadDir(name string) ([]DirEntry, error)

它读取给定目录并返回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())
    }
}
Run Code Online (Sandbox Code Playgroud)

感兴趣的:Go 1.17(2021 年第三季度)包括fs.FileInfoToDirEntry()

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())
    }
}
Run Code Online (Sandbox Code Playgroud)

FileInfoToDirEntry返回DirEntry从 返回信息的info
如果info为 nil,则FileInfoToDirEntry返回 nil。


背景

Go 1.16(2021 年第一季度)将基于CL 243908CL 243914提出基于接口ReadDir函数FS

func FileInfoToDirEntry(info FileInfo) DirEntry
Run Code Online (Sandbox Code Playgroud)

这允许“os:ReadDir轻量级目录读取的添加方法”
请参阅提交 a4ede9f

// 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)
}
Run Code Online (Sandbox Code Playgroud)

src/os/os_test.go#testReadDir() 说明它的用法:

// 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)
}
Run Code Online (Sandbox Code Playgroud)

本霍伊特指出Go 1.16的评论中os.ReadDir

os.ReadDir(path string) ([]os.DirEntry, error),您可以直接调用它而无需 Open跳舞。
所以你可以把它缩短为 just os.ReadDir,因为这是大多数人会调用的具体函数。

提交 3d913a9(2020 年 12 月):

os: 添加ReadFile, WriteFile, CreateTemp(是TempFile ), MkdirTemp(was TempDir) fromio/ioutil

io/ioutil 是一个定义不明确的助手集合。

提案 #40025将通用 I/O 助手移出到 io。提案 #42026 的此 CL将特定于操作系统的帮助程序移动到os,使整个io/ioutil弃用包。

os.ReadDir返回[]DirEntry,与ioutil.ReadDir's形成对比[]FileInfo
(提供返回的助手[]DirEntry是此更改的主要动机之一。)


Tim*_*nen 5

使用递归打印目录中所有文件的完整示例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
}
Run Code Online (Sandbox Code Playgroud)