我有一些文件路径的字符串数组:
path/to/folder/file.xxx
path/to/other/
path/to/file/file.xx
path/file.x
path/
如何将此列表转换为树结构?到目前为止,我有以下内容:
/// <summary>
/// Enumerates types of filesystem nodes.
/// </summary>
public enum FilesystemNodeType
{
/// <summary>
/// Indicates that the node is a file.
/// </summary>
File,
/// <summary>
/// Indicates that the node is a folder.
/// </summary>
Folder
}
/// <summary>
/// Represents a file or folder node.
/// </summary>
public class FilesystemNode
{
private readonly ICollection<FilesystemNode> _children;
/// <summary>
/// Initializes a new instance of the <see cref="FilesystemNode"/> class.
/// </summary>
public FilesystemNode()
{
_children = new LinkedList<FilesystemNode>();
}
/// <summary>
/// Gets or sets the name of the file or folder.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the full path to the file or folder from the root.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the node is a file or folder.
/// </summary>
public FilesystemNodeType Type { get; set; }
/// <summary>
/// Gets a list of child nodes of this node. The node type must be a folder to have children.
/// </summary>
public ICollection<FilesystemNode> Children
{
get
{
if (Type == FilesystemNodeType.Folder)
return _children;
throw new InvalidOperationException("File nodes cannot have children");
}
}
}
我对如何实际拆分路径感到有些不知所措。以/结尾的任何路径都是一个目录,任何不存在的路径都不是。
此外,虽然我的输入将始终包含该文件夹的路径,但如果没有,我将如何解释该情况?
例如,如果我有输入:
path/to/file.c
path/file.c
path/
我如何解释path/to/
不在输入中的事实?
答案 0 :(得分:6)
这是一个生成NodeEntry项的嵌套字典的解决方案(您可以根据需要替换文件信息类):
public class NodeEntry
{
public NodeEntry()
{
this.Children = new NodeEntryCollection();
}
public string Key { get; set; }
public NodeEntryCollection Children { get; set; }
}
public class NodeEntryCollection : Dictionary<string, NodeEntry>
{
public void AddEntry(string sEntry, int wBegIndex)
{
if (wBegIndex < sEntry.Length)
{
string sKey;
int wEndIndex;
wEndIndex = sEntry.IndexOf("/", wBegIndex);
if (wEndIndex == -1)
{
wEndIndex = sEntry.Length;
}
sKey = sEntry.Substring(wBegIndex, wEndIndex - wBegIndex);
if (!string.IsNullOrEmpty(sKey)) {
NodeEntry oItem;
if (this.ContainsKey(sKey)) {
oItem = this[sKey];
} else {
oItem = new NodeEntry();
oItem.Key = sKey;
this.Add(sKey, oItem);
}
// Now add the rest to the new item's children
oItem.Children.AddEntry(sEntry, wEndIndex + 1);
}
}
}
}
要使用上述内容,请创建一个新集合:
NodeEntryCollection cItems = new NodeEntryCollection();
然后,对于列表中的每一行:
cItems.AddEntry(sLine, 0);
答案 1 :(得分:0)
按'/'
字符拆分每一行。如果字符串数组的长度为5,那么前四个项应该是目录,并且您必须测试最后一个扩展名:
string.IsNullOrEmpty(new FileInfo("test").Extension)
如果像你的情况一样,即使对于最后一个目录也总是有'/'
,那么拆分字符串数组的最后一项是空的。
剩下的就是穿越你的树。解析项目时,请检查根节点的Children
属性中是否存在第一个目录。如果它不存在,添加它,如果它存在,使用这个并进一步。
答案 2 :(得分:0)
我受到来自competent_tech的回答的启发,并将Dictionary<string, NodeEntry>
替换为&#34;简单&#34; ObservableCollection<NodeEntry>
作为&#34; Key&#34;信息将在此词典中存储两次:一次作为词典的键,一次作为NodeEntry
类中的公共属性。
所以我的样本基于&#34; competent_tech&#34;代码如下所示:
public class NodeEntryObservableCollection : ObservableCollection<NodeEntry>
{
public const string DefaultSeparator = "/";
public NodeEntryObservableCollection(string separator = DefaultSeparator)
{
Separator = separator; // default separator
}
/// <summary>
/// Gets or sets the separator used to split the hierarchy.
/// </summary>
/// <value>
/// The separator.
/// </value>
public string Separator { get; set; }
public void AddEntry(string entry)
{
AddEntry(entry, 0);
}
/// <summary>
/// Parses and adds the entry to the hierarchy, creating any parent entries as required.
/// </summary>
/// <param name="entry">The entry.</param>
/// <param name="startIndex">The start index.</param>
public void AddEntry(string entry, int startIndex)
{
if (startIndex >= entry.Length)
{
return;
}
var endIndex = entry.IndexOf(Separator, startIndex);
if (endIndex == -1)
{
endIndex = entry.Length;
}
var key = entry.Substring(startIndex, endIndex - startIndex);
if (string.IsNullOrEmpty(key))
{
return;
}
NodeEntry item;
item = this.FirstOrDefault(n => n.Key == key);
if (item == null)
{
item = new NodeEntry(Separator) { Key = key };
Add(item);
}
// Now add the rest to the new item's children
item.Children.AddEntry(entry, endIndex + 1);
}
}
public class NodeEntry
{
public string Key { get; set; }
public NodeEntryObservableCollection Children { get; set; }
public NodeEntry(string separator = NodeEntryObservableCollection.DefaultSeparator)
{
Children = new NodeEntryObservableCollection(separator);
}
}
这有助于我在TreeView中绑定数据,如下所示:
<TreeView Name="trvMyTreeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:NodeEntry}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Key}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
后面有一个示例代码:
IList<string> pathes = new List<string>
{
"localhost",
"remotehost.levelD.levelDB",
"localhost.level1.level11",
"localhost.level1",
"remotehost.levelD.levelDA",
"localhost.level2.level22",
"remotehost.levelA",
"remotehost",
"remotehost.levelB",
"remotehost.levelD",
"localhost.level2",
"remotehost.levelC"
};
SortedSet<string> sortedPathes = new SortedSet<string>(pathes);
var obsCollection = new NodeEntryObservableCollection(".");
foreach (var p in sortedPathes) { obsCollection.AddEntry(p); }
trvMyTreeView.ItemsSource = obsCollection;