DirectoryInfo().GetFileSystemInfos() - how to avoid hidden folders? - c#

If I do this:
var entries = new DirectoryInfo(#"C:\Folder1")
.GetFileSystemInfos("test*", SearchOption.AllDirectories);
when I have a folder structure like this, where the "HiddenFolder" is hidden but the "test.txt" file is not hidden:
C:\Folder1\
C:\Folder1\HiddenFolder\
C:\Folder1\HiddenFolder\test.txt
Then how can I code to NOT get the "test.txt" file?
(You might think that GetFileSystemInfos would skip hidden folders, but it doesn't.)
Here is a partial solution
This code strips out hidden files and folders, but doesn't handle a non-hidden file inside a hidden folder!
var includeLocalFolderNames = true; // to include folder names in the list
var hiddenFolders = new List<FileSystemInfo>();
return entries
.Where(entry =>
{
// skip if this entry is hidden, or it is in a hidden folder
var isHidden = (entry.Attributes & FileAttributes.Hidden) != 0;
if (isHidden)
{
hiddenFolders.Add(entry);
return false;
}
return !hiddenFolders.Any(fsi => entry.FullName.StartsWith(fsi.FullName));
})
.Where(entry =>
{
// include folder names if requested...
if (includeLocalFolderNames) return true;
return (entry.Attributes & FileAttributes.Directory) == 0;
})
.Select(entry => {
// do something...
return entry.Name;
});

Certainly not the most optimal and/or elegant solution.
var root = new DirectoryInfo(path);
var nonHiddenDirectories =
root.GetDirectories("*", SearchOption.AllDirectories).Where(
d => (d.Attributes & FileAttributes.Hidden) == 0).ToArray();
var nonHiddenFiles =
nonHiddenDirectories.SelectMany(d => d.GetFiles()).Where(
f => (f.Attributes & FileAttributes.Hidden) == 0);

Related

How can I check if folders are empty?

public Form1()
{
InitializeComponent();
if (textBoxRadarPath.Text != "")
{
if (!Directory.Exists(textBoxRadarPath.Text))
{
Directory.CreateDirectory(textBoxRadarPath.Text);
btnStart.Enabled = true;
}
}
if (textBoxSatellitePath.Text != "")
{
if (!Directory.Exists(textBoxSatellitePath.Text))
{
Directory.CreateDirectory(textBoxSatellitePath.Text);
btnStart.Enabled = true;
}
}
CheckIfImagesExist();
}
I'm checking if the folders in textBoxRadarPath.Text and textBoxSatellitePath.Text exist and create them if not. Then I call the method CheckIfImagesExist();
The problem is in the folder CheckIfImagesExist(); I'm also trying to get files :
There is more code in the method but the important is the GetImagesFiles method
private void CheckIfImagesExist()
{
GetImagesFiles();
}
And in GetImagesFiles
private void GetImagesFiles()
{
if (textBoxRadarPath.Text != "" || textBoxSatellitePath.Text != "")
{
if (Directory.Exists(textBoxRadarPath.Text))
{
if (checkBoxGetImages)
{
filesRadar = System.IO.Directory.GetFiles(textBoxRadarPath.Text,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
else
{
var t = new DirectoryInfo(textBoxRadarPath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
filesRadar = System.IO.Directory.GetFiles(t.FullName,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
Array.Sort(filesRadar, new MyComparer(true));
}
if (Directory.Exists(textBoxSatellitePath.Text))
{
if (checkBoxGetImages)
{
filesSatellite = System.IO.Directory.GetFiles(textBoxSatellitePath.Text,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
else
{
var t = new DirectoryInfo(textBoxSatellitePath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
filesSatellite = System.IO.Directory.GetFiles(t.FullName,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
Array.Sort(filesSatellite, new MyComparer(false));
}
}
}
Here I'm trying to get images from the first child folder under textBoxRadarPath.Text and textBoxSatellitePath.Text
The problem is if the directories textBoxRadarPath.Text and textBoxSatellitePath.Text not exist so they have created but they are empty yet so I'm getting exception on the line
var t = new DirectoryInfo(textBoxRadarPath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
Because the two directories just created or they are empty.
How can I check in the constructor if the directories are empty don't call the method CheckIfImagesExist() ?
Only if there are child folders inside then call the method CheckIfImagesExist()
The exception message :
System.InvalidOperationException: 'Sequence contains no elements'
On the line :
var t = new DirectoryInfo(textBoxRadarPath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
This screenshot show example of the folders structure on the hard disk.
Use FirstOrDefault when you are not sure there will be a resulting item
var t = new DirectoryInfo(textBoxRadarPath.Text)
.GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc)
.FirstOrDefault(); // <- this will return null if there are no entries
then before you start checking the files, make sure you found a directory.
if (t != null)
{
filesSatellite = System.IO.Directory
.GetFiles(t.FullName, "*.gif", SearchOption.AllDirectories)
.OrderBy(x => x)
.ToArray();
}

Directory.EnumerateFiles - Get Files which start with certain number and greater than that same number?

I am trying to get list of files which starts with "6" and greater than this number. I have following list of files in a folder. In case of failure of certain file consider 6_q.sql I want to begin with that file and proceed ahead in ascending order.
1_q.sql
2_q.sql
6_q.sql
7_q.sql
8_q.sql
This is my current code, however I used StartsWith and it only takes 6_q.sql file not rest 7_q.sql and 8_q.sql in this case.
var files = Directory.EnumerateFiles(SQLScriptsFolderPath, "*.sql")
.Where(filename => Path.GetFileName(filename).StartsWith("6"))
.OrderBy(filename => filename);
May I know what can I use to take files starting with 6 and great then that in acending order?
Edit 1 - Filenames can have any characters after number like 11qq.sql
If file order is not important for you, you can just order files by filename us SkipWhile:
var files = Directory.EnumerateFiles(SQLScriptsFolderPath, "*.sql")
.OrderBy(filename => filename)
.SkipWhile(filename => Path.GetFileName(filename) != failedFileName)
.Skip(1);
If ordering by number is important (and number at start of filename is always present) then you will need to parse that number, convert it to int and order by it:
var reg = new Regex(#"^\d+");
var files = Directory.EnumerateFiles(SQLScriptsFolderPath, "*.sql")
.Select(file => (n: int.Parse(reg.Match(Path.GetFileName(file)).Value), file))
.Where(t => t.n > failedFileNumber)
.OrderBy(t => t.n)
.Select(t => t.file);
First, write a small function to convert a file name to a number that can be compared. One way to do it:
int FileNumber(string input)
{
string numericString = new string(input.Where( c => char.IsDigit(c) ).ToArray());
bool ok = int.TryParse(numericString, out int result);
if (ok) return result;
return default(int);
}
Once you have that it's pretty easy:
var files = Directory.EnumerateFiles(SQLScriptsFolderPath, "*.sql")
.Where(filename => FileNumber(filename) >= 6 )
.OrderBy(filename => filename);
If you're picky about the file order you may need something slightly more complex:
var files = Directory.EnumerateFiles(SQLScriptsFolderPath, "*.sql")
.Select( x => new { FileName = x, FileNumber = FileNumber(x) })
.Where( x => x.FileNumber >= 6 )
.OrderBy( x => x.FileNumber );
.Select( x => x.FileName )
Try this:
static void Main(string[] args)
{
var files = Directory.EnumerateFiles(#"yourpath", "*.sql")
.Where(filename => GetNumberPart(Path.GetFileName(filename)) >= 6)
.Select(Path.GetFileName);
foreach (var file in files)
{
Console.WriteLine(file);
}
Console.ReadKey();
}
public static int GetNumberPart(string value)
{
return Convert.ToInt32(Regex.Match(value, #"^\d+").Value);
}

Get Filepath, and move the files in an other Directory if they contain an ID in the name

Basically, I need to check if a file exist in 4 version which mean that a 11 digits code appear in the filename.
Once the check is done I need to move the file on another Server.
My problem is that I get the ID, and I do know when an ID appear 4 times, but I don't know how to get the files Path from the ID I got and then move the files.
Any kind of help would be super appreciated.
static void Main(string[] args)
{
string ExtractIDFromFileName(string filename)
{
return filename.Split('_').Last();
}
Dictionary<string, int> GetDictOfIDCounts()
{
List<string> allfiles = Directory.GetFiles("C:/Users/Desktop/Script/tiptop", "*.txt").Select(Path.GetFileNameWithoutExtension).ToList();
allfiles.AddRange(Directory.GetFiles("C:/Users/Desktop/Script/tiptop", "*.top").Select(Path.GetFileNameWithoutExtension).ToList());
Dictionary<string, int> dict = new Dictionary<string, int>();
foreach (var x in allfiles)
{
string fileID = ExtractIDFromFileName(x);
if (dict.ContainsKey(fileID))
{
dict[fileID]++;
}
else
{
dict.Add(fileID, 1);
}
}
return dict;
}
var result = GetDictOfIDCounts();
foreach (var item in result)
{
//Console.WriteLine("{0} > {1}", item.Key, item.Value);
if (item.Value == 4)
{
//When we know that those ID appear 4 times, I need to grab back the FilePath and then move the files in an other DIR.
Console.WriteLine("{0} > {1}", item.Key, item.Value);
}
}
Console.ReadLine();
}
You'll want to use File.Move: https://learn.microsoft.com/en-us/dotnet/api/system.io.file.move?view=netframework-4.7.2
It's pretty straightforward. Make the ID in your dictionary the full path of the source file, then File.Move(dict.Key, some-variable-holding-the-new-directory-and-file-name);
Since you're going to want to use the filepath, just switch over to using the instance version of Directory & File: DirectoryInfo & FileInfo:
replace
List<string> allfiles = Directory.GetFiles("C:/Users/Desktop/Script/tiptop", "*.txt").Select(Path.GetFileNameWithoutExtension).ToList();
with
DirectoryInfo di = new DirectoryInfo("C:/Users/Desktop/Script/tiptop");
var allFiles = di.GetFiles("*.txt");
Make the FileInfo the key of the dictionary. Then you can do things like
dict.Key.FullName
Try this to get files you want, it uses GroupBy on the ID and Count(). I haven't compiled it so there might be errors.
var files = Directory.GetFiles(#"C:\Users\Desktop\Script\tiptop", "*.*")
.Where(file => {
var ext = Path.GetExtension(file).ToLower();
return ext == ".txt" || ext == ".top";
})
.Select(file => new { Path = file, Id = file.Split('_').Last()})
.GroupBy(file => file.Id)
.Where(grp => grp.Count() >= 4)
.SelectMany(x => x)
.Select(x => x.Path);
Another solution would be to use a Dictionary<string, List<string>> instead of Dictionary<string, int>. When adding a new key to the Dictionary you would add new List<string> { x } so that you would keep the file names in the list of ids. Instead of checking for the item.Value == 4 in you if you could check the size of the list like item.Count == 4. Then you still have the file names so you may use them to move the files.
Hope it helps!

Extract common path from a collection of grouped file paths?

I have a question related to https://en.wikipedia.org/wiki/Longest_common_substring_problem, my source collection contains a list of file paths that doesn't always share a common path (outside of the C:\ drive sometimes) ex:
Source collection :
C:\Test\Root\Common\Data\a.txt
C:\Test\Root\Common\Data\Home\b.txt
C:\Test\Root\Common\Data\Home\Dev\c.txt
C:\Test2\Random\Data\a.txt
C:\Test2\Random\b.txt
C:\Test2\c.txt
D:\Data\a.txt
Output should be a collection :
C:\Test\Root\Common\Data\
C:\Test2\
D:\Data\
How to find the common path of each "group" of file paths ? I've found many solutions here but it is always with a collection of file paths sharing at least one common directory which is not the case here.
I am still not sure that I understand problem correctly...
I hope this will work.
public List<string> ExtractCommonPaths(List<string> paths)
{
var separatedImput = paths
.Select(path => path.Split(new [] {":\\", "\\" }, StringSplitOptions.RemoveEmptyEntries))
.Select(path => path.Take(path.Length - 1).ToList());
return separatedImput.GroupBy(path => path[0] + ":\\" + path[1])
.Select(g =>
{
var commonPath = g.Key;
var commpoPathLength = 2;
for (;;)
{
var exit = false;
var pathItem = string.Empty;
foreach (var path in g)
{
if (path.Count <= commpoPathLength)
{
exit = true;
break;
}
if (pathItem == string.Empty)
pathItem = path[commpoPathLength];
else
{
if (pathItem != path[commpoPathLength])
{
exit = true;
break;
}
}
}
if (exit)
break;
commonPath += "\\" + pathItem;
commpoPathLength++;
}
return commonPath;
})
.ToList();
}
I have something if you want to look for the directories inside some location.
To this method i have every of directories from some (C:\Files1) example location.
If you want to get only main directories from this list:
public List<DirectoryInfo> ExtractDirectoriesCommonPaths(List<DirectoryInfo> directories, string location)
{
var result = new List<DirectoryInfo>() { };
directories.ForEach(directory =>
{
var otherDirectories = directories.Where(d => d.FullName != directory.FullName);
var anyDirectoryWithThisSamePath = otherDirectories.Where(x => directory.FullName.Contains(x.FullName) && x.FullName.Length < directory.FullName.Length);
if (anyDirectoryWithThisSamePath.Any())
{
result.Add(anyDirectoryWithThisSamePath.FirstOrDefault());
}
});
return result.Where(x => x.FullName != location && x.FullName.Length > location.Length).Distinct().ToList();
}
Input:
C:\Files1\test_announcements
C:\Files1\Parent
C:\Files1\test_announcements\test_announcements_archive
C:\Files1\Parent\child
C:\Files1\Parent\child2
C:\Files1\Parent\child\subchild
C:\Files1\test_announcements
C:\Files1\Parent
Output:
C:\Files1\test_announcements
C:\Files1\Parent

An item with the same key has already been added C#

I'm trying to browse all *.txt files from folder to get metadata inside.
void SearchAndPopulate(string path, string searchText)
{
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] files = di.GetFiles("*.txt");
Dictionary<String, dynamic> dictionary = new Dictionary<String, Object>();
int i = 0;
foreach (FileInfo file in files)
{
dictionary.Add(String.Format("name{0}", i.ToString()), i);
using (StreamReader sr = new StreamReader(file.FullName))
{
string content = sr.ReadToEnd().ToLower();
if (content.Contains(searchText.ToLower()))
{
dictionary["name"+i] = File
.ReadAllLines(file.FullName)
.Select(y => y.Split('='))
.Where(y => y.Length > 1)
.ToDictionary(y => y[0].Trim(), y => y[1]);
var temp = dictionary["name" + i];
listBox1.Text = temp["NUM_CLIENT"];
}
}
i++;
}
}
I get "An item with the same key has already been added" for dictionary variable.
This exception is thrown when you try to add a duplicate entry in a Dictionary using the same key. The key value in a Dictionary must be unique.
Possible causes
Look at the content of your file, you will find at least 2 lines where the left side of the = sign have the same string value.
You have multiple empty values on the left side of the = sign in your file. You can correct this in your Linq statement by ignoring those lines:
dictionary["name"+i] = File.ReadAllLines(file.FullName)
.Select(y => y.Split('='))
.Where(y => y.Length > 1 && !string.IsNullOrWhiteSpace(y[0]))
.ToDictionary(y => y[0].Trim(), y => y[1]);
Honestly I don't think you need any dictionaries in your code. Also you can reduce the number of times you read in each file.
void SearchAndPopulate(string path, string searchText)
{
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] files = di.GetFiles("*.txt");
foreach (FileInfo file in files)
{
var content = File.ReadAllLines(file.FullName);
if (content.Any(line => line.ToLower().Contains(searchText.ToLower())))
{
var numClient = content.Select(y => y.Split('='))
.Where(y => y.Length > 1 && y[0].Trim() == "NUM_CLIENT")
.Select(y => y[1])
.FirstOrDefault();
if(numClient != null)
listBox1.Text = numClient;
else
// Do something here if "NUM_CLIENT" wasn't in the file.
}
}
}

Categories