I want to read 100s of textfiles. Theyre located in a directory which has about 15 folders and in those folders there are a number of textfiles. So the program should Loop through each folder and then loop through each file. Some nice users helped me come up with the code to do this, but I'm getting a number of errors and they all seem to be related.
The errors are numbered and marked as comments. I need help with applying the fix to each of them (new programmer so there are probably very obvious fixes to these which I probably overlooked).
private static void ReadAllFilesStartingFromDirectory(string // 1) Identifyer expected "#\\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic")
{
const string searchPattern = "*.txt";
string subDirectories = Directory.EnumerateDirectories( #"\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic");
// 2) cannot implicity convert systems.collections.generic.ieenumerable<string> to 'string'
string filesInDirectory = Directory.EnumerateFiles(//topLevelDirectory, searchPattern);
foreach (string subDirectory in subDirectories) // 3) windowsformapplication.form1.subdirectories is a 'field' but is used like a 'type';
{
ReadAllFilesStartingFromDirectory (subDirectory);
// 4) method must have return type
}
IterateFiles(filesInDirectory, topLevelDirectory);
// 5) A namespace cannot directly contain members such as fields or methods
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
int counter = 0;
foreach (var file in files)
{
// string[] filelines = File.ReadAllLines(file) and execute code;
}
}
Ok, lets try to tackle your issues one by one, before proposing a slightly different solution to your problem.
1) Identifyer expected "#\camis01srfs04\DATA\Stats Analysis Project\Sobeystats\Atlantic")
static void ReadAllFilesStartingFromDirectory(string )
You will need to provide a name for the arguments to a method, as in:
static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
2) cannot implicity convert systems.collections.generic.ieenumerable to 'string'
string subDirectories = Directory.EnumerateDirectories( #"\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic");
The return value of Directory.EnumerateDirectories is IEnumerable<string>, not string. You can change this code to one of the two lines below:
IEnumerable<string> subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
The same is true for this line (it returns an IEnumerable<string>):
string filesInDirectory = Directory.EnumerateFiles(//topLevelDirectory, searchPattern);
We can change it to
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
3) windowsformapplication.form1.subdirectories is a 'field' but is used like a 'type';
This error message should be gone now with the changes we have made in 2)
4) method must have return type
This error message should also be gone now with the changes we have made in 2)
5) A namespace cannot directly contain members such as fields or methods
This error message should also be gone now with the changes we have made in 2)
So this would make your program, with the corrections we just made look like this:
private static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
{
const string searchPattern = "*.txt";
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
foreach (string subDirectory in subDirectories)
ReadAllFilesStartingFromDirectory(subDirectory);
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
IterateFiles(filesInDirectory, topLevelDirectory);
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
int counter = 0;
foreach (var file in files)
{
var filelines = File.ReadAllLines(file)
//and execute code;
}
}
The method Directory.EnumerateFiles already supports searching subdirectories recursively, so you could also do this:
public static void ProcessAllFilesUnderDirectory(string topLevelDirectory, string searchMask, Action<string> processFile)
{
var files = Directory.EnumerateFiles(topLevelDirectory, searchMask, SearchOption.AllDirectories);
foreach (var file in files)
processFile(file);
}
private static void ProcessAFile(string fileName)
{
var lines = File.ReadAllLines(fileName);
// perform processing.
}
public static void Main(params string[] args)
{
ProcessAllFilesUnderDirectory(#"\camis01srfs04\DATA\Stats Analysis Project\Sobeys Stats\Atlantic", "*.txt", ProcessAFile);
}
Related
I am trying to access the files in the images directory that lies within another directory but when I run my code it doesn't print out anything:
string path = #"C:\Path";
DirectoryInfo DFolder = new DirectoryInfo(path);
Collection cDetails = new Collection(DFolder);
string DFPath = DFolder.Name;
DirectoryInfo imDetails = new DirectoryInfo(imPath);
// Get Desired Directories
List<string> directoryFilter = new List<string> {"images", "videos", "RAW"};
List<DirectoryInfo> directoryList = DFolder
.GetDirectories("*", SearchOption.AllDirectories)
.Where(x => directoryFilter.Contains(x.Name.ToLower()))
.ToList();
string dpath = directoryList.ToString();
foreach (DirectoryInfo record in directoryList)
{
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.TopDirectoryOnly))
{
Console.WriteLine(file); //It compiles but doesn't print anything on the console
}
}
Note: This isn't really an answer, so I'll delete it shortly, but wanted to give some sample code to test with in case it helps.
Your code works fine for me, so it seems that the problem is that either the directories don't exist, or they don't contain any files.
Here's a test program you can run which creates a bunch of directories under c:\temp, some of which have the names we care about. The names we care about are also found at different levels of depth in the path, yet they are all discovered:
static void CreateTestPathsAndFiles()
{
// Paths to create for testing. We will put a file in each directory below,
// but our search code should only print the file paths of those files that
// are directly contained in one of our specially-named folders
var testPaths = new List<string>
{
#"c:\temp\dummy1",
#"c:\temp\dummy1\raw", // This should print
#"c:\temp\dummy2",
#"c:\temp\dummy2\extra",
#"c:\temp\dummy3",
#"c:\temp\dummy3\dummy31",
#"c:\temp\dummy3\dummy32\raw", // This should print
#"c:\temp\extra",
#"c:\temp\images", // This should print
#"c:\temp\notUsed",
#"c:\temp\notUsed\videos", // This should print
#"c:\temp\raw", // This should print
#"c:\temp\videos\dummy1",
};
// Just something to make a unique file name
int fileId = 0;
// for testing, ensure that the directories exist and contain some files
foreach(var testPath in testPaths)
{
// Create the directory
Directory.CreateDirectory(testPath);
// Add a file to it
File.CreateText(Path.Combine(testPath, $"TempFile{fileId}.txt"))
.Write($"Dummy text in file {fileId}");
// Increment our file counter
fileId++;
}
}
static void Main(string[] args)
{
// Create our paths and files for testing
CreateTestPathsAndFiles();
// Now set our root directory, search for all folders matching our
// special folder names, and print out the files contained in them
var path = #"C:\Temp";
var directoryFilter = new List<string> {"images", "videos", "raw"};
// Get ALL sub-directories under 'path' whose name is in directoryFilter
var subDirectories = new DirectoryInfo(path)
.GetDirectories("*", SearchOption.AllDirectories)
.Where(x => directoryFilter.Contains(x.Name.ToLower()));
foreach (DirectoryInfo subDir in subDirectories)
{
foreach (FileInfo file in subDir.GetFiles(#"*", SearchOption.TopDirectoryOnly))
{
// We're using the FullName so we see the whole file path in the output
Console.WriteLine(file.FullName);
}
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
Note that the 5 files we expected to find are listed, but no others:
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.TopDirectoryOnly))
{
Console.WriteLine(file); //It compiles but doesn't print anything on the console
}
SearchOption.TopDirectoryOnly will only search files in C://Path/images but not its subfolders.
a possible fix for this is to simply change your 2nd foreach loop to look like this:
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.AllDirectories))
{
Console.WriteLine(file); //It compiles but doesn't print anything on the console
}
Edit:
Using SearchOption.AllDirectories as parameter is supposed to catch all cases of subfolders within your subfolder e.g. something like C://images//dir//somefile.txt instead of only taking the files within the topdirectory(in this case C://images). Which is(as i understood your question) exactly the kind of behaviour you were looking for.
Full code:
{
static void Main(string[] args)
{
// Directory Info
string path = #"C:\Path";
DirectoryInfo DFolder = new DirectoryInfo(path);
string DFPath = DFolder.Name;
// Get Desired Directories
List<string> directoryFilter = new List<string> { "images", "videos", "raw" };
List<DirectoryInfo> directoryList = DFolder.GetDirectories("*", SearchOption.AllDirectories).Where(x => directoryFilter.Contains(x.Name.ToLower())).ToList();
string dpath = directoryList.ToString();
foreach (DirectoryInfo record in directoryList)
{
foreach (FileInfo file in record.GetFiles(#"*", SearchOption.AllDirectories)) //searches directory record and its subdirectories
{
Console.WriteLine(file);
}
}
}
Final Edit: Sample output given the following structure:
C://Path//images//images.somefile.txt
C://Path//images//temp//images.temp.somefile.txt
C://Path//raw//raw.somefile.txt
C://Path//vidoes//videos.somefile.txt
I quite new to C# and have created a unit test project with two tests. However, I'm getting a weird result in one of the tests.
When i click "Run all tests", the test fails. But when i immediately click "Run Failed tests", the test passes.
What could the problem be please?
Here is the test method
[TestMethod]
public void GetFiles_WithNoIgnoreValueProvided_ReturnsAllFiles()
{
var path = #"C:\Users\myDocs\Desktop\New folder";
var thefiles = Filer.GetFiles(path) as List<string>;
Assert.AreEqual(3, thefiles.Count);
}
EDIT
The message on test failure is "Test Failed - Assert.Areequal failed. Expected: 3 Actual 2.
EDIT TWO:
Heres My Filer class. I am trying to create a static class that can be used to return all files in a directory (and any subdirectories recursively)
public static class Filer
{
private static List<string> ignorethesefiles;
private static List<string> thefiles;
public static IEnumerable<string> GetFiles(string path, IEnumerable<string> ignorefilescontaining = null)
{
if (ignorefilescontaining !=null)
{
ignorethesefiles = new List<string>(); ignorethesefiles.AddRange(ignorefilescontaining);
}
else
{
ignorethesefiles = new List<string>() { "#" };
}
if (File.Exists(path))
{
// This path is a file
ProcessFile(path, ref thefiles);
}
if (Directory.Exists(path))
{
// This path is a directory
//if (ignorethesefiles.Count > 0)
//{
ProcessDirectory(path, ref thefiles, ref ignorethesefiles);
}
return thefiles;
}
private static void ProcessDirectory(string path, ref List<string> thefiles, ref List<string> ignorethesefiles)
{
//Process files in the directory
IEnumerable<string> filesindir = Directory.GetFiles(path);
foreach (var filename in filesindir)
{
// if (ignorefilescontaining != string.Empty)
// if (ignorethesefiles.Count > 0)
// {
if (!ignorethesefiles.Any(s=>filename.Contains(s)))
{
ProcessFile(filename, ref thefiles);
}
// ProcessFile(filename, ref thefiles);
// }
}
//Recurse subdirectories
IEnumerable<string> subdirectories = Directory.GetDirectories(path);
foreach (var sub in subdirectories)
{
ProcessDirectory(sub, ref thefiles, ref ignorethesefiles);
}
}
private static void ProcessFile(string path, ref List<string> thefiles)
{
if (thefiles == null)
{
thefiles = new List<string>();
thefiles.Add(path);
}
else
{
thefiles.Add(path);
}
}
}
UPDATE:
Credit to #Matthewwatson for the following improvement (i think :) ) its certainly less verbose:
public static class SOFinder
{
public static IEnumerable<string>GetListOfFiles(string path,string pattern, SearchOption searchoption)
{
var files = Directory.GetFiles(path, pattern, SearchOption.AllDirectories);
return files;
}
}
Now reading up on LINQ to see how i can filter the result to produce a list that DOES NOT contain a given string. Or can someone give me a hint please?
Will also look to mock the file system then test again and update on the result. cheers
cheers
This can often happen because of a Remnant from the old test. if the unit test depends on something not existing, but the old Unit Test left something behind, it can have an issue. it really depends on the error message, which seems to be an inequality error. This could mean that something is creating a file/folder which collides with another file/folder in that directory. I would stick a breakpoint on the Unit Test and step into it. When the error gets thrown (you'll get a more detailed error), just read the message and you should get an explanation
It looks like you have a test dependency. Your other test is affecting the result of your failing test. I bet the other test looks at the same directory, it is then locking/removing a file, your other test is trying to look in the same directory but can't add one of the files because the other test has opened it.
I'm using a code to show all startup items in listbox with environment variable "%appdata%
There is some errors in this code that I need help with....
Check code for commented errors
Is there any other solution but still using %appdata%?
This is the code:
private void readfiles()
{
String startfolder = Environment.ExpandEnvironmentVariables("%appdata%") + "\\Microsoft\\Windows\\Start Menu\\Programs\\Startup";
foldertoread(startfolder);
}
private void foldertoread(string folderName)
{
FileInfo[] Files = folderName.GetFiles("*.txt"); // HERE is one error "Getfiles"
foreach (var file in Directory.GetFiles(folderName))
{
startupinfo.Items.Add(file.Name); // here is another error "Name"
}
}
This line won't work because folderName is a string (and does not have a GetFiles method):
FileInfo[] Files = folderName.GetFiles("*.txt");
The second error is occurring because the file variable is a string containing the filename. You don't need to call file.Name, just try the following:
startupinfo.Items.Add(file);
I don't think you need the following line:
FileInfo[] Files = folderName.GetFiles("*.txt");
The foreach loop will generate what you need.
Secondly, the file variable is a string, so rather than calling:
startupinfo.Items.Add(file.Name);
...call instead:
startupinfo.Items.Add(file);
Finally, instead of a var type for your loop, you can use a string, and you can specify the file type filter:
foreach (string fileName in Directory.GetFiles(folderName, "*.txt"))
The string object doesn't have a GetFiles() method. Try this:
string startfolder = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
string[] files = Directory.GetFiles(startfolder, "*.txt");
foreach (string file in files)
{
startupinfo.Items.Add(Path.GetFileNameWithoutExtension(file));
}
Path.GetFileNameWithoutExtension(file) returns just the file name instead of full path.
Is possible to get all directories, subdirectories and files with recursion.
I do this because i want to increase my programming logic, and learn how recursion work.
I know to do that with this way:
string path = "D://";
string rezdir,newpath;
DirectoryInfo di = new DirectoryInfo(path);
DirectoryInfo[] dir = di.GetDirectories().ToArray();
for (int i = 0; i < di.GetDirectories().Length; i++)
{
Console.WriteLine(dir[i].ToString());
}
Console.WriteLine("\n\nChoose File: ");
rezdir = Console.ReadLine();
newpath = path + rezdir;
di = new DirectoryInfo(newpath);
dir = di.GetDirectories().ToArray();
for (int i = 0; i < di.GetDirectories().Length; i++)
{
Console.WriteLine(dir[i].ToString());
}
Console.ReadKey();
But i don't do that with recursion way, so ff someone can to do this, i'll be grateful to him.
Going by the code you posted - you seem to want some user interaction - so try something like this:
public static class RecursiveTest
{
public static string Foo(DirectoryInfo currentPath)
{
if (!currentPath.Exists) return string.Empty;
foreach (var directory in currentPath.EnumerateDirectories())
Console.WriteLine("Directory {0}", directory.Name);
foreach (var file in currentPath.EnumerateFiles())
Console.WriteLine("File {0}", file.Name);
while(true)
{
Console.WriteLine("Choose directory or file: ");
string chosenPath = Console.ReadLine();
string newPath = Path.Combine(currentPath.FullName, chosenPath);
if(Directory.Exists(newPath))
return Foo(new DirectoryInfo(newPath));
if(File.Exists(newPath))
return newPath;
Console.WriteLine("File {0} doesn't exist!", newPath);
}
}
}
And call with something like this:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(RecursiveTest.Foo(new DirectoryInfo(#"d:\dev")));
Console.ReadLine();
}
}
HTH
I will avoid coding, because this is a valuable learning exercise. Try completing it yourself: once you do, you'll know that you understand recursion.
To be recursive, a method needs to call itself. Imagine that a method
public static void ShowDirectory(int indentationLevel, DirectoryInfo path)
is already written for you. This makes it easier to write the body:
Get all files in the directory, and print their names in the loop
Get all directories in the directory, and show their content at the next indentation level. You need another loop for that.
The first step is a simple exercise in writing loops. The second exercise becomes easy, too, because you can think of the ShowDirectory as pre-written.
Yeah, it's possible. But I do recommend that you first take a grasp of what recursion is. To put it simply, a recursion has a one-time executing part, and many-time executing part. That one time triggers the many-time part.
In this question, the one-time execution part might be to get the list of all directories beneath the root directory.
Then for each directory, you get all the sub-directories and files. This is the many-times part. However, to run a batch of codes many times, you need to bundle them into a callable routine, or procedure, or method, or function, whatever you call it. Just code bundle.
public void DoDirectories()
{
// one-time part; get a list of directories to start with.
List<string> rootDirectories = Directory.GetDirectories("c:\\").ToList();
foreach (string rootDirectory in rootDirectories)
{
GetSubdirectories(rootDirectory);
}
}
public List<string> GetSubdirectories(string parentDirectory)
{
List<string> subdirecotries = Directory.GetDirectories(
parentDirectory, "*.*", SearchOption.TopDirectoryOnly).ToList();
foreach (string subdirectory in subdirecotries)
{
GetSubdirectories(subdirectory); // recursing happens here
}
return subdirecotries;
}
I was trying to get the list of files in my remote directory and check only the file has name "test"; then copy to my local directory.
Just did a simple thing here but can someone please let me know the best way to handle this scenario.
class Program
{
static void Main(string[] args)
{
var getfiles = new fileshare.Program();
string[] filteredfiles =getfiles.GetFileList();
bool b;
foreach (string file in filteredfiles)
{
if(b=file.Contains("test"))
{
getfiles.copytolocal(file);
}
}
}
private string[] GetFileList()
{
string[] filepaths = Directory.GetFiles(#"\\testserver\dev");
return filepaths;
}
private void copytolocal(string filename)
{
File.Copy(filename, #"C:\" + filename);
}
}
Even i just stuck up when i was copy the file,the filename contains the whole directory inside the filename so filename look like "\\testserver\dev\test.txt". So it failed to copy in to local.
You can use DirectoryInfo to filter down to any file that contains the string "test":
private FileInfo[] GetFileList(string pattern)
{
var di = new DirectoryInfo(#"\\testserver\dev");
return di.GetFiles(pattern);
}
and then:
foreach (var file in GetFileList("*test*"))
{
getfiles.copytolocal(file.FullName);
}
You're looking for Path.GetFileName() (which returns a string).