I am trying to check whether a string is in an array and if continues even though the fileInfo.Name.Contains a string that is in files.Any:
\\FILES LIKE DATABASE.MDB IS IN C:PROJECTS\HOLON\DATABASE.MDB
**if (files.Any((fileInfo.Name.Contains)))**
\\DO SOMETHING
Console.WriteLine(
fileInfo.Name, fileInfo.Length,
If you alread have the filenames collected in an array, then you should either do it this way:
if (files.Any() && files.Contains(fileInfo.Name))
{
// Do something
}
If you just want to check if a file exists then you can use File.Exists:
if(System.IO.File.Exists(fileInfo.Name))
{
// Do Something
}
So you have a collection of full file paths? And you want to check if one or more of those list entries match with a specific file name?
Perhaps this would work for you:
string fileToSearch = "DATABASE.MDB";
bool found = files.Any(fileName => new FileInfo(fileName).Name.ToUpper() == fileToSearch.ToUpper());
Edit:
An alternative to constructing new FileInfo objects would be to use System.IO.Path:
bool found = files.Any(fileName => Path.GetFileName(fileName).ToUpper() == fileToSearch.ToUpper());
Edit 2:
On the other hand, if you want to search for a specific file name, and you want to use the result, you could do something like this:
var fileToSearch = "DATABASE.MDB";
var fileInfo =
(from f in files
let fi = new FileInfo(f)
where fi.Name.ToUpper() == fileToSearch.ToUpper()
select fi).FirstOrDefault();
if (fileInfo != null)
{
if (fileInfo.Exists)
{
Console.WriteLine($"{fileInfo.Name} ({fileInfo.Length} bytes).");
}
else
{
Console.WriteLine($"{fileInfo.Name} (does not exist).");
}
}
I used a LINQ query here for readability. You could use the extension methods (files.Select(f => new FileInfo(f)).Where(fi => fi.Name.ToUpper() == fileToSearch.ToUpper()).FirstOrDefault()) as well, but that's up to you.
if (Array.Exists(files, element => element.Contains(fileInfo.Name)))
Related
In short I'm building a treeview like structure of nodes. I'm looping through each folder and each file within the folder looking for specific file formats. I know the function im using for IsValidFileFormat is not ideal. I want to be able to pass it list of fileformats such as {.txt, .ms, .png} and upon first test returning true it returns True. That way it doesn't continue looping through the other formats if it doesn't need to.
The below example loops through each directory using a try catch so it doesn't error on folders which don't have permission.
// tests if given filepath has a compatible extension
static bool IsValidFileType(string filename)
{
bool results = false;
string ext = Path.GetExtension(filename);
// add multiple acceptable file extensions
if (string.Equals(".mse", ext, StringComparison.OrdinalIgnoreCase)) return true;
if (string.Equals(".ms", ext, StringComparison.OrdinalIgnoreCase)) return true;
return results;
}
public static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
{
try
{
// ignore direcotories which start with an 'underscore'
if (!directory.Name.StartsWith("_"))
{
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
}
catch (UnauthorizedAccessException) { }
}
foreach (var file in directoryInfo.GetFiles())
{
if (IsValidFileType(file.FullName))
{
TreeNode node = new TreeNode(file.Name, 1, 1);
node.Tag = file.FullName;
node.ForeColor = toolColor;
directoryNode.Nodes.Add(node);
// add to global fileList which is used for searches
fileList.Add(file.FullName);
}
}
// if (directoryNode.Nodes.Count != 0)
return directoryNode;
}
you could make an extension method like this:-
public static IEnumerable<FileInfo> GetFilesByExtensions(this DirectoryInfo dir,
params string[] extensions)
{
if (extensions == null)
throw new ArgumentNullException("extensions");
IEnumerable<FileInfo> files = dir.EnumerateFiles();
return files.Where(f => extensions.Contains(f.Extension));
}
and the use that like :
directoryInfo.GetFilesByExtensions(".ms",".mse")
which will return only the files you are interested in.
All you really need to do is create a list or array (etc) to hold the acceptable file types, then break as soon as you hit one.
static bool IsValidFileType(string filename)
{
bool results = false;
string ext = Path.GetExtension(filename);
List<string> fileTypes = new List<string>();
fileTypes.Add("exe");
fileTypes.Add("mse");
//etc
for (int i = 0; i < fileTypes.Count; i++)
{
if (ext == fileTypes[i])
{
results = true;
break;
//or just return true;
}
}
return results;
}
You could also move this list/array/whatever out to the class level and use the same list for different methods.
You can also use a LINQ query to acheive the same result:
results = fileTypes.Any(fileType => fileType.Equals(ext));
This will look through the list of fileTypes and return true if any match the right side of => -- In this case, String.Equals(ext).
Note that the LINQ method will return an ArgumentNullException if either fileTypes or ext is null, so if they CAN be null, make sure you check that first.
EDIT: If using the LINQ method, you'll still need to test for case. You can do that in a few different ways:
Your current method, using an overload of String.Equals: ... => fileType.Equals(ext, StringComparison.OrdinalIgnoreCase));
In-line: results = fileTypes.Any(fileType => fileType.ToLower().Equals(ext.ToLower()));
Same as above, but set the variable ext to lower case: 'string ext = Path.GetExtension(filename).ToLower();
etc.
Personally I think the StringComparison.OrdinalIgnoreCase is the cleaner way, the other methods are kind of ugly.
How are you fetching list of files? You can use Directory.EnumerateFiles Method (String, String, SearchOption) https://msdn.microsoft.com/en-us/library/dd383571(v=vs.110).aspx and use the searchPattern parameter. You just need to call it for every extension then concat the lists. This is an enumeration so it will only load the file info as you request it.
Note: you can also use Directory.GetFiles https://msdn.microsoft.com/en-us/library/ms143316(v=vs.110).aspx to load them immediately.
If I understand your questions correctly, you want your "IsValidFileType" function to accept a list of acceptable formats and see if a file matches one of them. You also want "true" returned at the first match. This should work:
static bool IsValidFileType(string filename, List<string> AcceptableExtensions)
{
string ext = Path.GetExtension(filename);
foreach (string AccExt in AcceptableExtensions)
if (ext.ToUpper() == AccExt.ToUpper())
return true;
return false;
}
Then you can call it like this:
List<string> AcceptableExtensions = new List<string>();
AcceptableExtensions.Add(".mse");
AcceptableExtensions.Add(".ms");
foreach (var file in directoryInfo.GetFiles())
{
if (IsValidFileType(file.FullName, AcceptableExtensions))
{
TreeNode node = new TreeNode(file.Name, 1, 1);
node.Tag = file.FullName;
node.ForeColor = toolColor;
directoryNode.Nodes.Add(node);
// add to global fileList which is used for searches
fileList.Add(file.FullName);
}
}
I run into examples like this all the time. In this case I want to populate a stringbuilder with a new line for each FileInfo object in a previously loaded variable called files, that of course contains a bunch of FileInfo objects. For the first object, I want to add FIRST after the text then for everything else I want to add NOTFIRST. To do this with a forloop, I have to setup a counter, do an if statement and increment the counter.
I've learned just enough linq that its on the tip of my fingers, but I know there has to be an elegant LINQ solution.
var mysb = new StringBuilder();
var count = 0;
string extra;
foreach (System.IO.FileInfo fi in files)
{
var newLine = fi.Name;
if (count == 0)
extra = "FIRST";
else
extra= "NOTFIRST";
count = count++;
mysb.AppendLine(string.Format("({0} {1})", newLine, extra));
}
Personally, I would forego the LINQ and stick with what you have, just simpler:
var mysb = new StringBuilder();
foreach (FileInfo fi in files)
{
string extra = mysb.Length == 0 ? "FIRST" : "NOTFIRST";
mysb.Append(fi.Name);
mysb.AppendLine(extra);
}
(It's not clear to me why you are treating the file name as a valid format string...of course, if it really is a valid format string, you can change my two calls to Append() and AppendLine() back to the single call with the string.Format())
You may use the overload of Select that gives you the current index: http://msdn.microsoft.com/pl-pl/library/bb534869(v=vs.110).aspx
I also don't like mutating state when using linq so I would use String.Join instead.
mysb.AppendLine(String.Join(Environment.NewLine,
files.Select((fi, i) => String.Format(fi.Name, i == 0 ? "FIRST" : "NOTFIRST"))));
As often as it happens I ask questions here, I went with a hybrid of suggetsions:
foreach (var fi in files)
{
var extra = (fi == files.First() ? "FIRST" : "NOTFIRST");
sb.AppendLine(fi.Name + extra);
}
I was unwilling to check the length of the stringbuilder, because I have other scenarios where extra pretty much requires using a linq function.
I suppose I could have just as easily done the following (for my stated example):
sb.AppendLine(files.First().Name + " FIRST");
sb.AppendLine(String.Join(Environment.NewLine,
files.Skip(1).Select( fi => fi.Name + " NOTFIRST")));
But to be honest, its half as readable.
I'm not suggesting that this is the best way to do this, but it was fun to write:
var fi = new [] { new { Name= "A"},
new { Name= "B"},
new { Name= "C"}};
String.Join(Environment.NewLine,
fi.Take(1).Select (f => Tuple.Create(f.Name,"FIRST"))
.Concat(fi.Skip(1).Select (f => Tuple.Create(f.Name,"NONFIRST")))
.Select(t=> String.Format("({0} {1})", t.Item1, t.Item2)))
.Dump();
I know how to get the last file, this the code:
string pattern = "Log*.xml";
string directory = set. Path;
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
My question is: How can I get the last file that not contain specific string? or in another words, how can I get the last file that not contain "This is temporally file" string?
Thank you!
from top of my head:
dirInfo.EnumerateFiles(pattern)
.OrderByDescending(f => f.LastWriteTime)
.Where(f => DoesntContain(f, myText))
.FirstOrDefault()
Now you are free to make DoesntContain as complex or simple as you want. Either use File.ReadAllText or something like:
bool DoesntContain(FileInfo fileInfo, string text) {
using (StreamReader r = fileInfo.OpenText()) {
var contents = r.ReadToEnd();
return !contents.Contains(text);
}
}
You can write the method as extension to get more natural syntax like fi.DoesntContain(...)
Additionally, I suggest using EnumerateFiles instead of GetFiles if the directory can contain many files: there is no need to retrieve them all, if the first one will match.
You can do something like this:
string pattern = "Log*.xml";
var dirInfo = new DirectoryInfo(directory);
var filesThatContains = dirInfo.GetFiles(pattern).
Where(f=>File.ReadAllLines(Path.Combine(directory, f.Name),
Encofing.UTF8).IndexOf(SEARCH_STRING)>=0);
I would do something simpler for a start:
public static string[] FileNamesExcluding(string path, string pattern, string textToExclude)
{
// Put all txt files in root directory into array.
string[] allFilesMatchingPattern = Directory.GetFiles(path, pattern); // <-- Case-insensitive
return allFilesMatchingPattern.SkipWhile(a => a.Contains(textToExclude)).ToArray();
}
To call this method you can do:
FileNamesExcluding(#"C:\", "*.sys", "config").Last();
I have two listboxes with several data, ListBox1 have this format:
C:\Users\Charlie\Desktop\Trials\Trial0004COP.txt
and in my ListBox2:
Trial0004COP
How can I check if Trial0004COP exists in my listbox1? I used Contain() but it doesn't work.
I would recommend something like:
var searchString = "Trial0004COP";
var isFound = false;
foreach(var name in listbox1.items)
{
if (Path.GetFileNameWithoutExtension(name).ToLower() == searchString.ToLower())
{
_IsFound = true;
break;
}
}
Note that in Windows, file names are case-preserving but not case-sensitive, so you need to check the file name ignoring its case.
You could do it in a single line in Linq, if that's your thing.
var searchString = "Trial0004COP";
var isFound = listBox1.Items.Cast<string>()
.Any(x => Path.GetFileNameWithoutExtension(x).ToLower() == searchString.ToLower());
What about:
bool found = listBox1.Items.Cast<string>()
.Where(x => x.Contains("Trial0004COP"))
.Any();
Or to make it more accurate use String.EndsWith() method but you also have to add ".txt" if you want to make it works:
bool found = listBox1.Items.Cast<string>()
.Where(x => x.EndsWith("Trial0004COP.txt"))
.Any();
Edit :
Hi Fuex, I'm using OpenFileDialog to select a file, then this file is
added to my listbox1 with all the Directory name. In my listbox2 I
read another file that contains several Trials000""" and add them to
it, I want to know if the file that I selected from the open dialog
exist in my listboz2
Yes can do it in this way:
bool found = false;
if(openFileDialog.ShowDialog() == DialogResult.OK){
found = listBox.Items.Cast<string>()
.Where(x => x.EndsWith(openFileDialog.FileName))
.Any();
if(!found)
MessageBox.Show(openFileDialog.FileName + " doesn't exist in listBox1"); //show a message
}
Using LINQ you can do:
listBox1.Items.Select(e => Path.GetFileNameWithoutExtension(e as string)).Contains("Trial0004COP");
I'm just trying to teach myself how to use Linq. This is what I have
if (FileReceivers.Exists(t => t.FileName == filename))
{
//I also want to do a c.Parent = proper FileReceiver
FileReceivers.Where(t=>t.FileName == filename).First().Clients.Add(c);
}
else
{
FileReceiver fr = new FileReceiver(filename);
fr.Clients.Add(c);
FileReceivers.Add(fr);
}
Any ideas how I would do this the right way? I don't really want to be using Linq to twice to grab the same thing, that would defeat the purpose.
I would just like to know the proper way to format this.
var fr = FileReceivers.FirstOrDefault(t=>t.FileName == filename);
if (fr == null) {
fr = new FileReceiver(filename);
FileReceivers.Add(fr);
}
fr.Clients.Add(c);
you could always pass the results of t => t.FileName == filename to an anonymous type and use that for later processing.