[英]Create iterative JSON directory tree - Golang
我在创建用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
}
从理论上讲,当我们在堆栈中移动时,应将子文件附加到根文件中。 但是,唯一返回的是根文件(未附加任何子项)。 知道为什么会这样吗?
主要问题是结构不是切片或映射之类的描述符值,也就是说,如果将结构值分配给变量,它将被复制。 如果将结构值分配给切片或数组的元素,则将复制切片。 他们将不会被链接!
因此,当您将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()
将解决这些问题,因此您不必这样做。
不要在函数开始时才声明所有局部变量,仅在需要它们时,并且在最内部的块中才需要它们。 这将确保您不会意外地分配了错误的变量,并且您将知道它没有在最里面的块之外被修改(因为在它的最外面的块不在范围内)-这有助于更轻松地理解代码。 您也可以使用短变量声明 。
利用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)
}
还可以使用零值 ,例如,如果文件不是链接,则无需设置IsLink
和LinksTo
字段,因为零值是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
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.