I want to check if two strings are paths to the same file or folder.
I've seen two questions dealing with this:
C# Canonical file names
and
How can one get an absolute or normalized file path in .NET?
.
All answers on both are wrong.
On Windows C:\A.TXT is the same file as c:\a.txt. AFAIK on UNIX/LINUX it's not, and on MacOS it depends on the user. (The code might be used on .Net MAUI on different OSs.) And it might depend on the file system even within one OS. Non of the answers there address that. But over a decade has passed since those questions were asked, and that's a lot in computer-years.
So, does .Net (perhaps .Net 7.0) have something for this?
More info: I don't want to touch the file system such as by opening both files and checking whether both point to the same one. I just want to do a text comparison. And I don't care about "different" files pointing to the same file such as by symbolic links etc. so it's fine if the files are considered different by whatever the answer is, even though they point to the same actual file.
EDIT
Some of the paths will be stored, and I need to know that they point to the same file if the user changed from uppercase to lowercase (on Windows). I also need a durable way to check file systems and OSs. Something that will be updated if filesystems will change etc. Like for example NTFS case-sensitive NTFS folders which most commentators here seem not to know about. There might be other pitfalls as well.
Either a Canonical(path) or a AreSame(path1, path2) would be welcome.
Filenames are just text. Both Windows and MacOS are by default, case-insensitive but case-preserving file systems. MacOS allows one to format a drive with a case-sensitive filesystem; I can't remember whether or not NTFS has such an option.
In either event, to compare to strings in a case-insenstive manner, use string.Equals():
string fn1 = GetFirstFileName();
string fn2 = GetSecondFileName();
bool areSame = fn1.Equals(fn2, StringComparision.OrdinalIgnoreCase );
To put them in a collection, list a Dictionary<TKey,TValue> or a set, use the appropriate constructor and pass it the StringComparer.OrdinalIgnoreCase as its key comparer.
Related
Does anyone happen to know if there is a complete list of Windows 10 localized folders (like this one just for all folders)?
I am trying to create a custom explorer where the folder should be displayed in the user's native language.
I have already tried to get a complete list of localized folders using the ShellSpecialFolderConstants enumeration or the Environment.SpecialFolder enumeration.
but there are still a few missing (C:/Users for example).
SHGetFileInfo is unfortunately also out of the question, because otherwise the performance would suffer massively.
I have thought of simply designing a long selection statement for it in which all localized folders are stored.
And if there was already a table of equivalents, it would of course be very handy to know.
Thanks in advance!
Microsoft does not provide a list because you are not supposed to know, you are supposed to use the special/known folder API. This is to prevent people from hard-coding c:\Program files etc. In older versions of Windows the folders on disk were often localized. In Vista this changed and the real names are English now.
If you are building a custom Explorer then you should be dealing with IShellFolder/IShellItem and they can provide the display name.
If you are working at a lower level and ignoring the shell namespace you can call SHGetLocalizedName. This name can be applied to any folder, not just special folders.
If you for some reason only care about the special folders and not paths in general, IKnownFolder::GetFolderDefinition claims to return a structure that includes the localized name.
I'm trying to programmaticaly locate a file on a network server where there are multiple directory levels, one of which has two spaces at the end of the name. EG:
\\MyServer\C$\TopLevel\Account \someone#somewhere.com\AFileName.pdf
Note that the Account level represents many folders using account codes for the folder name, and most of them have the two spaces at the end.
I can't amend the directory structure as it's used by other processes outside my control. I will know in advance the path down to and including the Account level, so I need to search the Email level subfolders for a file containing an order number in the file name. I also know the order number.
The problem is that FileInfo, Directory and DirectoryInfo all balk at the two spaces. Whilst Windows has allowed the folders to be created, and an automated third party process can create folders and files below the Account level, nothing I have tried so far has worked because the spaces get stripped out by the c# file and directory classes.
I can't use:
Directory.GetFiles(PathToTopLevel, "*.pdf")
because there are over 1000 Account level folders, each of which might have 5 or 6 Email level folders with possibly many hundreds of pdf files in each one. I did try it but had to kill it when it was still thinking about it after 5 minutes.
Directory.GetFiles(PathToAccountLevel, "*.pdf")
This returns the 'can't find part of the path' error.
I also tried:
filePath = Path.Combine(PathToAccountLevel, $"{order_no}.pdf");
var fi = new FileInfo(filePath);
which also returns 'can't find part of the path'.
I've done lots of trawling through SO and other forums with no success as they are mainly concerned either with file names or single spaces, but I did find a useful method here which I have tried:
filePath = AddQuotesIfRequired(PathToAccountLevel);
Directory.GetFiles(filePath, "*.pdf")
Which does indeed add the quotes, but it makes no difference to the outcome.
Also tried (with and without adding quotes):
Directory.GetFiles(#filePath, "*.pdf")
I've tried as many combinations as I can think of using quotes and '#' etc. but absolutely nothing has worked so far. I've even hard coded the full path down to the Email level and still no joy.
I'm using C# with VS2019 on Windows 10. The server is running Windows Server 2012 R2 DataCenter. I've run the test executable locally on the server and the results are the same.
The really galling aspect of this is that a simple test program written in delphi and using FindFirst/Next works perfectly!
I should also mention that permissions are not an issue, and the files exist.
Any help at all would be great, thanks.
Re the possible duplicate/proposed answer from qaabaam:
Answer 1 for that question is not valid code - the line
DirectoryInfo wantedDir = tempWithoutMac.GetDirectories.Where(c => c.Contains(MacID)).First();
throws an error for GetDirectories being a method and not valid in the context. If you change it to GetDirectories() it then complains that DirectoryInfo does not have a definition for Contains.
Answer 2 using alt 0160 still throws the can't find part of the path error.
Answer 3 is basically an opinion and doesn't help at all.
If you can figure out how to get Answer 1 to work I'll definitely give it a try!
Leading or trailing spaces in directory names is one of those grey areas in Windows: you can do it, but you probably shouldn't. The reason for this is simple - the vast majority of the time, a leading or trailing space is a typo, and the Windows APIs around paths have been designed to take this into account, to the point where (as you've discovered) they will outright ignore leading or trailing spaces. (For example, in Windows 10, it's literally impossible to delete a directory with trailing spaces via Windows Explorer!) Since C# is built on top of these APIs, it inherits their "quirks".
Fortunately there is a way to tell Windows "don't try to fix this path, it is correct, I know what I'm doing" via the use of DOS device paths. In brief:
For local paths, prefix them with the literal \\?\. In other words, C:\foo\bar would become \\?\C:\foo\bar.
For UNC paths, replace the leading \\ with \\?\UNC\. In other words, \\server\foo\bar would become \\?\UNC\server\foo\bar.
In your case, then, all you need to do is change \\MyServer\C$\TopLevel\Account \ to \\?\UNC\MyServer\C$\TopLevel\Account \ and everything should work as you expect.
As an aside, what you are doing is a document search/query operation, which will be incredibly slow as it's performing I/O, and over the network at that. You'd be far better served in writing a tool to migrate these documents into a proper database, then searching that database for the documents.
I am writing a program that copies all files and directories from a source location into a target location. Both locations are provided by the user.
I was just thinking about checking whether the location paths have a backslash "\" at the end, when I decided to run some tests and from what I see it doesn't matter if you tell c#
File.Copy("C:\\test", "D:\\test")`
or
File.Copy("C:\\test\\", "D:\\test\\")`
Am I wrong? Does a backslash at the end of a file path matter?
Update:
On Windows, you apparently cannot name a file and folder the same, so it won't matter there. You should still use a (back)slash for good style and cross-platform compatability. Some software or libraries might even depend on it.
Original Answer:
A folder is generally ending in \ (or /), because there might be a file with no extension, named test. For example, these two could coexist (Linux [notice the different capitalization]):
Folder: /Users/nikxda/Docouments/Test/
File: /Users/nikxda/Docouments/test
If you work case-sensitive, then you could in fact ignore it on Windows. I'd still recommend using a trailing slash, just for
Clarification (specify it is a folder)
Good style (following conventions)
Compatibility (other software, libraries, etc. might depend on it)
Cross platform (OSX, ...)
So yes, you should always use a (back)slash at the end of your path if referring to folders.
It should make no difference, because - on windows - you cannot have a folder "test" and file named "test" without extension in the same parent directory at the same time.
However, it will be much more cleaner to have a trailing backslash within your code, because this leaves no room for speculation. And it will be better for cross-plattform purposes as well.
Thinking of URIs for example, it can definitely make a difference between a trailing slash or no trailing slash, by the way.
TLDR: Use a trailing (back)slash, please :-)
I'm trying to find the most reliable way of finding new and modified files in a directory using C# and .NET. I'm not looking for a real time solution, I want to check for changes at given times. It could be every 5 minutes or every hour etc.
We have CreationTime and LastWriteTime on the FileInfo object, and this seems to be enough to get new and modified files. But if a file is renamed none of the available dates are changed and the file will be missed if we just look at CreationTime and LastWriteTime.
At the moment i'm maintaining af "snapshot" of the files in the directory including the time of the last check for changes. This enables me to compare all the files in the directory with files in the snapshot, if the snapshot is missing a file it is either new or renamed.
Is this the only way? Rr am I missing something. I'm not going to use FileSystemWatcher as it seems pretty "buggy" and is required to run all the time.
Any suggestions are very welcome.
Merry Christmas!
Use the FileSystemWatcher class, it's the good way. Maybe you could be more specific with the
as it seems pretty "buggy"
EDIT: FileSystemWatcher does support renaming events.
The Microsoft Sync Framework has components for synchronising files.
The framework covers all data types and data storage and the file system component should be more reliable than the FileSystemWatcher. As it says in the MSDN:
It can be used to synchronize files and folders in NTFS, FAT, or SMB file systems. The directories to synchronize can be local or remote; they do not have to be of the same file system. An application can use static filters to exclude or include files either by listing them explicitly or by using wildcard characters (such as *.txt). Or the application can set filters that exclude whole subfolders. An application can also register to receive notification of file synchronization progress
I know you really only want to know when files have changed, but given that you've already dismissed the FileSystemWatcher route it might be the only reliable route (other than doing what you are in maintaining a snapshot yourself).
Your problem looks very much like a Database with no primary key.
If you asssign, say, a GUID to each file in that folder and check for that GUID instead of the filename, your application will be much more reliable.
So that's the theory, in practice, we're talking metadata. Depending on your system, and the files contained in that folder, you could use Alternate Data Streams.
Here is a SO question about it.
It boils down to having information on a file that is not stored within the file, it is merely linked to it.
You can then look it up in a DOS box:
notepade.exe myfile.txt:MYGUID
It requires the system to use NTFS.
HTH.
A very primitive approach would use a command of "dir" and comparing outputs...
Here is some info on params:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/dir.mspx?mfr=true
Along with you snapshot of dates, you can compare the outputs of dir... its very fast and low on resources consuming...
File system watcher class in .net provides two methods:-
OnChanged
OnRenamed
You can set the EnableRaisingEvents to true and that is!!!! Every thing is simple with Dot Net chill!!!
I'm building an application (a side project which is likely to enlist the help of the stackoverflow community on more than one occasion) which will need to open a variety of file types (i.e. open Word documents in Word, not natively in my application).
I've been playing with some code for looking up the default application for the file type in the registry and passing this to Process.Start(). There seem to be two issues with this approach:
1) The application name is quoted in some instances, and not in others.
2) Process.Start() requires that the application path and it's arguments are passed separately (i.e. Process.Start("notepad.exe", #"C:\myfile.txt"); rather than Process.Start(#"notepad.exe C:\myfile.txt");).
This means when I retrieve the path from the registry, I have to split it (after determining if I need to split on quotes or spaces) to determine what part is the application path and what parts are arguments, then pass those separately to Process.Start().
The alternative seems to be to just pass the filename, as in Process.Start(#"C:\myfile.txt"), but I think this only works if the application is in the Path environment variable.
Which way is better? In the case of the registry, is there a common solution for how to do the argument parsing?
Thanks for any and all help!
Update:
I guess the short answer is 'No.'
It seems like I was really going the overkill route, and that passing just the filename will work whenever there's an associated value in the registry. I.e. anything I find in the registry myself, Process.Start() already knows how to do.
I did discover that when I try this with a "new" filetype, I get a Win32Exception stating "No application is associated with the specified file for this operation." Fredrik Mörk mentions in a comment that this doesn't occur for him in Vista. What's the proper way to handle this?
If the extension is registered to be opened with a certain application, it doesn't need to be in the PATH in order to run.
The application does not need to be in the PATH if you only specify the filename. The following code worked fine for me:
System.Diagnostics.Process.Start(#"C:\Users\Dan\Desktop\minors.pdf");
You typically do not need to lookup the program for registered types, and the program does not typically need to be in the PATH environment variable. Usually the command in the registry contains the full path. This is how the command for .kml files (Google Earth) looks (in my computer):
C:\Program Files\Google\Google Earth\googleearth.exe "%1"
Given that, you can safely just use Process.Start together with the document file names. Should it be that the file type is not registered you will invoke the default Windows behaviour for this (asking you which program to use, and so on).