I have a list of database items and a list of files. I am trying to find out what files are missing from the database. I read the database into a list DBItems. I read the files into another list:
List<DBFiles> DBItems = new List<DBFiles>();
ArrayList FileArray = Directory.GetFiles(#"C:\reports\", "*.rpt", SearchOption.AllDirectories);
public class DBFiles
{
public DBFiles(string fileName, string flag)
{
this.FileName = fileName;
this.Flag = flag;
}
public string FileName { set; get; }
public string Flag { set; get; }
}
My question is how do I look up if each item in FileArray is in DBFiles with a specific Flag. This is what I have so far:
private void ListCompare()
{
for (int i = 0; i < FileArray.Count; i++)
{
if (DBItems.FileName.Contains(FileArray[i]) && DBItems.Flag.Contains("A") )
{
}
}
}
Obviously it's not working. Any help would be sincerely appreciated.
Try using Linq: you want all the files in the directory Except the files in the DBItems:
var result = Directory
.EnumerateFiles(#"C:\reports\", "*.rpt", SearchOption.AllDirectories)
.Except(DBItems
.Where(item => item.Flag.Contains("A")) // add condition via Where in required
.Select(item => item.FileName)
,StringComparer.OrdinalIgnoreCase)
.ToArray(); // let's materialize into array
P.S. Try avoiding obsolete ArrayList class; put List<T> (List<string> in your case) instead.
Edit: according to the question refines (see comments) below the condition is more complicated.
I'm looking for files that start with h9347 (the database defines only
the first part of the file name for client specific needs
In this case I suggest creating a HashSet<string> of what to exclude, and again use Linq:
// File names in name.extension format to exclude
HashSet<string> toExclude = new HashSet<string>(DBItems
.Where(item => item.Flag.Contains("A")
.Select(item => item.FileName),
StringComparer.OrdinalIgnoreCase);
var result => Directory
.EnumerateFiles(#"C:\reports\", "*.rpt", SearchOption.AllDirectories)
.Where(file => Path.GetFileName(file).StartsWith("h9347") &&
!toExclude.Contains(Path.GetFileName(file)))
.ToArray();
Related
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)))
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 have some version folders like Vx_x_x
I want to retrieve the max folder version.
For example:
Folder contains,
V8_2_1
V9_3_2
V10_4_1
I want to check the max number next to V and so on to get a latest folder version.
I am able to get a list of folders, But confusion to get how can I get a max number. If anyone can suggest me would me a great helpful. Thank you.
private static void GetFolderVersion()
{
string startFolder = #"C:\Version\";
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);
IEnumerable<System.IO.DirectoryInfo> directoryList = dir.GetDirectories("*.*", System.IO.SearchOption.AllDirectories);
}
I would consider using the built-in System.Version type. Assuming all directory names are in the same format of "VX_Y_Z" (where X, Y and Z represent one or more digits, and V represents a literal "V"), the following code will do what you want:
public string GetMaxVersion(IEnumerable<string> directoryNames)
{
var vDict = directoryNames.ToDictionary(
s => new Version(s.Substring(1).Replace("_", ".")),
s => s);
var maxKey = vDict.Keys.Max();
return vDict[maxKey];
}
Here we build a dictionary of version to file name mappings (note that we change the string format from "VX_Y_Z" to "X.Y.Z" to be able to create a System.Version object). All that remains is to retrieve the max value of all dictionary keys, and return the value assigned to that given key, which will be the directory name you're looking for.
UPDATE: For completeness, here's a piece of code that uses the method above and takes care of everything:
public string GetMaxVersionDirectory(string rootDirectory)
{
var dirNames = Directory.GetDirectories(rootDirectory, "V*_*_*")
.Select(dir => Path.GetFileName(dir));
return GetMaxVersion(dirNames);
}
In your case, you need to pass #"C:\Version" as the rootDirectory parameter.
If the files all match the pattern you gave, I'd be tempted to extract the version information using a regular expression, then selecting the highest value, starting with the major version and working out.
Update: replace the V in the regular expression with the correct prefix for your case.
var regex = new Regex(#"^V(\d+)_(\d+)_(\d+)$", RegexOptions.Compiled);
var versions = directoryList
.Select(f => regex.Match(f))
.Where(m => m.Success)
.Select(m => new
{
Major = Int32.Parse(m.Groups[1].Value),
Minor = Int32.Parse(m.Groups[2].Value),
Patch = Int32.Parse(m.Groups[3].Value)
}).ToList();
var major = versions.Max(a => a.Major);
versions = versions
.Where(a => a.Major == major)
.ToList();
var minor = versions.Max(a => a.Minor);
versions = versions
.Where(a => a.Minor == minor)
.ToList();
var patch = versions.Max(a => a.Patch);
versions = versions
.Where(a => a.Patch == patch)
.ToList();
var newest = versions.First();
var filename = String.Format("V_{0}_{1}_{2}", newest.Major, newest.Minor, newest.Patch);
Although this probably does work, it's advisable to use the solution by #jbartuszek that uses the Version class.
Something along these lines could do it, but be aware that this is from the top of my head, written in notepad so might not build or contain some form of logical error. This just demonstrates the gist of it:
string[] version = folderName.Split('_');
string[] otherVersion = otherFolderName.Split('_');
then you write a method that checks the individual parts of the version:
private int CompareVersions(string[] version, string[] otherVersion)
{
//we won't need the first character (the 'V')
int vmajor = int.Parse(version[0].Substring(1));
int vminor = int.Parse(version[1]);
int vrevision = int.Parse(version[2]);
int ovmajor = int.Parse(otherVersion[0].Substring(1));
int ovminor = int.Parse(otherVersion[1]);
int ovrevision = int.Parse(otherVersion[2]);
int majorCompare = vmajor.CompareTo(ovmajor);
//check if major already decides outcome
if(majorCompare != 0)
{
return majorCompare;
}
int minorCompare = vminor.CompareTo(ovminor);
//then if major equal, check if minor decides outcome
if(minorCompare != 0)
{
return minorCompare;
}
//lastly, return outcome of revision compare
return vrevision.CompareTo(ovrevision);
}
this is how you'd compare two folder names. If you wanted to get the max folder version, you could foreach the folder names:
//we'll start out by assigning the first folder name as a preliminary max
string maxFolder = folderNames[1];
string[] maxFolderVersion = maxFolder.Split('_');
foreach(string folderName in folderNames)
{
if(CompareVersions(folderName.Split('_'), maxFolderVersion) > 0)
{
maxFolder = folderName;
}
}
You can use a mathematical approach. Let's say every single part of your version can go up to max. 1000. So the base is 1000. The version parts are your coefficents and the exponents are built in your loop. By summing up coefficent*base^exp you get a value which you can compare to get the highest version:
private static string GetHighestFolderVersion()
{
string startFolder = #"C:\Version\";
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);
IEnumerable<System.IO.DirectoryInfo> directoryList = dir.GetDirectories("*.*", System.IO.SearchOption.AllDirectories);
KeyValuePair<string, long> highestVersion = new KeyValuePair<string, long>("", 0);
foreach (System.IO.DirectoryInfo dirInfo in directoryList)
{
string versionOrigName = dirInfo.Name;
string versionStr = versionOrigName.Substring(1);
List<string> versionParts = versionStr.Split('_').ToList<string>();
long versionVal = 0;
int exp = 0;
for (int i = versionParts.Count - 1; i > -1; i--)
{
versionVal += (long.Parse(versionParts[i]) * (long)(Math.Pow(1000, exp)));
exp++;
}
if (versionVal > highestVersion.Value)
{
highestVersion = new KeyValuePair<string, long>(versionOrigName, versionVal);
}
}
return highestVersion.Key;
}
I'm having a hard time deduping a list based on a specific delimiter.
For example I have 4 strings like below:
apple|pear|fruit|basket
orange|mango|fruit|turtle
purple|red|black|green
hero|thor|ironman|hulk
In this example I should want my list to only have unique values in column 3, so it would result in an List that looks like this,
apple|pear|fruit|basket
purple|red|black|green
hero|thor|ironman|hulk
In the above example I would have gotten rid of line 2 because line 1 had the same result in column 3. Any help would be awesome, deduping is tough in C#.
how i'm testing this:
static void Main(string[] args)
{
BeginListSet = new List<string>();
startHashSet();
}
public static List<string> BeginListSet { get; set; }
public static void startHashSet()
{
string[] BeginFileLine = File.ReadAllLines(#"C:\testit.txt");
foreach (string begLine in BeginFileLine)
{
BeginListSet.Add(begLine);
}
}
public static IEnumerable<string> Dedupe(IEnumerable<string> list, char seperator, int keyIndex)
{
var hashset = new HashSet<string>();
foreach (string item in list)
{
var array = item.Split(seperator);
if (hashset.Add(array[keyIndex]))
yield return item;
}
}
Something like this should work for you
static IEnumerable<string> Dedupe(this IEnumerable<string> input, char seperator, int keyIndex)
{
var hashset = new HashSet<string>();
foreach (string item in input)
{
var array = item.Split(seperator);
if (hashset.Add(array[keyIndex]))
yield return item;
}
}
...
var list = new string[]
{
"apple|pear|fruit|basket",
"orange|mango|fruit|turtle",
"purple|red|black|green",
"hero|thor|ironman|hulk"
};
foreach (string item in list.Dedupe('|', 2))
Console.WriteLine(item);
Edit: In the linked question Distinct() with Lambda, Jon Skeet presents the idea in a much better fashion, in the form of a DistinctBy custom method. While similar, his is far more reusable than the idea presented here.
Using his method, you could write
var deduped = list.DistinctBy(item => item.Split('|')[2]);
And you could later reuse the same method to "dedupe" another list of objects of a different type by a key of possibly yet another type.
Try this:
var list = new string[]
{
"apple|pear|fruit|basket",
"orange|mango|fruit|turtle",
"purple|red|black|green",
"hero|thor|ironman|hulk "
};
var dedup = new List<string>();
var filtered = new List<string>();
foreach (var s in list)
{
var filter = s.Split('|')[2];
if (dedup.Contains(filter)) continue;
filtered.Add(s);
dedup.Add(filter);
}
// Console.WriteLine(filtered);
Can you use a HashSet instead? That will eliminate dupes automatically for you as they are added.
May be you can sort the words with delimited | on alphabetical order. Then store them onto grid (columns). Then when you try to insert, just check if there is column having a word which starting with this char.
If LINQ is an option, you can do something like this:
// assume strings is a collection of strings
List<string> list = strings.Select(a => a.Split('|')) // split each line by '|'
.GroupBy(a => a[2]) // group by third column
.Select(a => a.First()) // select first line from each group
.Select(a => string.Join("|", a))
.ToList(); // convert to list of strings
Edit (per Jeff Mercado's comment), this can be simplified further:
List<string> list =
strings.GroupBy(a => a.split('|')[2]) // group by third column
.Select(a => a.First()) // select first line from each group
.ToList(); // convert to list of strings
Directory.GetFiles() returns all files, even those that are marked as hidden. Is there a way to get a list of files that excludes hidden files?
This should work for you:
DirectoryInfo directory = new DirectoryInfo(#"C:\temp");
FileInfo[] files = directory.GetFiles();
var filtered = files.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden));
foreach (var f in filtered)
{
Debug.WriteLine(f);
}
// check whether a file is hidden
bool isHidden = ((File.GetAttributes(filePath) & FileAttributes.Hidden) == FileAttributes.Hidden);
Using .NET 4.0 and Directory.EnumerateDirectories, you could use this construct :
var hiddenFilesQuery = from file in Directory.EnumerateDirectories(#"c:\temp")
let info = new FileInfo(file)
where (info.Attributes & FileAttributes.Hidden) == 0
select file;
This is basically the same as the other answer, except Directory.EnumerateDirectories is a bit more lazy. This is not very useful if you enumerate everything, though.
(The let is here to have the query a but more readeable).
if use use:
var filtered = files.Select(f => f)
.Where(f => (f.Attributes & FileAttributes.Hidden) == 0);
this only find no hidden file, so you can use :
var filtered = files.Select(f => f)
.Where(f => (f.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden);
this is only to read the hidden file
One line Code:
FileInfo[] tmpFiles = tempDir.GetFiles().Where(file =>
(file.Attributes & FileAttributes.Hidden) == 0).ToArray();
I actually rather like passing a function parameter to a method which does what I want it to. I have a SearchDirectory method, which forms the basis for most of the calls I use:
private void SearchDirectory(DirectoryInfo startDirectory,
string pattern,
Action<FileInfo> act)
{
foreach (var file in startDirectory.GetFiles(pattern))
act(file);
foreach (var directory in startDirectory.GetDirectories())
SearchDirectory(directory, pattern, act);
}
private List<FileInfo> SearchDirectory(DirectoryInfo startDirectory,
string pattern,
Func<FileInfo, bool> isWanted)
{
var lst = new List<FileInfo>();
SearchDirectory(startDirectory,
pattern,
(fi) => { if (isWanted(fi)) lst.Add(fi); });
return lst;
}
Then you can use the other solutions listed to write an IsHidden function which takes a single FileInfo, and returns true if so:
private bool IsHiddenDirectory(DirectoryInfo d) {
if (d == null) return false;
if (d.Attributes.HasFlag(FileAttributes.Hidden))) return true;
if (d.Parent == null) return false;
return IsHiddenDirectory(d.Parent);
}
private bool IsHidden(FileInfo fi) {
if ((fi.Attributes & FileAttributes.Hidden) != 0) return true;
// If you're worried about parent directories hidden:
return IsHiddenDirectory(fi.Directory);
// otherwise:
return false;
}
Then I can call it in another method pretty easily:
var files = SearchDirectory(new DirectoryInfo("C:\temp\"),
"*.xml",
(fi) => { return !IsHidden(fi); );
If you're using SearchOption.TopDirectoryOnly - then it's relatively simple, however - it gets much more complex if you want to list all files recursively using SearchOption.AllDirectories. If can you GetFiles and then filter out read only, but unfortunately it will not work with directories marked as hidden. Files under that folders gets listed as well, but they are not hidden unlike directory.
You can use also GetDirectories, but again - you cannot list everything recursively using SearchOption.AllDirectories, since it also lists folders which resides under hidden folder, but those folders do not have hidden attribute enabled.
This is the case at least for Tortoise svn .svn hidden folder. It contains a lot of folders which are not hidden, but .svn is hidden. Finally I've wrote function which looks like this:
SearchOption sopt = SearchOption.AllDirectories;
List<String> listFiles = new List<string>();
List<DirectoryInfo> dirs2scan = new List<DirectoryInfo>();
dirs2scan.Add(new DirectoryInfo(fromPath) );
for( ; dirs2scan.Count != 0; )
{
int scanIndex = dirs2scan.Count - 1; // Try to preserve somehow alphabetic order which GetFiles returns
// by scanning though last directory.
FileInfo[] filesInfo = dirs2scan[scanIndex].GetFiles(pattern, SearchOption.TopDirectoryOnly);
foreach (FileInfo fi in filesInfo)
{
if (bNoHidden && fi.Attributes.HasFlag(FileAttributes.Hidden))
continue;
listFiles.Add(fi.FullName);
}
if( sopt != SearchOption.AllDirectories )
break;
foreach (DirectoryInfo dir in dirs2scan[scanIndex].GetDirectories("*", SearchOption.TopDirectoryOnly))
{
if (bNoHidden && dir.Attributes.HasFlag(FileAttributes.Hidden))
continue;
dirs2scan.Add(dir);
}
dirs2scan.RemoveAt(scanIndex);
}
sopt can be used a parameter in function if necessary or removed if not needed.
static bool IsHidden(string p)
{
return p.Contains("Hidden");
}
DirectoryInfo directory = new DirectoryInfo(#"C:\temp");
FileInfo[] files = directory.GetFiles();
var filtered = files.Where(f => !IsHidden(File.GetAttributes(f).ToString()));
foreach (var f in filtered)
{
Debug.WriteLine(f);
}
Steps:
Create bool that returns true when string contains 'Hidden' ---- static bool IsHidden(string p){return p.Contains("Hidden");}
get directory info ---- DirectoryInfo directory = new DirectoryInfo(#"C:\temp");
get file info array from directory ---- FileInfo[] files = directory.GetFiles();
get file info Attributes and convert into string from file info array and check it contains 'Hidden' or not ----
var filtered = files.Where(f=>!IsHidden(File.GetAttributes(f).ToString()));