Hi i'm a c# begginer and i'd like to do a simple program which is going to go through a folder and count how many files are .mp3 files and how many are .flac .
Like I said the program is very basic. It will ask for the music folder path and will then go through it. I know there will be a lot of subfolders in that main music folder so it will have to open them one at the time and go through them too.
E.g
C:/Music/
will be the given directory.
But it doesn't contain any music in itself.
To get to the music files the program would have to open subfolders like
C:/Music/Electronic/deadmau5/RandomAlbumTitle/
Only then he can count the .mp3 files and .flac files and store them in two separated counters.
The program will have to do that for at least 2000 folders.
Do you know a good way or method to go through files and return its name (and extension)?
You can use System.IO.DirectoryInfo. DirectoryInfo provides a GetFiles method, which also has a recursive option, so if you're not worried about speed, you can do this:
DirectoryInfo di = new DirectoryInfo(#"C:\Music");
int numMP3 = di.GetFiles("*.mp3", SearchOption.AllDirectories).Length;
int numFLAC = di.GetFiles("*.flac", SearchOption.AllDirectories).Length;
Use DirectoryInfo and a grouping by the file extension:
var di = new DirectoryInfo(#"C:/Music/");
var extensionCounts = di.EnumerateFiles("*.*", SearchOption.AllDirectories)
.GroupBy(x => x.Extension)
.Select(g => new { Extension = g.Key, Count = g.Count() })
.ToList();
foreach (var group in extensionCounts)
{
Console.WriteLine("There are {0} files with extension {1}", group.Count,
group.Extension);
}
C# has a built in method of searching for files in all sub-directories. Make sure you add a using statement for System.IO
var path = "C:/Music/"
var files = Directory.GetFiles(path, "*.mp3", SearchOption.AllDirectories);
var count = files.Length;
Since you're a beginner you should hold off on the more flexible LINQ method until later.
int fileCount = Directory.GetFiles(_Path, "*.*", SearchOption.TopDirectoryOnly).Length
Duplicate question How to read File names recursively from subfolder using LINQ
Jon Skeet answered there with
You don't need to use LINQ to do this - it's built into the framework:
string[] files = Directory.GetFiles(directory, "*.dll",
SearchOption.AllDirectories);
or if you're using .NET 4:
IEnumerable<string> files = Directory.EnumerateFiles(directory, "*.dll",
SearchOption.AllDirectories);
To be honest, LINQ isn't great in terms of recursion. You'd probably want to write your own general-purpose recursive extension method. Given how often this sort of question is asked, I should really do that myself some time...
Here is MSDN support page, How to recursively search directories by Visual C#
Taken directly from that page:
void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d, txtFile.Text))
{
lstFilesFound.Items.Add(f);
}
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
You can use this code in addition to creating FileInfo objects. Once you have the file info objects you can check the Extension property to see if it matches the ones you care about.
MSDN has lots of information and examples, for example how you can iterate through a directory: http://msdn.microsoft.com/en-us/library/bb513869.aspx
Related
Part of my program involves validating 2 directories if both contain at least 1 file from a given list of allowed extensions.
if (DoesDirectoryHaveValidFiles(directory1) &&
DoesDirectoryHaveValidFiles(directory2))
My current issue is that, given a directory tree that is deep enough, or having too much sub-directories, my current implementation takes too much time for what it does.
Can anyone give me some help how I can speed up my checking?
bool DoesDirectoryHaveValidFiles(string directory)
{
var allowedExtensions = new string[] {".aaa", ".bbb", ".ccc"};
var directoryInfo = new DirectoryInfo(directory);
var fileInfos = directoryInfo.GetFiles("*.*", SearchOption.AllDirectories)
.Where(file => allowedExtensions.Any(file.FullName.ToLower().EndsWith));
return fileInfos.Count() > 0;
}
UPDATE: After SBFrancies's help for the .Where to .Any, and Dour High Arch's help for the file.Extension, I have replaced it with the following code, and I think it is now fast enough for what I need it to do.
But if you guys have other comments about my implementation, please dont hesitate to inform me, as Im still learning with regards to these parts.
bool DoesDirectoryHaveValidFiles(string directory)
{
var allowedExtensions = new string[] {".aaa", ".bbb", ".ccc"};
var directoryInfo = new DirectoryInfo(directory);
var hasValidFiles = directoryInfo.GetFiles("*.*", SearchOption.AllDirectories)
.Any(file => allowedFileExtensions.Contains(file.Extension));
return hasValidFiles;
}
DirectoryInfo d = new DirectoryInfo(mypath);//Assuming Test is your Folder
FileInfo[] Files = d.GetFiles("*.jpg"); //Getting Text files
How the files array could be order by name?
let's say
files[0].Name is 'hi1.jpg'
files[1].Name is 'hi2.jpg'
and so on
It is just a call to OrderBy in Linq namespace
using System.Linq;
....
FileInfo[] Files = d.GetFiles("*.jpg").OrderBy(x => x.Name).ToArray();
By the way, I suggest you to use EnumerateFiles instead of GetFiles. In particular if you have to loop over the result like this
foreach(FileInfo fi in d.EnumerateFiles("*.jpg").OrderBy(x => x.Name))
Console.WriteLine(fi.Name);
As explained in the MSDN documentation
The EnumerateFiles and GetFiles methods differ as follows: When you
use EnumerateFiles, you can start enumerating the collection of
FileInfo objects before the whole collection is returned. When you
use GetFiles, you must wait for the whole array of FileInfo objects to
be returned before you can access the array.
FileInfo[] Files = d.GetFiles("*.jpg").OrderBy(f => f.Name).ToArray();
Using Linq
d.GetFiles("*.jpg").OrderBy(file=> file.Name).ToArray();
FileInfo[] files = d.GetFiles("*.jpg").OrderBy(file => file.Name).ToArray();
As answered above .ToArray() function along with OrderBy(columnName) does the job.
I am trying to extract a list of files within a folder and am currently using:
string[] files = Directory.GetFiles(txtbxNewFolder.Text);
But that returns things like "C:\Users\Dahlia\Desktop\New Folder\jerry.txt". Is there a way to return only "jerry.txt", or do I need to do some sort of split on the array strings?
I am also trying to return a list of folders within a directory and am currently using:
string[] folders = Directory.GetDirectories(txtbxOldFolder.Text);
But that returns things like "C:\Users\Dahlia\Desktop\New Folder\folder1". Is there a way to return only "folder1", or do I need to do some sort of split on the array strings?
Using LINQ you can get a list of just the files:
Directory.GetFiles(txtbxNewFolder.Text).Select(f => Path.GetFileName(f));
Though rather than GetFiles I'd probably use:
Directory.EnumerateFiles(txtbxNewFolder.Text).Select(f => Path.GetFileName(f));
It isn't as simple to get the directory name, but this should work (untested):
Directory.GetDirectories(txtbxOldFolder.Text)
.Select(d => new DirectoryInfo(d).Name);
Similarly, there is a:
Directory.EnumerateDirectories(txtbxOldFolder.Text)
.Select(d => new DirectoryInfo(d).Name);
You could use Path.GetFileName and LINQ
e.g.:
string[] files = Directory.GetFiles(txtbxNewFolder.Text)
.Select(f => Path.GetFileName(s))
.ToArray();
Have a look at the FileInfo and DirectoryInfo classes.
You can do:
foreach (String file in files) {
var fi = new FileInfo(file);
Console.Out.WriteLine(fi.Name);
}
Similar for DirectoryInfo.
I'm trying to iterate through all the files on a certain level in the folder hierachy, more specifically, in all the sub-sub folders. Before I do actual operations on the files, I also want to count all the files to be able to show a progress bar. This means the iterating method must be called 2 times. This is the relevant code, I'm using now:
Iterate(bool count)
{
foreach (string dir in Directory.GetDirectories(root))
foreach (string subdir in Directory.GetDirectories(dir))
foreach (string file in Directory.GetFiles(subdir))
{
if (count) progressBar.Maximum++;
else
{
//do operations
}
}
}
I'm wondering if there's a better way of doing this. Surely there must be a better way than adding a foreach for every folder level..?
It'd be easier to me to use LINQ here.
var files =
(from dir in Directory.GetDirectories(root)
from subdir in Directory.GetDirectories(dir)
from f in Directory.GetFiles(subdir)
select f).ToList();
var fileCount = files.Length;
foreach (var f in files) {
...
}
Before your GetFiles foreach, try this:
string [] fileEntries = Directory.GetFiles(subdir);
int intFileCount = fileEntries.length;
Or it can replace it, if the loop only serves to count the files.
The documentation of Directory.GetFiles shows how to recursively iterate a directory tree
you could download the fluent thing I wrote for System.IO (see here: http://blog.staticvoid.co.nz/2011/11/staticvoid-io-extentions-nuget.html) and then use this LINQ statement
var files = from d in di.Directories()
from dir in d.Directories()
from f in dir.Files()
select f;
write this instead your code
string [] files = Directory.GetFile("yourDirectory","*.*",SearchOptions.AllDirectories);
this will return all files in sub-directories instead of using recursion
I have a folder with two files:
Awesome.File.20091031_123002.txt
Awesome.File.Summary.20091031_123152.txt
Additionally, a third-party app handles the files as follows:
Reads a folderPath and a searchPattern out of a database
Executes Directory.GetFiles(folderPath, searchPattern), processing whatever files match the filter in bulk, then moving the files to an archive folder.
It turns out that I have to move my two files into different archive folders, so I need to handle them separately by providing different searchPatterns to select them individually. Please note that I can't modify the third-party app, but I can modify the searchPattern and file destinations in my database.
What searchPattern will allow me to select Awesome.File.20091031_123002.txt without including Awesome.File.Summary.20091031_123152.txt?
If your were going to use LINQ then...
var regexTest = new Func<string, bool>(i => Regex.IsMatch(i, #"Awesome.File.(Summary)?.[\d]+_[\d]+.txt", RegexOptions.Compiled | RegexOptions.IgnoreCase));
var files = Directory.GetFiles(#"c:\path\to\folder").Where(regexTest);
Awesome.File.????????_??????.txt
The question mark (?) acts as a single character place holder.
I wanted to try my meager linq skills here... I'm sure there is a more elegant solution, but here's mine:
string pattern = ".SUMMARY.";
string[] awesomeFiles = System.IO.Directory.GetFiles("path\\to\\awesomefiles");
IEnumerable<string> sum_files = from file in awesomeFiles
where file.ToUpper().Contains(pattern)
select file;
IEnumerable<string> other_files = from file in awesomeFiles
where !file.ToUpper().Contains(pattern)
select file;
This assumes there aren't any other files in the directory other than the two, but you can adjust the pattern here to suit your needs (i.e. add "Awesome.File" to the pattern start.)
When you iterate the collection of each, you should get what you need.
According to the documentation, searchPattern only supports the ***** and ? wildcards. You would need to write your own regex filter that takes the results of Directory.GetFiles and applies further filtering logic.
If you don't want to use Linq, here's one way.
public void FileChecker(string filePath)
{
DirectoryInfo di = new DirectoryInfo(filePath);
int _MatchCounter;
string RegexPattern = "^[a-zA-Z_a-zA-Z_a-zA-Z_0-9_0-9_0-9.csv]*$";
Regex RegexPatternMatch = new Regex(RegexPattern, RegexOptions.IgnoreCase);
foreach (FileInfo matchingFile in di.GetFiles())
{
Match m = RegexPatternMatch.Match(matchingFile.Name);
if ((m.Success))
{
MessageBox.Show(matchingFile.Name);
_MatchCounter += 1;
}
}
}