我正在尝试使用go语言将文件夹的目录层次结构提取到数据结构中。 filepath.Walk
似乎是要走的路,但我到目前为止所做的就是打印文件和文件夹的名称。这是我正在使用的:
func main() {
visit := func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
fmt.Println("dir: ", path)
} else {
fmt.Println("file: ", path)
}
return nil
}
err := filepath.Walk("./", visit)
if err != nil {
log.Fatal(err)
}
}
这将打印文件夹的名称,如:
dir: folder1
file: folder1/file1.txt
file: folder1/file2.txt
file: folder1/file3.txt
file: folder1/file4.txt
dir: folder1/folder2
file: folder1/folder2/file5.txt
file: folder1/folder2/file6.txt
file: folder1/folder2/file7.txt
file: folder1/folder2/file8.txt
file: folder1/folder2/file9.txt
对于树结构我想过使用类似的东西:
type File struct {
Name string
Content string
}
type Folder struct {
Name string
Files []File
Folders []Folder
}
但当然欢迎任何建议。
如何将其转换为go中的树结构?有更简单的方法吗?
答案 0 :(得分:3)
AFAIK在Go标准库中没有为此做好准备。
树结构非常适合递归方法。我在您的文件和文件夹类型上定义了addFile
和addFolder
方法。从根文件夹开始,然后可以在Walk中调用这些方法。如果您获得了/ b / c,我们会拨打root.addFile(a, b, c)
,a.addFile(b, c)
,b.addFile(c)
。
我还将Folder.Folders更改为地图,因为filepath.Walk总是为我们提供完整路径,因此我们可以拆分它们并在文件夹地图中查找它们的组件。
这是一些快速而脏的代码,可能存在错误,并且不会执行完整的错误检查。它只适用于当前目录,但应该很容易修复。
我还在Folder上添加了一个String()方法,该方法由编译器识别,并在打印出该类型的实例时使用。
package main
import (
"log"
"os"
"path/filepath"
"strings"
)
type File struct {
Name string
}
type Folder struct {
Name string
Files []File
Folders map[string]*Folder
}
func newFolder(name string) *Folder {
return &Folder{name, []File{}, make(map[string]*Folder)}
}
func (f *Folder) getFolder(name string) *Folder {
if nextF, ok := f.Folders[name]; ok {
return nextF
} else {
log.Fatalf("Expected nested folder %v in %v\n", name, f.Name)
}
return &Folder{} // cannot happen
}
func (f *Folder) addFolder(path []string) {
for i, segment := range path {
if i == len(path)-1 { // last segment == new folder
f.Folders[segment] = newFolder(segment)
} else {
f.getFolder(segment).addFolder(path[1:])
}
}
}
func (f *Folder) addFile(path []string) {
for i, segment := range path {
if i == len(path)-1 { // last segment == file
f.Files = append(f.Files, File{segment})
} else {
f.getFolder(segment).addFile(path[1:])
return
}
}
}
func (f *Folder) String() string {
var str string
for _, file := range f.Files {
str += f.Name + string(filepath.Separator) + file.Name + "\n"
}
for _, folder := range f.Folders {
str += folder.String()
}
return str
}
func main() {
startPath := "."
rootFolder := newFolder(startPath)
visit := func(path string, info os.FileInfo, err error) error {
segments := strings.Split(path, string(filepath.Separator))
if info.IsDir() {
if path != startPath {
rootFolder.addFolder(segments)
}
} else {
rootFolder.addFile(segments)
}
return nil
}
err := filepath.Walk(startPath, visit)
if err != nil {
log.Fatal(err)
}
log.Printf("%v\n", rootFolder)
}
答案 1 :(得分:3)
我需要类似于我的一个小应用程序的东西,所以我写了一个很小的独立库,可以让你的观看愉快on Github。因为我需要为返回的os.FileInfo内置JSON序列化,所以我也添加了它。
我知道对于这个问题的原始作者来说为时已晚,但无论如何都要在这里张贴,以防有人正在寻找类似的东西。拉请求很容易接受:)
答案 2 :(得分:0)
少许修改
package main
import (
"fmt"
"path"
"strings"
)
type File struct {
Id string
Name string
}
type Folder struct {
Name string
Files []File
Folders map[string]*Folder
}
func newFolder(name string) *Folder {
return &Folder{name, []File{}, make(map[string]*Folder)}
}
func (f *Folder) getFolder(name string) *Folder {
if nextF, ok := f.Folders[name]; ok {
return nextF
} else if f.Name == name {
return f
} else {
return &Folder{}
}
}
func (f *Folder) existFolder(name string) bool {
for _, v := range f.Folders {
if v.Name == name {
return true
}
}
return false
}
func (f *Folder) addFolder(folderName string) {
if !f.existFolder(folderName) {
f.Folders[folderName] = newFolder(folderName)
}
}
func (f *Folder) addFile(fileName string, fileId string) {
f.Files = append(f.Files, File{fileId, fileName})
}
func (f *Folder) getList() (result []map[string]interface{}) {
for _, v := range f.Folders {
result = append(result, map[string]interface{}{
"name": v.Name,
"type": "folder",
})
}
for _, v := range f.Files {
result = append(result, map[string]interface{}{
"id": v.Id,
"name": v.Name,
"type": "file",
})
}
return
}
func isFile(str string) bool {
if path.Ext(str) != "" {
return true
}
return false
}
func DeleteEmptyElements(s []string) []string {
var r []string
for _, str := range s {
if str != "" {
r = append(r, str)
}
}
return r
}
type IS map[string]string
func main() {
arrayPaths := []interface{}{
IS{
"id": "1",
"filePath": "/print/some/com.png",
},
IS{
"id": "2",
"filePath": "/print/some2/com412412.png",
},
IS{
"id": "3",
"filePath": "/print/some2/41241241241.png",
},
}
breadcrumb := "/print/some2"
startPath := "/"
rootFolder := newFolder(startPath)
for _, path := range arrayPaths {
filePath := path.(IS)["filePath"]
fileId := path.(IS)["id"]
splitPath := DeleteEmptyElements(strings.Split(filePath, "/"))
tmpFolder := rootFolder
for _, item := range splitPath {
if isFile(item) {
tmpFolder.addFile(item, fileId)
} else {
if item != startPath {
tmpFolder.addFolder(item)
}
tmpFolder = tmpFolder.getFolder(item)
}
}
}
currentFolder := rootFolder.getFolder("/")
breadcrumbElements := DeleteEmptyElements(strings.Split(breadcrumb, "/"))
for i, v := range breadcrumbElements {
if currentFolder.existFolder(v) {
currentFolder = currentFolder.getFolder(v)
if i == len(breadcrumbElements)-1 {
break
}
} else {
currentFolder = currentFolder.getFolder(v)
}
}
fmt.Println(currentFolder.getList())
}
答案 3 :(得分:0)
仅将其中一个用于循环和文件路径。步行
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"path"
"path/filepath"
)
func main() {
tree := BuildTree(os.Args[1])
fmt.Println(tree)
}
type File struct {
Name string
}
type Folder struct {
Name string
Files []*File
Folders map[string]*Folder
}
func (f *Folder) String() string {
j, _ := json.Marshal(f)
return string(j)
}
func BuildTree(dir string) *Folder {
dir = path.Clean(dir)
var tree *Folder
var nodes = map[string]interface{}{}
var walkFun filepath.WalkFunc = func(p string, info os.FileInfo, err error) error {
if info.IsDir() {
nodes[p] = &Folder{path.Base(p), []*File{}, map[string]*Folder{}}
} else {
nodes[p] = &File{path.Base(p)}
}
return nil
}
err := filepath.Walk(dir, walkFun)
if err != nil {
log.Fatal(err)
}
for key, value := range nodes {
var parentFolder *Folder
if key == dir {
tree = value.(*Folder)
continue
} else {
parentFolder = nodes[path.Dir(key)].(*Folder)
}
switch v := value.(type) {
case *File:
parentFolder.Files = append(parentFolder.Files, v)
case *Folder:
parentFolder.Folders[v.Name] = v
}
}
return tree
}
答案 4 :(得分:0)
从 Go 1.16 开始,您可以使用 fstest.MapFS
作为您的数据结构
要求:
package main
import (
"io/fs"
"os"
"path/filepath"
"testing/fstest"
)
func main() {
m := make(fstest.MapFS)
walk := func(s string, d fs.DirEntry, e error) error {
if e != nil { return e }
if ! d.IsDir() {
data, e := os.ReadFile(s)
if e != nil { return e }
m[s] = &fstest.MapFile{Data: data}
}
return nil
}
filepath.WalkDir(`C:\go\src\net`, walk)
data := m[`C:\go\src\net\textproto\writer.go`].Data[:44]
println(string(data) == "// Copyright 2010 The Go Authors. All rights")
}