clarification on how to access numerous files from server in c# - c#

Basically I want to access 1000s of textfiles, input their data, store them in sqlite databases, parse them then show the output to users. So far I've developed a program that does this for only ONE textfile.
What I want to do: There is a Directory on our server which has about 15 folders. In each folder there are about 30-50 textfiles. I want to Loop through EACH FOLDER, and in each folder, loop through EACH file. A nice user helped me with doing this for 1000s of textfiles but I needed further clarification his method. This was his approach:
private static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
{
const string searchPattern = "*.txt";
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
foreach (var subDirectory in subDirectories)
{
ReadAllFilesStartingFromDirectory(subDirectory);//recursion
}
IterateFiles(filesInDirectory, topLevelDirectory);
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
foreach (var file in files)
{
Console.WriteLine("{0}", Path.Combine(directory, file));//for verification
try
{
string[] lines = File.ReadAllLines(file);
foreach (var line in lines)
{
//Console.WriteLine(line);
}
}
catch (IOException ex)
{
//Handle File may be in use...
}
}
}
My problems/questions:
1) topLevelDirectory - what should I exactly put there? The 15 folders are located on a server with the format something like this \servername\randomfile\random\locationoftopleveldirectory. But how can I put the double slashes (at the begining of the path name) in this? Is this possible in c#? I thought we could only access local files (example :"c:\" - paths with single, not double slashes)
2) I dont understand what the purpose of the first foreach loop is. "readAllFilesStartingFromDirectory(subDirectory)" , yes we are looping the folders, but we aren't even doing anything with that loop. It's just reading the folders.

I'm not going to know your top level directory, but essentially if your files are in C:\tmp, then you would pass it #"C:\tmp". Escape your string with the # character to get double-slashes (or escape each slash individually).
string example0 = #"\\some\network\path";
string example1 = "\\\\some\\network\\path";
With ReadAllFilesStartingFromDirectory you're recursively calling IterateFiles, and it's doing whatever IterateFiles does in each directory. With the code you pasted above, that happens to be doing nothing, since Console.Writeline(line) is commented out.

Lets get to clarify the topLevelDirectory: This is a folder, which has items in it. It does not matter if these are files or other directories. These caontained other "subfolders" can contain folders themselves.
What toplevelDirectory means to you: take the folder which encapsulates all your files you need at the lowest level possible.
Your toplevelfolder is the directory which contains the 15 folders you want to crawl.
ReadAllFilesStartingFromDirectory(string topLevelDirectory)
You need to realise what recursion means. Recursion describes a method which calls itself.
Compare the name of the function (ReadAllFilesStartingFromDirectory), with the name of the function called in the foreach loop - they are the same.
In you case: The method gets all folders located in your topfolder. He then loops through all subfolders. Each subfolder then becomes the toplevel folder, which in turn can contain subfolders, who will become toplevelfolders in the next method call.
This is a nice way to loop through the whole file structure. If there are no more subfolders, there won't be any recursion and the method ends.
Your path problem: You need to mask the backslashes. You mask them by adding a backslash in front of them.
\path\randfolder\file.txt will become \\path\\randfolder\\file.txt
Or you set an # before the string. var path = #"\path\randfolder\file.txt", which also does the trick for you. Both ways work

1) Yes it is possible in C#. If your program has access permission to the network location you can use: "\\\\servername\\randomfile\\random\\locationoftopleveldirectory" - double slash in string interpreated as one slash. or you can use # before the string, it means 'ignore escape character' which is slash, then your string will look like this: #"\\servername\randomfile\random\locationoftopleveldirectory"
2) ReadAllFilesStartingFromDirectory is recursive function. Directories structure is hierarchical, therefore it is easy to traverse them recursively. This function looks for files in the root directory and in its sub-directories and in all their sub directories...
Try to put comment on this loop and you will see that only the files of the root directory parsed by the IterateFiles function

Related

C# DirectoryInfo FullName and FullPath can't handle trailing spaces, how to fix?

string directorypath = #"C:\Folder01\Subfolder01\Next_level_Down ";
DirectoryInfo mydirectoryinfo = new DirectoryInfo(directorypath);
When the above code is run mydirectoryinfo.FullName and FullPath will cut off the trailing white space at the end of the last folder in directorypath. This seems like a bug?
This will cause a crash when I run:
DirectoryInfo[] mysubdirectories = mydirectoryinfo.GetDirectories();
As it will throw an exception "could not find a part of the path..."
I have a bunch of old folders that I am sorting and collecting data on so I need to get the DirectoryInfo on them, but some of them have been saved with white space at the end of the folder name. Rather than do a separate pass to correct folder names (which could break other connections to these folders) I was hoping on being able to get DirectoryInfo to handle the white space at the end of the folder name. If that is possible?
I have a list of directories that I've entered into a .CSV file. The paths are read into a List before being looped through to instance DirectoryInfo and run my checks, so there is no way to add more formatting that might help. I am using .Net Framework 4.7.2
I don’t know how you did create a Folder with a white space at the and but if it is possible add a backslash
string directorypath = #"C:\Folder01\Subfolder01\Next_level_Down \";
DirectoryInfo mydirectoryinfo = new DirectoryInfo(directorypath);
If your real path looks like the code above then delete the whitespace or the backslash. I think it is not possible to create a directory with a whitespace at the end and also why to put there a white space?
The last possible solution for your problem, that comes to my mind, is that the path is wrong. Somewhere in the middle is a character not correct, so that this exception will be thrown.
I hope i could help you
What also helps with such unorthodox path names not supported by the shell (see https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/file-folder-name-whitespace-characters) is prepending \\?\:
string directorypath = #"\\?\C:\Folder01\Subfolder01\Next_level_Down ";
DirectoryInfo mydirectoryinfo = new DirectoryInfo(directorypath);
From the documentation:
For file I/O, the "\?" prefix to a path string tells the Windows
APIs to disable all string parsing and to send the string that follows
it straight to the file system.

Trying to delete folders that are either empty, or only have hidden files within

I'm trying to clean up my lightroom folders and have found that sometimes there are hidden files left behind from moving the files from them around.
I did some searching and was able to build this Frankenstein function, but every time it tries to delete an empty folder I get an error saying that the folder is in use by another process....
Basically I am trying to recurs through all of the folders and delete the ones that are empty children, or ones that only have hidden files within. This process should repeat through all of the folders removing their children and eventually the parent if there are no files (or only hidden files) contained therein.
Any help or suggestions would be greatly appreciated!
Cheers!
private static void processDirectory(string startLocation)
{
//For every folder in this folder, recurse into that folder and take a peek...
foreach (var directory in Directory.GetDirectories(startLocation))
{
processDirectory(directory);
//Get a handle to the directory to get files and whatnot from....
DirectoryInfo di = new DirectoryInfo(directory);
FileInfo[] files = di.GetFiles();
//We want to ignore any hidden files in the directory
var filtered = files.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden));
//Make sure there are no other files or directories behind this one
if (filtered.Count() == 0 && Directory.GetDirectories(directory).Length == 0)
{
//Okay it's safe, delete it now.
di.Delete();
}
}
}
Well if this weren't bizarre enough, this morning when I ran the code it worked just fine!? My folders have been pruned down to only the ones that actually have photos in them, and life is good.
The one thing that I did have to do is add true to the di.Delete() function. This was because the function was stopping on folders that had hidden files in them.
I am assuming it was something along the lines of what Chris posted above and some latent function was still open from Adobe.
Thanks to everyone that posted a reply!
Cheers!

Converting absolute path to relative path C# [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Getting path relative to the current working directory?
I have code in C# that includes some images from an absolute path to a relative so the image can be found no matter where the application fold is located.
For example the path in my code (and in my laptop for the image) is
C:/something/res/images/image1.jpeg
and I want the path in my code to be
..../images/image1.jpeg
So it can run wherever the folder is put, whatever the name of the C: partition is etc.
I want to have a path in my code which is independant of the application folder location or if it is in another partition, as long as it is in the same folder as the the rest of the solution.
I have this code:
try
{
File.Delete("C:/JPD/SCRAT/Desktop/Project/Resources/images/image1.jpeg");
}
catch (Exception)
{
MessageBox.Show("File not found:C:/Users/JPD/Desktop/Project/images/image1.jpeg");
}
This code only runs if the file and folder are in that certain path, (which is also the location of the code) I wish for that path to be relative so wherever I put the whole folder (code, files etc) the program will still work as long as the code (which is under project folder) is at the same location with the folder images... what should I do?
Relative paths are based from the binary file from which your application is running. By default, your binary files will be outputted in the [directory of your .csproj]/bin/debug. So let's say you wanted to create your images folder at the same level as your .csproj. Then you could access your images using the relative path "../../images/someImage.jpg".
To get a better feel for this, try out the following as a test:
1) create a new visual studio sample project,
2) create an images folder at the same level as the .csproj
3) put some files in the images folder
4) put this sample code in your main method -
static void Main(string[] args)
{
Console.WriteLine(Directory.GetCurrentDirectory());
foreach (string s in Directory.EnumerateFiles("../../images/"))
{
Console.WriteLine(s);
}
Console.ReadLine(); // Just to keep the console from disappearing.
}
You should see the relative paths of all the files you placed in step (3).
see: Getting path relative to the current working directory?
Uri uri1 = new Uri(#"c:\foo\bar\blop\blap");
Uri uri2 = new Uri(#"c:\foo\bar\");
string relativePath = uri2.MakeRelativeUri(uri1).ToString();
Depending on the set up of your program, you might be able to simply use a relative path by skipping a part of the full path string. It's not braggable, so J. Skit might be up my shiny for it but I'm getting the impression that you simply want to make it work. Beauty being a later concern.
String absolutePath = #"c:\beep\boop\HereWeStart\hopp.gif";
String relativePath = absolutePath.Substring(13);
You could then, if you need/wish, exchange the number 13 (which is an ugly and undesirable approach, still working, though) for a dynamically computed one. For instance (assuming that the directory "HereWeStart", where your relative path is starting, is the first occurrence of that string in absolutePath) you could go as follows.
String absolutePath = #"c:\beep\boop\HereWeStart\hopp.gif";
int relativePathStartIndex = absolutePath.IndexOf("HereWeStart");
String relativePath = absolutePath.Substring(relativePathStartIndex);
Also, your question begs an other question. I'd like to know how you're obtaining the absolute path. Perhaps there's an even more clever way to avoid the hustle all together?
EDIT
You could also try the following approach. Forget the Directory class giving you an absolute path. Go for the relative path straight off. I'm assuming that all the files you're attempting to remove are in the same directory. If not, you'll need to add some more lines but we'll cross that bridge when we get there.
Don't forget to mark an answer as green-checked (or explain what's missing or improvable still).
String
deletableTarget = #"\images\image1.jpeg",
hereWeAre = Environment.CurrentDirectory;
MessageBox.Show("The taget path is:\n" + hereWeAre + deletableTarget);
try
{ File.Delete(hereWeAre + deletableTarget); }
catch (Exception exception)
{ MessageBox.Show(exception.Message); }
Also, please note that I took the liberty of changing your exception handling. While yours is working, it's a better style to rely on the built-in messaging system. That way you'll get more professionally looking error messages. Not that we ever get any errors at run-time, right? ;)

How to copy files from one disk to another location with the same folder structure?

I want to make an exact copy of some files, directories and subdirectories that are on my USB drive I:/ and want them to be in C:/backup (for example)
My USB drive has the following structure:
(just to know, this is an example, my drive has more files, directories and subdirectories)
courses/data_structures/db.sql
games/pc/pc-game.exe
exams/exam01.doc
Well, I am not sure how to start with this but my first idea is to get all the files doing this:
string[] files = Directory.GetFiles("I:");
The next step could be to make a loop and use File.Copy specifying the destination path:
string destinationPath = #"C:/backup";
foreach (string file in files)
{
File.Copy(file, destinationPath + "\\" + Path.GetFileName(file), true);
}
At this point everything works good but not as I wanted cause this doesn't replicate the folder structure. Also some errors happen like the following...
The first one happens because my PC configuration shows hidden files for every folder and my USB has an AUTORUN.INF hidden file that is not hidden anymore and the loop tries to copy it and in the process generates this exception:
Access to the path 'AUTORUN.INF' is denied.
The second one happens when some paths are too long and this generates the following exception:
The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the
directory name must be less than 248 characters.
So, I am not sure how to achieve this and validate each posible case of error. I would like to know if there is another way to do this and how (maybe some library) or something more simple like an implemented method with the following structure:
File.CopyDrive(driveLetter, destinationFolder)
(VB.NET answers will be accepted too).
Thanks in advance.
public static void Copy(string src, string dest)
{
// copy all files
foreach (string file in Directory.GetFiles(src))
{
try
{
File.Copy(file, Path.Combine(dest, Path.GetFileName(file)));
}
catch (PathTooLongException)
{
}
// catch any other exception that you want.
// List of possible exceptions here: http://msdn.microsoft.com/en-us/library/c6cfw35a.aspx
}
// go recursive on directories
foreach (string dir in Directory.GetDirectories(src))
{
// First create directory...
// Instead of new DirectoryInfo(dir).Name, you can use any other way to get the dir name,
// but not Path.GetDirectoryName, since it returns full dir name.
string destSubDir = Path.Combine(dest, new DirectoryInfo(dir).Name);
Directory.CreateDirectory(destSubDir);
// and then go recursive
Copy(dir, destSubDir);
}
}
And then you can call it:
Copy(#"I:\", #"C:\Backup");
Didn't have time to test it, but i hope you get the idea...
edit: in the code above, there are no checks like Directory.Exists and such, you might add those if the directory structure of some kind exists at destination path. And if you're trying to create some kind of simple sync app, then it gets a bit harder, as you need to delete or take other action on files/folders that don't exist anymore.
This generally starts with a recursive descent parser. Here is a good example: http://msdn.microsoft.com/en-us/library/bb762914.aspx
You might want to look into the overloaded CopyDirectory Class
CopyDirectory(String, String, UIOption, UICancelOption)
It will recurse through all of the subdirectories.
If you want a standalone application, I have written an application that copies from one selected directory to another, overwriting newer files and adding subdirectories as needed.
Just email me.

Get attributes of all files under a directory while accessing the directory only

I'm trying to write a function in C# that gets a directory path as parameter and returns a dictionary where the keys are the files directly under that directory and the values are their last modification time.
This is easy to do with Directory.GetFiles() and then File.GetLastWriteTime(). However, this means that every file must be accessed, which is too slow for my needs.
Is there a way to do this while accessing just the directory? Does the file system even support this kind of requirement?
Edit, after reading some answers:
Thank you guys, you are all saying pretty much the same - use FileInfo object. Still, it is just as slow to use Directory.GetFiles() (or Directory.EnumerateFiles()) to get those objects, and I suspect that getting them requires access to every file. If the file system keeps last modification time of its files in the files themselves only, there can't be a way to extract that info without file access. Is this the case here? Do GetFiles() and EnumerateFiles() of DirectoryInfo access every file or get their info from the directory entry? I know that if I would have wanted to get just the file names, I could do this with the Directory class without accessing every file. But getting attributes seems trickier...
Edit, following henk's response:
it seems that it really is faster to use FileInfo Object. I created the following test:
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now);
foreach (string file in Directory.GetFiles(#"\\169.254.78.161\dir"))
{
DateTime x = File.GetLastWriteTime(file);
}
Console.WriteLine(DateTime.Now);
DirectoryInfo dirInfo2 = new DirectoryInfo(#"\\169.254.78.161\dir");
var files2 = from f in dirInfo2.EnumerateFiles()
select f;
foreach (FileInfo file in files2)
{
DateTime x = file.LastWriteTime;
}
Console.WriteLine(DateTime.Now);
}
For about 800 files, I usually get something like:
31/08/2011 17:14:48
31/08/2011 17:14:51
31/08/2011 17:14:52
I didn't do any timings but your best bet is:
DirectoryInfo di = new DirectoryInfo(myPath);
FileInfo[] files = di.GetFiles();
I think all the FileInfo attributes are available in the directory file records so this should (could) require the minimum I/O.
The only other thing I can think of is using the FileInfo-Class. As far as I can see this might help you or it might read the file as well (Read Permissions are required)

Categories