find a file name knowing only the extension name - c#

I am trying to verify if a file exist in a c# console program. The only thing is that the file can have any name.
The only thing that I know is the file extension and there can only be one file of this extension. How can I verify if it exist and then use it whatever the name is?

The problem with using Directory.GetFiles() is that is walks the entire filesystem first, then returns all matches as an array. Even if the very first file examined is the one and only match, it still walks the entire filesystem from the specified root before returning the one match.
Instead, use EnumerateFiles() to do a lazy walk, stopping when the first match is encountered, thus:
DirectoryInfo root = new DirectoryInfo( #"C:\" ) ;
string pattern = "*.DesiredFileExtension" ;
FileInfo desiredFile = root.EnumerateFiles( pattern , SearchOption.AllDirectories )
.First()
;
It will throw an exception if the file's not found. Use FirstOrDefault() to get a null value instead.

Try the Directory.GetFiles static method:
var fileMatches = Directory.GetFiles("folder to start search in", "*.exe", SearchOption.AllDirectories);
if (fileMatches.Length == 1)
{
//my file was found
//fileMatches[0] contains the path to my file
}
Note that with the SearchOption enum you can specify just the current folder or to search recursively.

string extension = "txt";
string dir = #"C:\";
var file = Directory.GetFiles(dir, "*." + extension).FirstOrDefault();
if (file != null)
{
Console.WriteLine(file);
}
If the file does not exist directly under 'dir', you will need to use SearchOption.AllDirectories for Directory.GetFiles

Something like this may work
if (Directory.GetFiles(path, "*.ext").Any())
{
var file = Directory.GetFiles(path, ".ext").First();
}

Related

How to Remove Directories From EnumerateFiles?

So I'm working on a program that will list all the files in a directory. Pretty simple. Basically, when I do this: List<string> dirs = new List<string>(Directory.EnumerateFiles(target));, I don't want it to include the directory and all. Just the file name. When I run my code;
List<string> dirs = new List<string>(Directory.EnumerateFiles(target));
Console.WriteLine($"Folders and files in this directory:\n");
foreach (string i in dirs) {
Console.WriteLine($"> {i}");
}
it gives me the following:
C:\Users\Camden\Desktop\Programming\Visual Studio\C#\DirectoryManager\DirectoryManager\bin\Debug\DirectoryManager.exe
I just want the DirectoryManager.exe part, so I looked it up and I found that you can replace strings inside of strings. Like so: i.Replace(target, "");. However, this isn't doing anything, and it's just running like normal. Why isn't it replacing, and how should I instead do this?
Use methods from the System.IO.Path class.
var fullfile = #"C:\Users\Camden\Desktop\Programming\Visual Studio\C#\DirectoryManager\DirectoryManager\bin\Debug\DirectoryManager.exe";
var fileName = Path.GetFileName(fullfile); // DirectoryManager.exe
var name = Path.GetFileNameWithoutExtension(fullfile); // DirectoryManager
The simplest way is to use the Select IEnumerable extension
(you need to have a using Linq; at the top of your source code file)
List<string> files = new List<string>(Directory.EnumerateFiles(target)
.Select(x => Path.GetFileName(x)));
In this way the sequence of files retrieved by Directory.EnumerateFiles is passed, one by one, to the Select method where each fullfile name (x) is passed to Path.GetFileName to produce a new sequence of just filenames.
This sequence is then returned as a parameter to the List constructor.
And about your question on the Replace method. Remember that the Replace method doesn't change the string that you use to call the method, but returns a new string with the replacement executed. In NET strings are immutable.
So if you want to look at the replacement you need
string justFileName = i.Replace(target, "");
An alternative to using Directory.EnumerateFiles, would be DirectoryInfo.EnumerateFiles. This method returns an IEnumerable<FileInfo>. You can then make use of the FileInfo.Name property of each of the returned objects. Your code would then become:
var files = new DirectoryInfo(target).EnumerateFiles();
Console.WriteLine("Files in this directory:\n");
foreach (FileInfo i in files) {
Console.WriteLine($"> {i.Name}");
}
For just the list of file names:
List<string> fileNames = new DirectoryInfo(target).EnumerateFiles().Select(f => f.Name).ToList();
Alternatively, if you want both files and directories, you can use EnumerateFileSystemInfos. If you need to know if you have a file vs a directory you can query the Attributes property and compare it to the FileAttributes flags enumeration.
var dirsAndFiles = new DirectoryInfo(target).EnumerateFileSystemInfos();
Console.WriteLine("Folders and files in this directory:\n");
foreach (var i in dirsAndFiles) {
var type = (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory ? "Directory" : "File";
Console.WriteLine($"{type} > {i.Name}");
}
The FileSystemInfo.Name property will return either the file's name (in case of a file) or the last directory in the hierarchy (for a directory)--so just the subdirectory name and not the full path ("sub" instead of "c:\sub").

Searching with System.IO.Directory.GetFiles and wildcards in path

I have a curious problem in a C#-program.
I have some local folderpaths like
"C:\test\AB_Systems\ELEGANCE\CB-DOC\live\M7-091.249.99.XX.01\extobjects".
Now i want to search for PDF-files in the subfolder called "extobjects".
Unfortunately there are many subfolders in the folder "live", which got a subfolder called "extobjects", so i thought it would be better to use a wildcard in the searchpath like that:
"C:\test\AB_Systems\ELEGANCE\CB-DOC\live\*\extobjects"
But this doesn't work.
Is there a way do do this?
public static FileInfo[] findFile(String whereToSearch, String searchFor , String mode)
{
IEnumerable<FileInfo> files = null;
if (mode.Equals(""))
mode = "s";
if (searchFor.Equals(""))
searchFor = "*";
if (mode.Equals("r") || mode.Equals("recursive"))
{
DirectoryInfo dir = new DirectoryInfo(whereToSearch);
files = dir.EnumerateFiles(searchFor, searchOption: SearchOption.AllDirectories);
}
if (mode.Equals("s") || mode.Equals("specific"))
{
DirectoryInfo dir = new DirectoryInfo(whereToSearch);
files = dir.EnumerateFiles(searchFor, searchOption: SearchOption.TopDirectoryOnly);
}
if (files != null) return files.ToArray<FileInfo>();
else return null;
}
That's an example how to do it.
It's important to say that only the filename can contain a wildcard pattern like *. The Path can be given as where to start the search and by giving searchOption: searchOption.AllDirectories as an argument it will go through all sub-directories of the entry path.
You will receive an Array of FileInfo which objects that contain the the path and more information.
You can use Linq like this:
var files = Directory
.EnumerateDirectories(#"C:\test\AB_Systems\ELEGANCE\CB-DOC\live", "extobjects", SearchOption.AllDirectories)
.SelectMany(x => Directory.EnumerateFiles(x, "*pdf", SearchOption.TopDirectoryOnly))
.ToArray();
I'd choose a solution exactly what BugFinder proposed, you could optimize the following foreach-loop into a LINQ query if your .NET target supports it.
// Itterate subdirectories of the live folder
foreach (var subDir in Directory.GetDirectories(#"C:\test\AB_Systems\ELEGANCE\CB-DOC\live"))
{
// Check if path to extobjects exists
var extObjects = Path.Combine(subDir, "extobjects");
if (Directory.Exists(extObjects))
{
var pdfFiles = Directory.GetFiles(extObjects, "*").Where(x=>x.EndsWith(".pdf"));
// Do something with the pdf file paths
}
}

How to get folder in directory by part of the name

I am trying to figure out how to get the name of a folder in certain directory, but the folder name I need is generated randomly, but its name always ends in .user (for example the folder could be 1245fa.user or WRf5.user).
How can I do that?
Here is how I am getting a folder with a name that never changes
string slug = #"%userprofile%\AppData\Roaming\MyApp\Profiles\constantuser\";
string filePath = Environment.ExpandEnvironmentVariables(slug);
string targetPath = #"C:\ErrorLog";
string sourceFile = System.IO.Path.Combine(filePath, p);
string destFile = System.IO.Path.Combine(targetPath, p);
In this case I would need the constantuser to be the folder that ends with .user
You can try Where() (or First(), Single(), ...) LINQ extensions on Directory.EnumerateDirectories().
Assuming the following code (note the slug ending in the "parent" directory):
var slug = #"%userprofile%\AppData\Roaming\MyApp\Profiles\";
var parentDirectoy = Environment.ExpandEnvironmentVariables(slug);
If you know for sure there's exactly one directory matching the criterion, you can do this:
var directoryName = Directory.EnumerateDirectories(parentDirectory)
.SingleOrDefault(dir => dir.EndsWith(".user"));
if (directoryName != null)
{
// do your thing
}
If you use Single instead of SingleOrDefault, a missing directory or more than one directory will result in an Exception instead of returning null.
If there may be multiple directories, you can do this instead:
var directoryNames = Directory.EnumerateDirectories(parentDirectory)
.Where(dir => dir.EndsWith(".user"));
foreach (var directoryName in directoryNames)
{
// do your thing
}
Where the loop may either run zero times if no matches are found or multiple times; one for each match.
System.IO.Directory.GetDirectories(filePath, "*.user")

How to search for Files using GetFiles method (multiple criteria..)

The code below obviously searches a directory for Files that contain the word "FINAL" but what I'm wondering is can I add to its search criteria? I have a Well_Name and Actual_Date strings that I would like to search for in the File names in addition to the "FINAL" word. Thoughts? Thanks in advance.
DirectoryInfo myDir = new DirectoryInfo("C://DWGs");
var files = myDir.GetFiles("FINAL");
//Can I do something like this to add to my search criteria?
var files = myDir.GetFiles("FINAL" +
drow["Well_Name"].ToString() +
drow["Actual_Date"]);
var files = myDir.GetFileInfo()
.Where(f => f.FileName.Contains("FINAL") ||
f.FileName.Contains(drow["Well_Name"].ToString()) ||
f.FileName.Contains(drow["Actual_Date"]));
Since GetFiles() returns an Enumerable Collection of FileInfo you can just check all of the file names for the criteria that you want.
If you want to get really generic on this you could write a function that looks like this
public IEnumerable<FileInfo> addCriteria(IEnumerable<FileInfo> FileList,
List<String> searchCriteria)
{
var newFileList = FileList;
foreach(String criteria in searchCriteria)
{
newFileList = newFileList.Where(f => f.FileName.Contains(criteria).AsQueryable();
}
return newFileList.AsEnumerable();
}
GetFiles method does not support multiple search criteria, but there is a simple way around this limitation. Run a getFile for each file extension, and then "merge" returned arrays into a List<>. Then use a List's ToArray method to "convert" a List back to an Array.
I used this approach, and it works for me
The code is below (do not forget to reference "using System.Collections.Generic;" namespace):
// Get the DirectoryInfo and FileInfo objects for aspx and html files.
FileInfo[] files_aspx = dir.GetFiles("*.aspx");
FileInfo[] files_html = dir.GetFiles("*.html");
List<FileInfo> files = new List<FileInfo>();
files.AddRange(files_aspx);
files.AddRange(files_html);
files.ToArray();

Delete files from directory if filename contains a certain word

I need to check a directory to see if there are any files whose file name contains a specific keyword and if there are, to delete them. Is this possible?
For example, delete all existing files in "C:\Folder" whose file name contains the keyword "Apple".
To expand on Henk's answer, you need:
string rootFolderPath = #"C:\\SomeFolder\\AnotherFolder\\FolderCOntainingThingsToDelete";
string filesToDelete = #"*DeleteMe*.doc"; // Only delete DOC files containing "DeleteMe" in their filenames
string[] fileList = System.IO.Directory.GetFiles(rootFolderPath, filesToDelete);
foreach(string file in fileList)
{
System.Diagnostics.Debug.WriteLine(file + "will be deleted");
// System.IO.File.Delete(file);
}
BE VERY CAREFUL!
Note that I've commented out the delete command. Run it and test it carefully before you let it actually delete anything!
If you wish to recursively delete files in ALL subfolders of the root folder, add ,System.IO.SearchOption.AllDirectories); to the GetFiles call.
If you do this it is also a very good idea to refuse to run if the rootFolderPath is less than about 4 characters long (a simple protection against deleting everything in C:\ - I've been there and done that and it's not fun!!!)
You can use System.IO.Directory.GetFiles() to a list of the files, in string[] format.
Then you can use System.IO.File.ReadAllText() to read complete files, or if they are very big, open a TextReader with System.IO.File.OpenText().
If you are looking for a literal keyword, String.Contains() is all you need.
Deleting a file can be done with System.IO.File.Delete(). Make sure the file is closed again.
Edit, 2 examples of GetFiles():
string[] fileNames = System.IO.Directory.GetFiles(#"C:\");
string[] fileNames = System.IO.Directory.GetFiles(#"C:\", #"*.sys");
new List<string>(Directory.GetFiles(#"C:\Folder")).ForEach(file => {
if (file.IndexOf("apple", StringComparison.OrdinalIgnoreCase) >= 0)
File.Delete(file);
});
or
new List<string>(Directory.GetFiles(#"C:\Folder")).ForEach(file => {
Regex re = new Regex("apple", RegexOptions.IgnoreCase);
if (re.IsMatch(file))
File.Delete(file);
});
More or less, this:
string DeleteThis = "apple";
string[] Files = Directory.GetFiles(#"C:\Folder");
foreach (string file in Files)
{
if (file.ToUpper().Contains(DeleteThis.ToUpper()))
{
File.Delete(file);
}
}
new List<string>(Directory.GetFiles(#"C:\Folder")).ForEach(file => { if (file.ToUpper().Contains("apple".ToUpper())) File.Delete(file); });

Categories