What would be the fastest way to search for a file programtically in C#. I know the relative location of the file, lets say its "abcd\efgh\test.txt". I also know that this file is on my E:\ drive. "abcd" is a subdirectory on some directory in E:\ drive.
Thanks
Since you know the root directory you want to search and a string pattern for a filename, you can create a DirectoryInfo with the root directory:
DirectoryInfo dir = new DirectoryInfo(#"E:\");
And then call GetFiles() to get all the matches. Passing SearchOption.AllDirectories will ensure the search is recursive.
List<FileInfo> matches =
new List<FileInfo>(dir.GetFiles(partialFilename,
SearchOption.AllDirectories));
Or if you know part of the path (instead of the filename):
List<DirectoryInfo> matches =
new List<DirectoryInfo>(dir.GetDirectories(partialDirectoryName,
SearchOption.AllDirectories));
And then you can navigate to the file from there.
So if I understand correctly you know the path will be of the form:
"E:\\" + something + "\\abcd\\efgh\\test.txt"
If something is only 1 level deep, then simply get all directories on your E:, and then try to do a file open on each of those subdirectories.
If something is more than 1 level deep, then do a recursive call to get the directories until you find abcd.
The fastest way would probably be to use FindFirstFile etc api but I would have thought that you could do it fairly quickly (And much easier) with Directory.GetDirectories (to find the right sub direcotry) and then Directory.GetFiles to find the actual file.
Just the use File.Exists method and iterate through all possible filenames. Maybe something like this (not tested):
string SearchInFolder(string root) {
if (File.Exists(Path.Append(root, "\\abcd\\efgh\\test.txt")) return Path.Append(root, "\\abcd\\efgh\\test.txt");
foreach(var folder in Directory.GetDirectories(root)) {
var fullFile = Path.Append(folder, "\\abcd\\efgh\\test.txt");
if (File.Exists(fullFile)) {
// Found it !!!!
return fullFile;
} else {
var result = SearchInFolder(folder);
if (result != null) return result;
}
}
return null;
}
But this will seach the whole E:\ drive for the pattern, also subfolders are searched.
I think the algorithm would be start at e:\ and read all directories. If one of them is \abcd\, immediately check for the rest of the path and file, e.g., efgh\test.txt.
If e:\ does NOT have \abcd\, then you need to traverse into each subdirectory and do the same check. Does \abcd\ exist? Yes, check for efgh\text.txt. No? Traverse subdirs.
If you need the absolutely fastest, when you fail to find \abcd\, and you have your list of subdirs you must now go check, you could introduce some level of threading. But that could get hairy rather quickly.
Related
I am trying to get the full path a file by its name only.
I have tried to use :
string fullPath = Path.GetFullPath("excelTest");
but it returns me an incorrect path (something with my project path).
I have read somewhere here a comment which says to do the following:
var dir = Environment.SpecialFolder.ProgramFilesX86;
var path = Path.Combine(dir.ToString(), "excelTest.csv");
but I do not know where the file is saved , therefore I do not know its environment.
can someone help me how to get the full path of a file only by its name?
The first snippet (with Path.GetFullPath) does exactly what you want. It returns something with your project path because the program EXE file is located in the project\Bin\Debug path, which is therefore the "current directory".
If you want to search for a file on a drive, you can use Directory.GetFiles, which will recursively search for a file in a directory given a name pattern.
This returns all xml-files recursively :
var allFiles = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
http://msdn.microsoft.com/en-us/library/ms143316%28v=vs.100%29.aspx
http://msdn.microsoft.com/en-us/library/ms143448.aspx#Y252
https://stackoverflow.com/a/9830162/2196124
I guess you're trying to find file (like in windows search), right ?
I'd look into this question - you will find all files that has that string in their filename, and from there you can return full filepath.
var fileList = new DirectoryInfo(#"c:\").GetFiles("*excelTest*", SearchOption.AllDirectories);
And then just use foreach to do you manipulations, e.g.
foreach(string file in fileList)
{
// MessageBox.Show(file);
}
What you're looking for is Directory.GetFiles(), you can read up on it here. The gist of it is, you'll pass in the file path and the file name, and you'll get a string array back. In this instance, you can assume top level with C:\. It should be noted, that if nothing is found, the string array will be empty.
You have passed a relative file name to Path.GetFullPath. Microsoft documentation states:
If path is a relative path, GetFullPath returns a fully qualified path that can be based on the current drive and current directory. The current drive and current directory can change at any time as an application executes. As a result, the path returned by this overload cannot be determined in advance.
You cannot get the same full path name from a relative path unless your current directory is the same each time you invoke the function.
I'm using C# to get the exact path of the specific folder in windows system by giving the folder name. Is their any way to get the folder path by giving the folder name, where the folder name will be unique.
Update:
Folder is created at run time with current time as the name. This
process is done by the application. Here i know the folder name but i
didn't know path, because path is selected by the user during
installation and installation is done before very long time.
That changes the question considerably. Why not use the application to tell you where it lives:
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.startuppath.aspx
I had a similar idea ages ago and wrote about it as a Code Project Tip:
http://www.codeproject.com/Tips/132804/Open-folders-using-a-Run-Command
Otherwise you would need to index every folder on the PC and make them unique names and look up the full path that way.
The other suggestion I have is using LogParser as the Most efficient way to find all exe files on disk using C#? Its a free Microsoft product but I'm not sure about re-dist permissions, I had to include it in my package separately last time I used it. It full on flys, faster than a speeding train!
I found a Log Parser example that finds folders, you could try it out and adapt it if its useful:
SELECT TOP 1 * FROM C:\TFS\Project\*.* WHERE INDEX_OF(Path, 'Database') > 0
The good folks over at http://visuallogparser.codeplex.com/ have
provided us with the source code.
Open the VisualLogParser solution in VS2010, ignore the prompt about debugging, after the solution loads, F5, set the combo-box to FS (FileSystem), paste in this query and press go.
You could probably use something like this, but it'll be rather slow, depending on how many folders needed to be looked through.
Use it like FindFullPath(rootFolder, folderNameToLookFor)
public static string FindFullPath(string path, string folderName)
{
if (string.IsNullOrWhiteSpace(folderName) || !Directory.Exists(path))
{
return null;
}
var di = new DirectoryInfo(path);
return findFullPath(di, folderName);
}
private static string findFullPath(DirectoryInfo directoryInfo, string folderName)
{
if (folderName.Equals(directoryInfo.Name, StringComparison.InvariantCultureIgnoreCase))
{
return directoryInfo.FullName;
}
try
{
var subDirs = directoryInfo.GetDirectories();
return subDirs.Select(subDir => findFullPath(subDir, folderName)).FirstOrDefault(fullPath => fullPath != null);
}
catch
{
// DirectoryNotFound, Security, UnauthorizedAccess
return null;
}
}
See following link
string dirName = new DirectoryInfo(#"c:\projects\roott\wsdlproj\devlop\beta2\text").Name;
I'm about to develop a software in C# that must select random folders (in a scenario with 10.000 folders more or less) that follow these rules:
select only the ones which contains files;
the software must stop the selection when the size of selected folder is 8GB;
when I copy a single folder, I need to keep the whole path of that folder (if c:\folder\temp\hello is the copied one, I want to keep d:\COPIED\folder\temp\hello);
I think I will do somethings like:
analyze the whole list of folder starting from an assigned root;
select random "line" in this list, moving it to the "selected list", counting the size;
when I reach 8GB, I stop this first phase, and I start to copy it;
I think here it is not a big trouble. What do you think about? Any suggestions?
My real problem will to "recreate" the whole path for each single folder when I move it.
How can I do it? Create folder for each level with C# API or is there a workaround?
So the last paragraph is the question? I understand it in the following way:
How to create a new path that contains the same path as the source but
with a different root?
Then you can use the Path class and it's static methods + String.Substring to get the new path.
D:\Copied is your root destination folder which you use in Path.Combine. Then you have to add the old-path without it's root-folder(there is no method in Path for this, i'll use Substring):
var rootDest = #"D:\Copy"; // your root directory
var pathSource = #"C:\Test\Test.txt"; // a sample file
var root = Path.GetPathRoot(pathSource); // C:\
var oldPathWithoutRoot = pathSource.Substring(root.Length); // Test\Test.txt
var newPath = Path.Combine(rootDest, oldPathWithoutRoot); // D:\Copy\Test\Test.txt
Then use File.Copy to copy all files in the folder from the old to the new path.
You have to check if the directory exists and create it otherwise:
var targetDir = Path.GetDirectoryName(newPath);
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir); // D:\Copy\Test
}
File.Copy(pathSource, newPath);
We use absolut path or relatvie path to find a file in our C# application right now. Is there a way to find the file just by its name if the file is under the currect working directory or under one of the "paths"?
Use absolute pathe is not good and use relative path is not good enough because we may change project structure by rename or move project folders. If we our code can automatically search current working directory, its subfolders and search system path, that will be more flexible.
thanks,
You can easily build a recursive function to do this for you. Look at Directory.GetDirectories and Directory.GetFiles, both under System.IO
Try this:
string target = "yourFilenameToMatch";
string current = Directory.GetCurrentDirectory();
// 1. check subtree from current directory
matches=Directory.GetFiles(current, target, SearchOption.AllDirectories);
if (matches.Length>0)
return matches[0];
// 2. check system path
string systemPath = Environment.GetEnvironmentVariable("PATH");
char[] split = new char[] {";"};
foreach (string nextDir in systemPath.Split(split))
{
if (File.Exists(nextDir + '\\' + target)
{
return nextDir;
}
}
return String.Empty;
You could call Directory.GetFiles for each root folder in which you want to search for the file. the parameter searchOption allow you to specify whether the search operation look in all subdirectories or only the directory specified. E.g:
public string GetFileName(string[] folders,string fileName) {
string[] filePaths;
foreach(var folder in folders) {
filePaths=Directory.GetFiles(folder,fileName,SearchOption.AllDirectories)
if (filePaths.Lenght>0)
return filePaths[0];
}
}
Try this:
Directory.EnumerateFiles(pathInWhichToSearch, fileNameToFind, SearchOption.AllDirectories);
And, you need to use:
using System.IO;
on top of your class.
This searches all subdirectories of pathInWhichToSearch for a file with name fileNameToFind (it can be a pattern too - like *.txt) and returns result as IEnumerable<string> with full paths of found files.
You can get the exe's directory using
Path.GetDirectoryName(Application.ExecutablePath);
Example Code: http://www.csharp-examples.net/get-application-directory/
And then, you can search the folders from there using recursion. This is a good article on recursive searching for files:
http://support.microsoft.com/kb/303974
I have a DB that contains a list of paths to files. I want to build a routine to cleanup the folders, removing files in the directories if there is not a db record for it (for temp ajax file uploads, in cases where the user doesn't complete the form, etc...).
I'm thinking something like this:
var dbFiles = db.allPaths();
var allFiles = Directory.EnumerateFiles(path);
foreach (var f in allFiles) {
if (!dbFiles.Contains(f) {
File.Delete(f);
}
}
Any "Gotchas" waiting for me? The routine will be set to run once a week at first, more often if temp files become a problem. It will be run during a time when there are nearly no users on, so performance - while important - is not paramount.
UPDATE
Wow, lots of great answers. This bit of code is turning into something "share" worthy. ;D My code above was just a simple, quick placeholder bit... but it's transformed into solid code. Thank you!
Looks okay, but you can make it simpler:
foreach (var file in allFiles.Except(dbFiles))
{
File.Delete(file);
}
You've got to make sure that the paths are in exactly the same format though. If one list has relative files and the other has absolute files, or if one uses "/" and the other uses "\" you'll end up deleting things you don't expect to.
Ideally you'd canonicalise the files explicitly first, but I can't see a nice way of getting a canonical file name in .NET...
EDIT: Note that Path.GetFullPath does not canonicalize. It fixes slashes and makes it absolute, but it doesn't address case: "c:/users" becomes "c:\users", but "c:/Users" becomes "c:\Users".
This could be fixed by using a string comparer in the call to Except:
var dbFiles = db.AllPaths().Select(Path.GetFullPath));
var allFiles = Directory.EnumerateFiles(path).Select(Path.GetFullPath));
foreach (var file in allFiles.Except(dbFiles, StringComparer.OrdinalIgnoreCase))
{
File.Delete(file);
}
Now that's ignoring case - but in an "ordinal" manner. I don't know what the Windows file system really does in terms of its case sensitivity.
Looks good to me; however I've never deleted files within C#, just VB.
However, you might want to throw that into a Try/Catch loop, as if the file isn't able to be deleted (read-only, currently in use, no longer exists, etc.), it will throw an exception.
EDIT: How are the paths stored? Remember, in C# you need to escape out paths "//" instead of using "\" IIRC.
EDIT 2: Scratch that last edit out lol.
To combine all the suggestions into one:
// canonicalize paths
var dbFiles = db.allPaths().Select(Path.GetFullPath);
var allFiles = Directory.EnumerateFiles(Path.GetFullPath(path))
foreach (var file in allFiles.Except(dbFiles, StringComparer.OrdinalIgnoreCase))
{
try {
File.Delete(file);
} catch (IOException) {
// handle exception here
}
}
I think it's alright in spirit, though it would be closer to:
List<string> dbFiles = db.allPaths();
string[] allFiles = Directory.GetFiles(path);
foreach (string f in allFiles)
if (!dbFiles.Contains(f))
File.Delete(f);