2012-09-29 41 views
6

我试图用go语言将文件夹的目录层次结构提取到数据结构中。 filepath.Walk似乎是要走的路,但我迄今所能做的只是打印文件和文件夹的名称。下面是我使用的是什么:使用go语言提取目录层次结构

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 
} 

但当然任何欢迎提出建议。

我该如何将它转换为树形结构?有没有更简单的方法来做到这一点?

回答

3

AFAIK在Go标准库中没有为此准备好的。

树结构很好地适用于递归方法。我在文件和文件夹类型上定义了addFileaddFolder方法。从根文件夹开始,然后可以在Walk中调用这些方法。如果你得到一个/ b/c,我们会打电话给root.addFile(a, b, c),a.addFile(b, c)b.addFile(c)

我还将Folder.Folders更改为地图,因为filepath.Walk总是给我们提供完整路径,所以我们可以拆分它们并在文件夹地图中查找它们的组件。

这里有一些快速和脏的代码,可能有错误,并没有做全面的错误检查。它只适用于当前目录,但应该很容易解决。

我还在文件夹中添加了一个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) 
} 
+0

我得到这个意义上,你的回答是真实的但它并不在我的电脑上运行'说13点25分23秒2012/09/30预计嵌套页面中的文件夹页面退出状态1' – none

+0

不知道什么会导致此问题。正如我所说,这是快速和肮脏的代码来说明这个概念。您应该能够从那里调试和/或更改它。 –

+0

由于代码比我预期的要长,我决定用我的设计换一种方式。同时,如果有人想出一个优雅的解决方案,我会暂时将问题留待开放。感谢您的帮助.. – none

3

我需要类似我的一个小应用程序,所以我写了一个微小的独立库,该库为您的观赏乐趣on Github东西。由于我需要为返回的os.FileInfo内置JSON序列化,我也添加了它。

我知道这个问题的原始作者来得太晚,但无论如何都发布在这里,以防有人正在寻找类似的东西。拉欣然接受请求:)

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()) 
} 
+1

请解释您所编辑的内容以及它现在的工作原理。 –