How can I loop over sub folders in Assets folder? - c#

string selectedPath = GetPath();
var subFolders = AssetDatabase.GetSubFolders(selectedPath);
List<string> paths = new List<string>();
foreach(string path in subFolders)
{
paths.Add(path);
}
For example the subFolders is Assets/My Folder
but under My Folder there are many more subfolders.
AssetDatabase.GetSubFolders don't make recursive it's getting the first sub folder only.
I want to get all the sub folders recursive.
I tried :
List paths = new List();
foreach(string path in subFolders)
{
paths.Add(path);
}
but it's still giving me only the first sub folder.
This is how I'm getting the selected path name in the Assets :
[MenuItem("Assets/Get Path")]
private static string GetClickedDirFullPath()
{
string clickedAssetGuid = Selection.assetGUIDs[0];
string clickedPath = AssetDatabase.GUIDToAssetPath(clickedAssetGuid);
string clickedPathFull = Path.Combine(Directory.GetCurrentDirectory(), clickedPath);
FileAttributes attr = File.GetAttributes(clickedPathFull);
return attr.HasFlag(FileAttributes.Directory) ? clickedPathFull : Path.GetDirectoryName(clickedPathFull);
}
[MenuItem("Assets/Get Path")]
private static string GetPath()
{
string path = GetClickedDirFullPath();
int index = path.IndexOf("Assets");
string result = path.Substring(index);
return result;
}

You could simply make it recursive using List<T>.AddRange like
private static string[] GetSubFoldersRecursive(string root)
{
var paths = new List<string>();
// If there are no further subfolders then AssetDatabase.GetSubFolders returns
// an empty array => foreach will not be executed
// This is the exit point for the recursion
foreach (var path in AssetDatabase.GetSubFolders(root))
{
// add this subfolder itself
paths.Add(path);
// If this has no further subfolders then simply no new elements are added
paths.AddRange(GetSubFoldersRecursive(path));
}
return paths.ToArray();
}
So e.g.
[ContextMenu("Test")]
private void Test()
{
var sb = new StringBuilder();
var folders = SubFolders("Assets");
if(folders.Length > 0)
{
foreach (var folder in SubFolders("Assets"))
{
sb.Append(folder).Append('\n');
}
}
else
{
sb.Append(" << The given path has no subfolders! >>");
}
Debug.Log(sb.ToString());
}
will print out the entire project's folder structure.
For
I get
Assets/Example 1
Assets/Example 1/SubFolder A
Assets/Example 1/SubFolder B
Assets/Example 1/SubFolder C
Assets/Example 2
Assets/Example 2/SubFolder A
Assets/Example 2/SubFolder A/SubSubFolder A
So in your case it would be
string selectedPath = GetPath();
var folders = SubFolders(selectedPath);
foreach(var path in folders)
{
...
}

Try this code for get recursive folder path
//this is your code
string selectedPath = GetPath();
var subFolders = AssetDatabase.GetSubFolders(selectedPath);
List<string> paths = new List<string>();
if(subFolders != null)
{
foreach(string path in subFolders)
{
GetAllRecursiveFolder(path,ref paths);
}
}
else
{
paths.add(selectedPath);
}
public void GetAllRecursiveFolder(string currentPath,ref List<string> paths)
{
var subFolders = AssetDatabase.GetSubFolders(currentPath);
if(subFolders != null)
{
foreach(string path in subFolders)
{
GetAllRecursiveFolder(path,ref paths);// Get recursive folder path, and stored in ref variable
}
}
else
{
paths.add(currentPath);
}
}

Related

Enumerate directories that match wildcard

This is my folder structure:
c:\logs\v1\api1
c:\logs\v1\api2
c:\logs\v2\api1
c:\logs\v2\api2
c:\logs\other
I would like to get all folders that match this pattern:
c:\logs\v*\api*
So, what I want is to get list of theese:
c:\logs\v1\api1
c:\logs\v1\api2
c:\logs\v2\api1
c:\logs\v2\api2
This doesn't work:
var result = Directory.GetDirectories(#"c:\logs\v*\api*");
Because System.IO.IOException: 'The filename, directory name, or volume label syntax is incorrect. : 'c:\logs\v*\api*'' is thrown
This doesn't work:
var directory = #"c:\logs\v*\api*";
var rootDirectory = Directory.GetDirectoryRoot(directory);
var remainingPath = directory.Substring(rootDirectory.Length);
Console.WriteLine($"remainingPath: {remainingPath}");
var result = Directory.GetDirectories(rootDirectory, remainingPath);
Because System.IO.IOException: 'The filename, directory name, or volume label syntax is incorrect. : 'c:\logs\v*'' is thrown
This works:
public static IEnumerable<string> ResolveDirectories(string path)
{
if (path.Contains("*") || path.Contains("?"))
{
var parts = new Regex(#"[\\/](?=[^\\/]*[\*?])").Split(path, 2);
var searchRoot = parts[0];
var searchPatterns = parts[1].Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
foreach (var dir in ResolveWildcards(searchRoot, searchPatterns))
yield return dir;
}
else
{
yield return path;
}
}
public static IEnumerable<string> ResolveWildcards(string searchRoot, string[] searchPatterns)
{
if (Directory.Exists(searchRoot))
{
// use next pattern to search in a search root
var next = searchPatterns[0];
// leave the rest for recursion
var rest = searchPatterns.Skip(1).ToArray();
if (!searchRoot.EndsWith("\\"))
searchRoot += "\\";
foreach (var dir in Directory.EnumerateDirectories(searchRoot, next))
{
// if nothing left (last pattern) - return it
if (rest.Length == 0)
yield return dir;
else
{
// otherwise search with rest patterns in freshly found directory
foreach (var sub in ResolveWildcards(dir, rest))
yield return sub;
}
}
}
}
But I don't like how this is coded.
Thanks to #Alessandro_D'Andria I created this:
public static IEnumerable<string> ResolveDirectories(string path)
{
var parts = new Regex(#"[\\/](?=[^\\/]*[\*?])").Split(path, 2);
var root = parts[0];
var part = #".*\\" + parts[1].Replace(#"\", #"\\");
var regex = new Regex(part);
var dirs = Directory
.EnumerateDirectories(
root,
"*",
SearchOption.AllDirectories)
.Where(x => regex.IsMatch(x))
.ToList();
return dirs;
}
But both solutions are big mess. Is there any more elegant solution?
I think you are complicating, just using some regex and linq should be enough:
var regex = new Regex(#".*\\logs\\v\d+\\api\d+");
var dirs = Directory
.EnumerateDirectories(
#"C:\logs",
"*",
SearchOption.AllDirectories)
.Where(x => regex.IsMatch(x));

List<string> to listbox.Items C#

I have this code
public List<string> GetAllFilesFromFolder(string root, bool searchSubfolders)
{
Queue<string> folders = new Queue<string>();
List<string> files = new List<string>();
folders.Enqueue(root);
while (folders.Count != 0)
{
string currentFolder = folders.Dequeue();
try
{
string[] filesInCurrent = System.IO.Directory.GetFiles(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
files.AddRange(filesInCurrent);
}
catch
{
// Do Nothing
}
try
{
if (searchSubfolders)
{
string[] foldersInCurrent = System.IO.Directory.GetDirectories(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
foreach (string _current in foldersInCurrent)
{
folders.Enqueue(_current);
}
}
}
catch
{
// Do Nothing
}
}
return files;
}
It list all files from especific directory and search for subdirectories ignoring excepetions to a List<string>
But How can I list the List<string> results to a listbox?
I tried to put
foreach (var foo in files)
{
listbox1.Items.Add(foo);
}
after return files; but for some reason the appears nothing in listbox... I am using visual basic windows forms
Try
listBox1.DataSource = MyList;
(duplicate: C#: easiest way to populate a ListBox from a List)

Treeview with different folder classes

I'm trying to pull together a treeview with folders of mixed classes. The root folders are one class and subfolders are another, like this:
Root Folder #1 <-- DirectoryItem class
--Sub Folder <-- SubDirectoryItem class
----file <-- FileItem class
Root Folder #2 <-- DirectoryItem class
--Sub Folder <-- SubDirectoryItem class
----file <-- FileItem class
I've been attempting to combine two different recursive methods so the subfolders are retrieved as "SubDirectoryItems" instead of "DirectoryItem" I'll spare that code attempt due to lack of clarify, but I'm wondering if there's a better approach?
class ItemProvider
{
public List<Item> GetItems(string path, SearchOption searchOption)
{
var items = new List<Item>();
var dirInfo = new DirectoryInfo(path);
foreach (var directory in dirInfo.GetDirectories("*.*", SearchOption.TopDirectoryOnly))
{
var item = new DirectoryItem
{
Name = directory.Name,
Path = directory.FullName,
Items = GetItems(directory.FullName, SearchOption.AllDirectories)
};
items.Add(item);
}
foreach (var subdirectory in dirInfo.GetDirectories("*.*", SearchOption.TopDirectoryOnly))
{
var item = new SubDirectoryItem()
{
Name = subdirectory.Name,
Path = subdirectory.FullName,
Items = GetItems(subdirectory.FullName, SearchOption.AllDirectories)
};
items.Add(item);
}
foreach (var file in dirInfo.GetFiles())
{
var item = new FileItem
{
Name = file.Name,
Path = file.FullName
};
items.Add(item);
}
return items;
}
}
}
I was able to figure it out. I made the first list TopDirectoryOnly then pulled in the rest of the items via a 2nd list for AllDirectories. Here's the code in case it helps any other beginners (like me):
class ItemProvider
{
public List<Item> GetItems(string path, SearchOption searchOption)
{
var items = new List<Item>();
var dirInfo = new DirectoryInfo(path);
foreach (var directory in dirInfo.GetDirectories("*.*", SearchOption.TopDirectoryOnly))
{
var item = new DirectoryItem
{
Name = directory.Name,
Path = directory.FullName,
Items = GetSubItems(directory.FullName, SearchOption.AllDirectories)
};
items.Add(item);
}
return items;
}
public List<Item> GetSubItems(string path, SearchOption searchOption)
{
var items = new List<Item>();
var dirInfo = new DirectoryInfo(path);
foreach (var subdirectory in dirInfo.GetDirectories("*.*", SearchOption.AllDirectories))
{
var item = new SubDirectoryItem()
{
Name = subdirectory.Name,
Path = subdirectory.FullName,
Items = GetSubItems(subdirectory.FullName, SearchOption.AllDirectories)
};
items.Add(item);
}
foreach (var file in dirInfo.GetFiles())
{
var item = new FileItem
{
Name = file.Name,
Path = file.FullName
};
items.Add(item);
}
return items;
}
}

C# Adding an array or list into an List

I've got a List of Document
public class Document
{
public string[] fullFilePath;
public bool isPatch;
public string destPath;
public Document() { }
public Document(string[] fullFilePath, bool isPatch, string destPath)
{
this.fullFilePath = fullFilePath;
this.isPatch = isPatch;
this.destPath = destPath;
}
The fullFilepath should a List or an Array of Paths.
For example:
Document 1
---> C:\1.pdf
---> C:\2.pdf
Document 2
---> C:\1.pdf
---> C:\2.pdf
---> C:\3.pdf
etc.
My problem if I am using an array string all Documents got "null" in its fullFilePath.
If I'm using a List for the fullFilePath all Documents got the same entries from the last Document.
Here is how the List is filled:
int docCount = -1;
int i = 0;
List<Document> Documents = new List<Document>();
string[] sourceFiles = new string[1];
foreach (string file in filesCollected)
{
string bc;
string bcValue;
if (Settings.Default.barcodeEngine == "Leadtools")
{
bc = BarcodeReader.ReadBarcodeSymbology(file);
bcValue = "PatchCode";
}
else
{
bc = BarcodeReader.ReadBacrodes(file);
bcValue = "009";
}
if (bc == bcValue)
{
if(Documents.Count > 0)
{
Array.Clear(sourceFiles, 0, sourceFiles.Length);
Array.Resize<string>(ref sourceFiles, 1);
i = 0;
}
sourceFiles[i] = file ;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents.Add(new Document(sourceFiles, true,""));
docCount++;
}
else
{
if (Documents.Count > 0)
{
sourceFiles[i] = file;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents[docCount].fullFilePath = sourceFiles;
}
}
}
You are using the same instance of the array for every document. The instance is updated with a new list of files at every inner loop, but an array is a reference to an area of memory (oversimplification, I know but for the purpose of this answer is enough) and if you change the content of that area of memory you are changing it for every document.
You need to create a new instance of the source files for every new document you add to your documents list. Moreover, when you are not certain of the number of elements that you want to be included in the array, it is a lot better to use a generic List and remove all that code that handles the resizing of the array.
First change the class definition
public class Document
{
public List<string> fullFilePath;
public bool isPatch;
public string destPath;
public Document() { }
public Document(List<string> fullFilePath, bool isPatch, string destPath)
{
this.fullFilePath = fullFilePath;
this.isPatch = isPatch;
this.destPath = destPath;
}
}
And now change your inner loop to
foreach (string file in filesCollected)
{
string bc;
string bcValue;
....
if (bc == bcValue)
{
List<string> files = new List<string>();
files.Add(file);
Documents.Add(new Document(files, true, ""));
docCount++;
}
else
Documents[docCount].fullFilePath.Add(file);
}
Notice that when you need to add a new Document I build a new List<string>, add the current file and pass everything at the constructor (In reality this should be moved directly inside the constructor of the Document class). When you want to add just a new file you could add it directly to the public fullFilePath property
Moving the handling of the files inside the Documents class could be rewritten as
public class Document
{
public List<string> fullFilePath;
public bool isPatch;
public string destPath;
public Document()
{
// Every constructory initializes internally the List
fullFilePath = new List<string>();
}
public Document(string aFile, bool isPatch, string destPath)
{
// Every constructory initializes internally the List
fullFilePath = new List<string>();
this.fullFilePath.Add(aFile);
this.isPatch = isPatch;
this.destPath = destPath;
}
public void AddFile(string aFile)
{
this.fullFilePath.Add(aFile);
}
}
Of course, now in you calling code you pass only the new file or call AddFile without the need to check for the list initialization.
The issue should be here:
string[] sourceFiles = new string[1];
If you move this line of code in your foreach you should solve this problem because in your foreach you always use the same variable, so the same reference.
int docCount = -1;
int i = 0;
List<Document> Documents = new List<Document>();
foreach (string file in filesCollected)
{
string[] sourceFiles = new string[1];
string bc;
string bcValue;
if (Settings.Default.barcodeEngine == "Leadtools")
{
bc = BarcodeReader.ReadBarcodeSymbology(file);
bcValue = "PatchCode";
}
else
{
bc = BarcodeReader.ReadBacrodes(file);
bcValue = "009";
}
if (bc == bcValue)
{
if(Documents.Count > 0)
{
Array.Clear(sourceFiles, 0, sourceFiles.Length);
Array.Resize<string>(ref sourceFiles, 1);
i = 0;
}
sourceFiles[i] = file ;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents.Add(new Document(sourceFiles, true,""));
docCount++;
}
else
{
if (Documents.Count > 0)
{
sourceFiles[i] = file;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents[docCount].fullFilePath = sourceFiles;
}
}
}

Collect Directory and Sub Directories with mapping and index in a List<>

I want to collect the directory listing in a collection(a List<> perhaps)
my directory structure is like :
MainFolder\ParentFolder1\SubFolder1
\SubFolder2
\SubFolder3
MainFolder\ParentFolder2\SubFolder1
\SubFolder2
\SubFolder3
I want to list all the subfolders mapped to their parent directories.
Also, the records will have index of ParentFolder 0-n in MainFolder and index of SubFolder 0-n in each ParentFolder.
I did tried below but not yet achieved
lstParents = (from f in Directory.GetDirectories(MainFolder)
select Data
{
parent =f
}).ToList();
var lstSubDir = (from f in lstParents.Select(m => Directory.GetDirectories(m.parent).ToList());
You can use this overload of the GetDirectories method to find all subdirectories recursively:
var mainDirectory = new DirectoryInfo(#"C:\temp\MainFolder");
var subDirectories = mainDirectory.GetDirectories("*", SearchOption.AllDirectories);
Then you can map them into pairs of directory/parent like this:
var mappedDirectories = subDirectories.Select(sd => new { Parent=sd.Parent, Child=sd });
If you want to exclude the first level of subdirectories (ParentFolder1 and ParentFolder2, in your case) you can filter them like this:
var mappedDirectories = subDirectories
.Where(sd => sd.Parent.FullName != mainDirectory.FullName)
.Select(sd => new { Parent=sd.Parent, Child=sd });
EDIT after you've asked for indices:
You stated, that you'll always only have a nesting level of 2, the following piece of code will not work for deeper directory structures.
var mainDirectory = new DirectoryInfo(#"C:\temp\MainFolder");
var firstLevelDirectories = mainDirectory.GetDirectories().Select((f1,i) => new {
Parent = f1,
ParentIndex = i
});
var secondLevelDirectories = firstLevelDirectories
.SelectMany(f1 => f1.Parent.GetDirectories().Select((f2,i) => new {
f1.Parent,
f1.ParentIndex,
Child = f2,
ChildIndex = i
} ));
This will give you a list of records, each containing
the parent directory,
the parent directory index,
the child directory and
the child directory index within its parent.
Try this recursive algorithm.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Folders folders = new Folders(#"c:\temp", null);
Console.ReadLine();
}
}
public class Folders
{
public string path { get; set; }
List<string> files = new List<string>();
List<Folders> folders = new List<Folders>();
Folders parent = null;
public Folders(string path, Folders parent)
{
this.parent = parent;
this.path = path;
foreach (string folderPath in Directory.GetDirectories(path))
{
Folders newFolder = new Folders(folderPath, this);
folders.Add(newFolder);
}
files = Directory.GetFiles(path).ToList();
int pathlength = path.Length;
Boolean first = true;
Console.Write(path);
if (files.Count == 0) Console.WriteLine();
foreach (string file in files)
{
string shortname = file.Substring(file.LastIndexOf("\\") + 1);
if (first)
{
Console.WriteLine("\\" + shortname);
first = false;
}
else
{
Console.WriteLine(new string(' ', pathlength + 1) + shortname);
}
}
}
}
}
​

Categories