高级文件管理
让我们想象一个情况,我们有几个数据集分布在文件夹中,文件夹中也可能有垃圾(带有有趣浣熊的图片,文本文档)。 我希望能够找到必要的文件,以便以后与他们一起工作。 问题是Julia只实现了文件的基本工作-<https://engee.com/helpcenter/stable/ru-en/julia/base/file.html >,这显然不足以完成这样的任务。
在这篇文章中,我将使用称为树的数据结构制作一个更智能的工具来处理文件。
创建树
让我们观察一下,目录和文件的结构与树非常相似。 编程中的树是描述一种特殊图形的数据结构。 在这样的图中,除了"根"之外的所有节点都有一个父节点。:
用于图遍历和图修改的最优算法对于这样的图是已知的,并且它们通常已经实现。 我的想法如下:将文件夹及其子文件夹的内容表示为一棵树,其中的每个节点都将是一个文件或文件夹。 每个节点将单独存储创建和修改的路径,名称,扩展名,日期和时间以及文件夹属性。 并且为了组织一个树结构,我将存储这个节点的"后代"。:
using Dates
struct FileTreeNode
path::String
name::String
ext::String
isdir::Bool
created::DateTime
modified::DateTime
children::Vector{FileTreeNode}
end
这样,我将能够遍历文件和文件夹树比使用Julia标准库更容易和更快。
要获取文件创建和修改的日期和时间,以及获取文件名,路径和扩展名,我将编写额外的函数:
function get_metadata(path::String)
st = stat(path)
created = unix2datetime(st.ctime)
modified = unix2datetime(st.mtime)
return created, modified
end
function split_name_ext(path::String)
name = basename(path)
base, ext = splitext(name)
return base, ext
end
种植一棵树
已经创建了必要的数据结构和辅助功能,您可以继续执行目录结构树的实现。
让我们创建一个函数,该函数将接收文件或文件夹的创建和修改时间,特定路径的名称和扩展名。 接下来,使用 readdir 我们将获得当前文件夹内的文件和文件夹列表。
我们将对每个检测到的文件夹重复相同的操作。 也就是说,我们的函数将调用自己。 这种技术称为递归。
让我们为我们的功能添加一个限制:搜索深度。 此限制将限制文件夹的嵌套级别以爬网并加快树构建速度。
由于这个函数,我们得到了一个树节点。
function build_tree(path::String; maxdepth=typemax(Int), depth=0)
is_dir = isdir(path)
name, ext = split_name_ext(path)
created, modified = get_metadata(path)
if is_dir && depth < maxdepth
entries = readdir(path; join=true)
children = [
build_tree(e; maxdepth, depth=depth+1)
for e in sort(entries)
]
else
children = FileTreeNode[]
end
return FileTreeNode(path, name, ext, is_dir, created, modified, children)
end
接下来,我们将简化使用AbstractTrees可视化树的任务。jl库。 我们将为我们的树定义两个新函数:
*children()-获取树节点的后代
*printnode()-在屏幕上显示节点
import Pkg
Pkg.add("AbstractTrees")
import AbstractTrees: children, printnode
children(node::FileTreeNode) = node.children
function printnode(io::IO, node::FileTreeNode)
if node.isdir
print(io, "📁 ", node.name)
else
print(io, "📄 ", node.name, node.ext)
end
end
准备好了! 让我们检查一下这棵树:
tree = build_tree(@__DIR__,maxdepth=1)
using AbstractTrees: print_tree
print_tree(tree)
把树付诸实践
让我们将位于DataDepot文件夹中的3个数据集粘合在一起。
首先,让我们看看我们的文件夹中有什么。:
tree = build_tree(joinpath(@__DIR__,"DataDepot"),maxdepth=2)
print_tree(tree)
然后,我们将得到所有*。csv文件通过绕过上面创建的树。:
function find_files_by_ext(node::FileTreeNode, ext::String,acc=String[])
if !startswith(ext,".")
ext = "."*ext
end
if isequal(node.ext,ext)
push!(acc, node.path)
println("$(acc)")
end
for c in node.children
accchild = find_files_by_ext(c, ext)
if ~isempty(accchild)
append!(acc,accchild)
end
end
return acc
end
csv_files = find_files_by_ext(tree,"csv")
现在让我们下载它们并将它们粘合在一起。:
using CSV
df_v = Vector{DataFrame}()
for f in csv_files
df = CSV.read(joinpath(pwd(),f),DataFrame)
println("从$f文件中读取$(nrow(df))行")
push!(df_v,df)
end
df_v = reduce(vcat,df_v)
结论
在这篇文章中,我们看了一个使用编程中的经典数据结构来解决实际工程问题的例子。 在随后的出版物中,将考虑促进技术计算任务的其他编程技术。