8

フォルダのディレクトリ階層を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でツリー構造に変換するにはどうすればよいですか?これを行う簡単な方法はありますか?

4

5 に答える 5

4

私の小さなアプリにも似たようなものが必要だったので、Githubで閲覧できる小さな別のライブラリを作成しました。返されたos.FileInfoに組み込みのJSONシリアル化が必要だったので、それも追加しました。

この質問の元の作者には遅すぎることは知っていますが、誰かが似たようなものを探している場合に備えて、とにかくここに投稿してください。プルリクエストはすぐに受け入れられます:)

于 2013-08-09T10:48:48.853 に答える
3

AFAIKには、Go標準ライブラリにこのための既製のものはありません。

ツリー構造は、再帰的なアプローチに適しています。私はあなたのファイルとフォルダのタイプでメソッドを定義addFileしました。addFolderルートフォルダから始めて、Walkでこれらのメソッドを呼び出すことができます。a / 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)
}
于 2012-09-30T07:34:49.840 に答える
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())
}
于 2017-11-10T14:37:15.347 に答える
0

forループとfilepath.Walkは1つだけ使用してください

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
}
于 2019-06-12T14:54:52.463 に答える
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")
}

https://golang.org/pkg/testing/fstest#MapFS

于 2021-04-22T20:33:51.707 に答える