面试题答案
一键面试算法逻辑
- 初始化数据结构:
- 使用一个
map[string]int
来统计每种文件类型(扩展名)的文件数量,键为文件扩展名,值为文件数量。 - 使用一个
map[string]string
来记录符号链接及其指向的实际路径,键为符号链接路径,值为实际路径。
- 使用一个
- 深度优先遍历函数:
- 定义一个递归函数
dfs
,接受当前目录路径作为参数。 - 使用
filepath.Walk
函数来进行深度优先遍历目录结构。filepath.Walk
会对每个目录项(文件或目录)调用一个用户定义的函数。 - 在用户定义的函数中,首先检查文件信息,判断是否是符号链接。如果是符号链接,使用
filepath.EvalSymlinks
函数获取其实际路径,并处理循环符号链接(通过维护一个已访问路径的集合)。 - 如果是普通文件,提取其扩展名,更新文件类型统计的
map
。 - 如果是目录,递归调用
dfs
函数处理该子目录。
- 定义一个递归函数
异常情况处理策略
- 权限问题:
- 在
filepath.Walk
的用户定义函数中,当遇到权限不足无法访问的文件或目录时,filepath.Walk
会返回错误。捕获这个错误并记录下来,例如可以使用log
包记录日志,同时继续遍历其他路径。
- 在
- 循环符号链接:
- 维护一个
map[string]bool
来记录已经访问过的路径。在处理符号链接时,在解析实际路径之前,先检查该符号链接路径是否已经在已访问路径集合中。如果已存在,则说明存在循环符号链接,记录错误(同样可以用log
包)并跳过该链接。
- 维护一个
性能优化思路
- 减少系统调用:
- 批量处理文件操作,例如在处理目录中的所有文件时,尽量减少对每个文件单独进行系统调用获取文件信息的次数。可以先获取目录中的所有项,然后批量处理。
- 缓存处理结果:
- 对于已经解析过的符号链接路径,可以缓存其结果。这样在后续再次遇到相同的符号链接时,直接从缓存中获取实际路径,避免重复解析。
- 并行处理:
- 在遍历子目录时,可以考虑使用Go的goroutine进行并行处理,提高遍历速度。但要注意处理好资源竞争问题,例如对文件类型统计
map
和符号链接实际路径记录map
的并发访问。
- 在遍历子目录时,可以考虑使用Go的goroutine进行并行处理,提高遍历速度。但要注意处理好资源竞争问题,例如对文件类型统计
示例代码
package main
import (
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
)
func main() {
root := "/" // 假设根目录为遍历起点
fileTypeCount := make(map[string]int)
symlinkTarget := make(map[string]string)
visited := make(map[string]bool)
err := filepath.Walk(root, func(path string, info fs.DirEntry, err error) error {
if err != nil {
log.Printf("Error accessing %s: %v\n", path, err)
return nil
}
if info.Mode()&os.ModeSymlink != 0 {
realPath, err := filepath.EvalSymlinks(path)
if err != nil {
log.Printf("Error evaluating symlink %s: %v\n", path, err)
return nil
}
if visited[path] {
log.Printf("Cycle detected in symlink %s\n", path)
return nil
}
visited[path] = true
symlinkTarget[path] = realPath
} else if info.Mode().IsRegular() {
ext := filepath.Ext(path)
fileTypeCount[ext]++
}
if info.IsDir() {
newPath := filepath.Join(path, info.Name())
if visited[newPath] {
log.Printf("Cycle detected in directory %s\n", newPath)
return filepath.SkipDir
}
visited[newPath] = true
}
return nil
})
if err != nil {
log.Fatalf("Error walking the path %s: %v\n", root, err)
}
fmt.Println("File type counts:")
for ext, count := range fileTypeCount {
fmt.Printf("%s: %d\n", ext, count)
}
fmt.Println("\nSymbolic link targets:")
for link, target := range symlinkTarget {
fmt.Printf("%s -> %s\n", link, target)
}
}
上述代码实现了对目录结构的深度优先遍历,统计文件类型数量并找出符号链接指向的实际路径,同时处理了权限问题和循环符号链接等异常情况。