【问题标题】:Create iterative JSON directory tree - Golang创建迭代 JSON 目录树 - Golang
【发布时间】:2015-10-06 05:08:50
【问题描述】:

我在创建我在 GoLang 中递归编写的程序的迭代版本时遇到问题。目标是获取目录路径并返回包含来自该目录的文件信息并保留目录结构的 JSON 树。这是我目前所拥有的:

我创建了一个 File 结构,它将包含目录树中每个条目的信息:

type File struct {
    ModifiedTime time.Time `json:"ModifiedTime"`
    IsLink       bool      `json:"IsLink"`
    IsDir        bool      `json:"IsDir"`
    LinksTo      string    `json:"LinksTo"`
    Size         int64     `json:"Size"`
    Name         string    `json:"Name"`
    Path         string    `json:"Path"`
    Children     []File    `json:"Children"`
}

在我的迭代程序中,我创建了一个堆栈来模拟递归调用。

func iterateJSON(path string) {
    var stack []File
    var child File
    var file File
    rootOSFile, _ := os.Stat(path)
    rootFile := toFile(rootOSFile, path) //start with root file
    stack = append(stack, rootFile) //append root to stack 
    for len(stack) > 0 { //until stack is empty,
        file = stack[len(stack)-1] //pop entry from stack
        stack = stack[:len(stack)-1] 
        children, _ := ioutil.ReadDir(file.Path) //get the children of entry 
        for i := 0; i < len(children); i++ { //for each child
            child = (toFile(children[i], path+"/"+children[i].Name())) //turn it into a File object
            file.Children = append(file.Children, child) //append it to the children of the current file popped
            stack = append(stack, child) //append the child to the stack, so the same process can be run again
        }
    }
    rootFile.Children
    output, _ := json.MarshalIndent(rootFile, "", "     ")
    fmt.Println(string(output))
}

func toFile(file os.FileInfo, path string) File {
    var isLink bool
    var linksTo string
    if file.Mode()&os.ModeSymlink == os.ModeSymlink {
        isLink = true
        linksTo, _ = filepath.EvalSymlinks(path + "/" + file.Name())
    } else {
        isLink = false
        linksTo = ""
    }
    JSONFile := File{ModifiedTime: file.ModTime(),
        IsDir:    file.IsDir(),
        IsLink:   isLink,
        LinksTo:  linksTo,
        Size:     file.Size(),
        Name:     file.Name(),
        Path:     path,
        Children: []File{}}
    return JSONFile
}

理论上,当我们在堆栈中移动时,子文件应该附加到根文件。但是,唯一返回的是根文件(没有附加任何子文件)。知道为什么会这样吗?

【问题讨论】:

    标签: go tree iteration directory-structure


    【解决方案1】:

    主要问题是结构不是像切片或映射那样的描述符值,也就是说,如果您将结构值分配给变量,它将被复制。如果将结构值分配给切片或数组的元素,则切片将被复制。它们不会被链接!

    因此,当您将rootFile 添加到stack,然后从stack 中弹出一个元素(将等于rootFile)并修改弹出的元素时,您不会观察到更改在你的局部变量rootFile

    解决方案很简单:使用指向结构的指针。

    你的代码也有错误:

    child = (toFile(children[i], path+"/"+children[i].Name())) //turn it into a File object
    

    应该是:

    child = (toFile(children[i], file.Path+"/"+children[i].Name())) // ...
    

    改进代码的提示:

    我宁愿使用path.Join()filepath.Join()来加入路径元素:

    child = toFile(children[i], filepath.Join(file.Path, children[i].Name()))
    

    如果初始路径以斜杠或反斜杠结尾并且您明确将其与另一个斜杠连接,您的代码甚至可能无法正常工作。 Join() 会处理这些,所以您不必这样做。

    不要在函数的开头声明所有局部变量,只有在需要它们时,并且在最内部的块中才需要它们。这将确保您不会意外地分配给错误的变量,并且您会知道它不会在最里面的块之外被修改(因为它不在范围内) - 这有助于更容易地理解您的代码。你也可以使用short variable declaration

    使用for ... range 结构,更简洁。例如:

    for _, chld := range children {
        child := toFile(chld, filepath.Join(file.Path, chld.Name()))
        file.Children = append(file.Children, child)
        stack = append(stack, child)
    }
    

    还可以使用zero values,例如如果文件不是链接,则不需要设置IsLinkLinksTo字段,因为零值是false""是你最终会得到的。

    虽然它在这里可能并不重要,但始终处理错误,打印或记录它们作为最低限度,这样你就不会浪费时间找出问题所在,如果某些事情不是你所期望的(你最终会搜索代码中的错误,几小时后,您终于添加了打印错误,发现错误不在您的代码中,而是在其他地方)。

    使用上述指针和提示的工作变体

    type File struct {
        ModifiedTime time.Time `json:"ModifiedTime"`
        IsLink       bool      `json:"IsLink"`
        IsDir        bool      `json:"IsDir"`
        LinksTo      string    `json:"LinksTo"`
        Size         int64     `json:"Size"`
        Name         string    `json:"Name"`
        Path         string    `json:"Path"`
        Children     []*File   `json:"Children"`
    }
    
    func iterateJSON(path string) {
        rootOSFile, _ := os.Stat(path)
        rootFile := toFile(rootOSFile, path) //start with root file
        stack := []*File{rootFile}
    
        for len(stack) > 0 { //until stack is empty,
            file := stack[len(stack)-1] //pop entry from stack
            stack = stack[:len(stack)-1]
            children, _ := ioutil.ReadDir(file.Path) //get the children of entry
            for _, chld := range children {          //for each child
                child := toFile(chld, filepath.Join(file.Path, chld.Name())) //turn it into a File object
                file.Children = append(file.Children, child)                 //append it to the children of the current file popped
                stack = append(stack, child)                                 //append the child to the stack, so the same process can be run again
            }
        }
    
        output, _ := json.MarshalIndent(rootFile, "", "     ")
        fmt.Println(string(output))
    }
    
    func toFile(file os.FileInfo, path string) *File {
        JSONFile := File{ModifiedTime: file.ModTime(),
            IsDir:    file.IsDir(),
            Size:     file.Size(),
            Name:     file.Name(),
            Path:     path,
            Children: []*File{},
        }
        if file.Mode()&os.ModeSymlink == os.ModeSymlink {
            JSONFile.IsLink = true
            JSONFile.LinksTo, _ = filepath.EvalSymlinks(filepath.Join(path, file.Name()))
        } // Else case is the zero values of the fields
        return &JSONFile
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-18
      • 2019-12-28
      • 1970-01-01
      • 2013-01-13
      • 2011-07-28
      • 1970-01-01
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多