Verifying path equality with .Net - c#

What is the best way to compare two paths in .Net to figure out if they point to the same file or directory?
How would one verify that these are the same:
c:\Some Dir\SOME FILE.XXX
C:\\\SOME DIR\some file.xxx
Even better: is there a way to verify that these paths point to the same file on some network drive:
h:\Some File.xxx
\\Some Host\Some Share\Some File.xxx
UPDATE:
Kent Boogaart has answered my first question correctly; but I`m still curious to see if there is a solution to my second question about comparing paths of files and directories on a network drive.
UPDATE 2 (combined answers for my two questions):
Question 1: local and/or network files and directories
c:\Some Dir\SOME FILE.XXX
C:\\\SOME DIR\some file.xxx
Answer: use System.IO.Path.GetFullPath as exemplified with:
var path1 = Path.GetFullPath(#"c:\Some Dir\SOME FILE.XXX");
var path2 = Path.GetFullPath(#"C:\\\SOME DIR\subdir\..\some file.xxx");
// outputs true
Console.WriteLine("{0} == {1} ? {2}", path1, path2, string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase));
Question 2: local and/or network files and directories
Answer: Use the GetPath method as posted on
http://briancaos.wordpress.com/2009/03/05/get-local-path-from-unc-path/

var path1 = Path.GetFullPath(#"c:\Some Dir\SOME FILE.XXX");
var path2 = Path.GetFullPath(#"C:\\\SOME DIR\subdir\..\some file.xxx");
// outputs true
Console.WriteLine("{0} == {1} ? {2}", path1, path2, string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase));
Ignoring case is only a good idea on Windows. You can use FileInfo.FullName in a similar fashion, but Path will work with both files and directories.
Not sure about your second example.

Although it's an old thread posting as i found one.
Using Path.GetFullpath I could solve my Issue
eg.
Path.GetFullPath(path1).Equals(Path.GetFullPath(path2))

Nice syntax with the use of extension methods
You can have a nice syntax like this:
string path1 = #"c:\Some Dir\SOME FILE.XXX";
string path2 = #"C:\\\SOME DIR\subdir\..\some file.xxx";
bool equals = path1.PathEquals(path2); // true
With the implementation of an extension method:
public static class StringExtensions {
public static bool PathEquals(this string path1, string path2) {
return Path.GetFullPath(path1)
.Equals(Path.GetFullPath(path2), StringComparison.InvariantCultureIgnoreCase);
}
}
Thanks to Kent Boogaart for the nice example paths.

I had this problem too, but I tried a different approach, using the Uri class. I found it to be very promising so far :)
var sourceUri = new Uri(sourcePath);
var targetUri = new Uri(targetPath);
if (string.Compare(sourceUri.AbsoluteUri, targetUri.AbsoluteUri, StringComparison.InvariantCultureIgnoreCase) != 0
|| string.Compare(sourceUri.Host, targetUri.Host, StringComparison.InvariantCultureIgnoreCase) != 0)
{
// this block evaluates if the source path and target path are NOT equal
}

As reported by others, Path.GetFullPath or FileInfo.FullName provide normalized versions of local files. Normalizing a UNC path for comparison is quite a bit more involved but, thankfully, Brian Pedersen has posted a handy MRU (Method Ready to Use) to do exactly what you need on his blog, aptly titled Get local path from UNC path. Once you add this to your code, you then have a static GetPath method that takes a UNC path as its sole argument and normalizes it to a local path. I gave it a quick try and it works as advertised.

A very simple approach I use to determine if two path strings point to the same location is to create a temp file in Path1 and see if it shows up in Path2. It is limited to locations you have write-access to, but if you can live with that, it’s easy! Here’s my VB.NET code (which can easily be converted to C#) …
Public Function SamePath(Path1 As String, Path2 As String) As String
' Returns: "T" if Path1 and Path2 are the same,
' "F" if they are not, or
' Error Message Text
Try
Path1 = Path.Combine(Path1, Now.Ticks.ToString & ".~")
Path2 = Path.Combine(Path2, Path.GetFileName(Path1))
File.Create(Path1).Close
If File.Exists(Path2) Then
Path2 = "T"
Else
Path2 = "F"
End If
File.Delete(Path1)
Catch ex As Exception
Path2 = ex.Message
End Try
Return Path2
End Function
I return the result as a string so I can provide an error message if the user enters some garbage. I’m also using Now.Ticks as a “guaranteed” unique file name but there are other ways, like Guid.NewGuid.

you can use FileInfo!
string Path_test_1="c:\\main.h";
string Path_test_2="c:\\\\main.h";
var path1=new FileInfo(Path_test_1)
var path2=new FileInfo(Path_test_2)
if(path1.FullName==path2.FullName){
//two path equals
}

Only one way to ensure that two paths references the same file is to open and compare files. At least you can instantiate FileInfo object and compare properties like:
CreationTime, FullName, Size, etc. In some approximation it gives you a guarantee that two paths references the same file.

Why not create a hash of each file and compare them? If they are the same, you can be reasonably sure they are the same file.

Related

Combining two relative paths with C#

There are many dupes for "appending a relative path to an absolute path", but I need to add relative to relative.
e.g.:
Path1 = "Parent/Child/a.txt"
Path2 = "../Sibling/file.cs"
Result = "Parent/Sibling/file.cs"
Tried:
Directory.GetParent() - works, but I can't find a way to return the result (it can only return absolute paths)
Path.Combine() - only works for simple cases and absolute paths. Fails (badly!) on the use of ".." with relative paths
...it seems absurd to write a string-tokenizing Path class to solve this, but I've been digging through the MSDN docs and can't seem to find a working Path/Directory class that correctly works with relative paths.
To make matters worse ... I'm trying to make this work all the way back to .NET 2 (thanks to Mono compatibilty)
I know the following code is ugly but will work (sorry I don't confirm on mono yet):
var Result =
Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Path1), Path2))
.Substring(Directory.GetCurrentDirectory().Length + 1); // +1 to remove leading path separator
I'm not sure why you say Path.Combine fails badly, since you don't give any examples of what you've tried. But Path.Combine does exactly what you're looking for. You do have to give it a little help in this case; your first path is a filename, and you need the directory.
var filePath1 = "Parent/Child/a.txt";
var filePath2 = "../Sibling/file.cs";
var baseDirectory = Path.GetDirectoryName(filePath1);
var result = Path.Combine(baseDirectory, filePath2);
This returns "Parent\Child\../Sibling/file.cs" rather than the "Parent/Sibling/file.cs" you're looking for, but the two paths are exactly equivalent.
I tried this on Windows, it should work on Mac OS (Mono/Xamarin) or Linux (Mono) but you might need a different value for the dummyDriveLetter variable.
static void Main(string[] args)
{
string Path1 = "Parent/Child/a.txt";
string Path2 = "../Sibling/file.cs";
string dummyDriveLetter = "C:/"; // must be an absolute path, so let's add a dummy prefix, works on Windows
string absolutePath1 = dummyDriveLetter + Path1;
var path1Uri = new Uri(absolutePath1, UriKind.Absolute);
var path2Uri = new Uri(Path2, UriKind.Relative);
var diff = new Uri(path1Uri, path2Uri);
string Result = diff.OriginalString.Replace(dummyDriveLetter, "");
Console.WriteLine(Result);
}

Path.Combine() returns unexpected results

I'm trying to create a path using Path.Combine() but I am getting unexpected results.
using System;
using System.IO;
namespace PathCombine_Delete
{
class Program
{
static void Main(string[] args)
{
string destination = "D:\\Directory";
string destination02 = "it";
string path = "L:\\MyFile.pdf";
string sourcefolder = "L:\\";//In other instances, it could be L:\\SomeFolder\AndMayBeAnotherFolder
string replacedDetails = path.Replace(sourcefolder + "\\", "");
string result = Path.Combine(destination, destination02, replacedDetails);
Console.WriteLine(result);
Console.ReadKey();//Keep it on screen
}
}
}
I would expect the result D:\\Directory\it\MyFile.pdf but instead, I get L:\MyFile.pdf
I can't see why? I admit it's late in the evening here, but still, I've used Path.Combine many times, and since .NET 4.0 it allows the string param to be passed. However, it appears to be ignoring the first 2 and only reading the last.
Here is the error
string replacedDetails = path.Replace(sourcefolder + "\\" , "");
You are adding another backslash and nothing is found to be replaced.
Removing the added backslash gives the correct string to search for and replace
string replacedDetails = path.Replace(sourcefolder , "");
however you could avoid all that replace stuff and intermediate variables just with
string result = Path.Combine(destination, destination02, Path.GetFileName(path));
I would recommend using:
string replacedDetails = Path.GetFileName(path);
That will handle removing the source folder from the path without using string replacement, which isn't necessarily reliable if you're getting the path from elsewhere (eventually).
Have you read the documentation? Have you verified what you're passing to Path.Combine()? The documentation says, and I quote:
path1 should be an absolute path (for example, "d:\archives" or "\archives\public").
If path2 or path3 is also an absolute path, the combine operation discards all
previously combined paths and resets to that absolute path.
That should hint at the problem.

How to match two paths pointing to the same file?

I have two lists containing paths to a directory of music files and I want to determine which of these files are stored on both lists and which are only stored on one. The problem lies in that the format of the paths differ between the two lists.
Format example:
List1: file://localhost//FILE/Musik/30%20Seconds%20To%20Mars.mp3
List2: \\FILE\Musik\30 Seconds To Mars.mp3
How do I go about comparing these two file paths and matching them to the same source?
The answer depends on your notion of "same file". If you merely want to check if the file is equal, but not the very same file, you could simply generate a hash over the file's content and compare that. If the hashes are equal (please use a strong hash, like SHA-256), you can be confident that the files are also. Likewise you could of course also compare the files byte by byte.
If you really want to figure that the two files are actually the same file, i.e. just addressed via different means (like file-URL or UNC path), you have a little more work to do.
First you need to find out the true file system path for each of the addresses. For example, you need to find the file system path behind the UNC path and/or file-URL (which typically is the URL itself). In the case of UNC paths, that are shares on a remote computer, you might even be able to do so.
Also, even if you have the local path figured out somehow, you also need to deal with different redirection mechanisms for local paths (on Windows junctions/reparse points/links; on UNIX symbolic or hard links). For example, you could have a share using file system link as source, while the file URL uses the true source path. So to the casual observer they still look like different files.
Having all that said, the "algorithm" would be something like this:
Figure out the source path for the URLs, UNC paths/shares, etc. you have
Figure out the local source path from those paths (considering links/junctions, subst.exe, etc.)
Normalize those paths, if necessary (i.e. a/b/../c is actually a/c)
Compare the resulting paths.
I think the best way to do it is by temporarily converting one of the paths to the other one's format. I would suggest you change the first to match the second.
string List1 = "file://localhost//FILE/Musik/30%20Seconds%20To%20Mars.mp3"
string List2 = "\\FILE\Musik\30 Seconds To Mars.mp3"
I would recommend you use Replace()-method.
Get rid of "file://localhost":
var tempStr = List1.Replace("file://localhost", "");
Change all '%20' into spaces:
tempStr = List1.Replace("%20", " ");
Change all '/' into '\':
tempStr = List1.Replace("/", "\");
Voilà! To strings in matching format!
Use python: you can easily compare the two files like this
>>> import filecmp
>>> filecmp.cmp('file1.txt', 'file1.txt')
True
>>> filecmp.cmp('file1.txt', 'file2.txt')
False
to open the files with the file:// syntax use URLLIB
>>> import urllib
>>> file1 = urllib.urlopen('file://localhost/tmp/test')
for the normal files path use the standard file open.
>>> file2 = open('/pathtofile','r')
I agree completely with Christian, you should re-think structure of the lists, but the below should get you going.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication5
{
class Program
{
public static List<string> SanitiseList(List<string> list)
{
List<string> sanitisedList = new List<string>();
foreach (string filename in list)
{
String sanitisedFilename = String.Empty;
if (!String.IsNullOrEmpty(filename))
{
sanitisedFilename = filename;
// get rid of the encoding
sanitisedFilename = Uri.UnescapeDataString(sanitisedFilename);
// first of all change all back-slahses to forward slashes
sanitisedFilename = sanitisedFilename.Replace(#"\", #"/");
// if we have two back-slashes at the beginning assume its localhsot
if (sanitisedFilename.Substring(0, 2) == "//")
{
// remove these first double slashes and stick in localhost
sanitisedFilename = sanitisedFilename.TrimStart('/');
sanitisedFilename = sanitisedFilename = "//localhost" + "/" + sanitisedFilename;
}
// remove file
sanitisedFilename = sanitisedFilename.Replace(#"file://", "//");
// remove double back-slashes
sanitisedFilename = sanitisedFilename.Replace("\\", #"\");
// remove double forward-slashes (but not the first two)
sanitisedFilename = sanitisedFilename.Substring(0,2) + sanitisedFilename.Substring(2, sanitisedFilename.Length - 2).Replace("//", #"/");
}
if (!String.IsNullOrEmpty(sanitisedFilename))
{
sanitisedList.Add(sanitisedFilename);
}
}
return sanitisedList;
}
static void Main(string[] args)
{
List<string> listA = new List<string>();
List<string> listB = new List<string>();
listA.Add("file://localhost//FILE/Musik/BritneySpears.mp3");
listA.Add("file://localhost//FILE/Musik/30%20Seconds%20To%20Mars.mp3");
listB.Add("file://localhost//FILE/Musik/120%20Seconds%20To%20Mars.mp3");
listB.Add(#"\\FILE\Musik\30 Seconds To Mars.mp3");
listB.Add(#"\\FILE\Musik\5 Seconds To Mars.mp3");
listA = SanitiseList(listA);
listB = SanitiseList(listB);
List<string> missingFromA = listB.Except(listA).ToList();
List<string> missingFromB = listA.Except(listB).ToList();
}
}
}

How can I get a full path of a given path (can be a directory or file, or even full path) using C#?

The closest I get is using new FileInfo(path).FullPath, but as far as I know FileInfo is for files only, not directory.
See also my comments to Jon Skeet's answer here for context.
The Path class also gives you a lot of nice methods and properties, e.g. GetFullPath(). See MSDN for all details.
Path.GetFullPath()
I think it's-
DirectoryInfo.FullName
Try this:
String strYourFullPath = "";
IO.Path.GetDirectoryName(strYourFullPath)
Use the DirectoryInfo class which extends FileSystemInfo and will give the correct result for either files or directories.
string path = #"c:\somefileOrDirectory";
var directoryInfo = new DirectoryInfo(path);
var fullPath = directoryInfo.FullName;
Use the DirectoryInfo class for paths to directories. Works much in the same matter as FileInfo.
Note that the property for the path is called FullName.
DirectoryInfo di = new DirectoryInfo(#"C:\Foo\Bar\");
string path = di.FullName;
If you want to determine whether a path is a file or a directory, you can use static methods from the Path class:
string path1 = #"C:\Foo\Bar.docx";
string path2 = #"C:\Foo\";
bool output1 = Path.HasExtension(path1); //Returns true
bool output2 = Path.HasExtension(path2); //Returns false
However, paths could also contain something that might resemble an extension, so you might want to use it in conjunction with some other checks, e.g. bool isFile = File.Exists(path);
According to msdn, FileSystemInfo.FullName gets the full path of the directory or file, and can be applied to a FileInfo.
FileInfo fi1 = new FileInfo(#"C:\someFile.txt");
Debug.WriteLine(fi1.FullName); // Will produce C:\someFile.txt
FileInfo fi2 = new FileInfo(#"C:\SomeDirectory\");
Debug.WriteLine(fi2.FullName); // Will produce C:\SomeDirectory\
You can use file.getdirectory to get this done.

The SaveAs method is configured to require a rooted path, and the path 'fp' is not rooted

I am doing Image uploader in Asp.net and I am giving following code under my controls:
string st;
st = tt.PostedFile.FileName;
Int32 a;
a = st.LastIndexOf("\\");
string fn;
fn = st.Substring(a + 1);
string fp;
fp = Server.MapPath(" ");
fp = fp + "\\";
fp = fp + fn;
tt.PostedFile.SaveAs("fp");
But during uploading or saving image the error message comes that The SaveAs method is configured to require a rooted path, and the path 'fp' is not rooted.
So Please help me what is the problem
I suspect the problem is that you're using the string "fp" instead of the variable fp. Here's the fixed code, also made (IMO) more readable:
string filename = tt.PostedFile.FileName;
int lastSlash = filename.LastIndexOf("\\");
string trailingPath = filename.Substring(lastSlash + 1);
string fullPath = Server.MapPath(" ") + "\\" + trailingPath;
tt.PostedFile.SaveAs(fullPath);
You should also consider changing the penultimate line to:
string fullPath = Path.Combine(Server.MapPath(" "), trailingPath);
You might also want to consider what would happen if the posted file used / instead of \ in the filename... such as if it's being posted from Linux. In fact, you could change the whole of the first three lines to:
string trailingPath = Path.GetFileName(tt.PostedFile.FileName));
Combining these, we'd get:
string trailingPath = Path.GetFileName(tt.PostedFile.FileName));
string fullPath = Path.Combine(Server.MapPath(" "), trailingPath);
tt.PostedFile.SaveAs(fullPath);
Much cleaner, IMO :)
Use Server.MapPath():
fileUploader.SaveAs(Server.MapPath("~/Images/")+"file.jpg");
If you want to save the uploaded file to the value of fp, just pass it in, don't put it in quotes:
tt.PostedFile.SaveAs(fp);
When reading the title of the question, I was thinking that it looked like you had put quotation marks around the variable name. Not really believing that it was so, I opened the question to read it, but it really was so...
We cannot use the "SaveAs" method to write directly to an FTP server.
Only local paths and UNC paths are supported for the above method.
To save it to FTP, please use the FtpWebRequest class.
You will get the full details to this in the same type of question answer in social.msdn.
Please go through the link.. and you will be able to solve the issue..
enter link description here
--thanks for the answer by Jesse HouwingXPirit (MCC, Partner, MVP)
I encountered the same problem. The problem is that you did not specify the path of the server that you want the file to be saved. And here is a probably simpler answer:
string fileName = tt.PostedFile.FileName;
string savePath = Server.MapPath("Path/Of/The/Folder/Comes/Here/") + fileName);
tt.PostedFile.SaveAs(savePath);

Categories