Combining two relative paths with C# - 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);
}

Related

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 get the second to last directory in a path string in C#

For example,
string path = #"C:\User\Desktop\Drop\images\";
I need to get only #"C:\User\Desktop\Drop\
Is there any easy way of doing this?
You can use the Path and Directory classes:
DirectoryInfo parentDir = Directory.GetParent(Path.GetDirectoryName(path));
string parent = parentDir.FullName;
Note that you would get a different result if the path doesn't end with the directory-separator char \. Then images would be understood as filename and not as directory.
You can also use a subsequent call of Path.GetDirectoryName
string parent = Path.GetDirectoryName(Path.GetDirectoryName(path));
This behaviour is documented here:
Because the returned path does not include the DirectorySeparatorChar
or AltDirectorySeparatorChar, passing the returned path back into the
GetDirectoryName method will result in the truncation of one folder
level per subsequent call on the result string. For example, passing
the path "C:\Directory\SubDirectory\test.txt" into the
GetDirectoryName method will return "C:\Directory\SubDirectory".
Passing that string, "C:\Directory\SubDirectory", into
GetDirectoryName will result in "C:\Directory".
This will return "C:\User\Desktop\Drop\" e.g. everything but the last subdir
string path = #"C:\User\Desktop\Drop\images";
string sub = path.Substring(0, path.LastIndexOf(#"\") + 1);
Another solution if you have a trailing slash:
string path = #"C:\User\Desktop\Drop\images\";
var splitedPath = path.Split('\\');
var output = String.Join(#"\", splitedPath.Take(splitedPath.Length - 2));
var parent = "";
If(path.EndsWith(System.IO.Path.DirectorySeparatorChar) || path.EndsWith(System.IO.Path.AltDirectorySeparatorChar))
{
parent = Path.GetDirectoryName(Path.GetDirectoryName(path));
parent = Directory.GetParent(Path.GetDirectoryName(path)).FullName;
}
else
parent = Path.GetDirectoryName(path);
As i commented GetDirectoryName is self collapsing it returns path without tralling slash - allowing to get next directory.Using Directory.GetParent for then clouse is also valid.
Short Answer :)
path = Directory.GetParent(Directory.GetParent(path)).ToString();
Example on the bottom of the page probably will help:
http://msdn.microsoft.com/en-us/library/system.io.path.getdirectoryname(v=vs.110).aspx
using System;
namespace Programs
{
public class Program
{
public static void Main(string[] args)
{
string inputText = #"C:\User\Desktop\Drop\images\";
Console.WriteLine(inputText.Substring(0, 21));
}
}
}
Output:
C:\User\Desktop\Drop\
There is probably some simple way to do this using the File or Path classes, but you could also solve it by doing something like this (Note: not tested):
string fullPath = "C:\User\Desktop\Drop\images\";
string[] allDirs = fullPath.split(System.IO.Path.PathSeparator);
string lastDir = allDirs[(allDirs.length - 1)];
string secondToLastDir= allDirs[(allDirs.length - 2)];
// etc...

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.

Verifying path equality with .Net

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.

Path functions for URL

I want to use functions of Path class (GetDirectoryName, GetFileName, Combine,etc.) with paths in URL format with slash (/).
Example of my path:
"xxx://server/folder1/folder2/file"
I tried to do the job with Path functions and in the end just replaced the separator.
I've found that the GetDirectoryName function does not correctly replace the slashes:
Path.GetDirectoryName(#"xxx://server/folder/file") -> #"xxx:\server\folder"
Like you see one slash is lost.
How can I cause the Path functions to use the 'alternative' separator?
Can I use another class with the same functionality?
I'm afraid GetDirectoryName, GetFileName, Combine,etc. use Path.DirectorySeparatorChar in the definition and you want Path.AltDirectorySeparatorChar.
And since Path is a sealed class, I think the only way to go about is string replacement.You can replace Path.DirectorySeparatorChar('\') with Path.AltDirectorySeparatorChar('/') and Path.VolumeSeparatorChar(':') with ":/"
For GetDirectoryName(), you can use
pageRoot = uri.Remove(uri.LastIndexOf('/') + 1);
Have you considered using a combination of System.Uri, System.UriBuilder, and (if necessary) custom System.UriParser subclass(es)?
If the URI is a local file URI of the form file://whatever then you can call string path = new Uri(whatever).LocalPath and call the Path methods on it. If you cannot guarantee the Uri is to a local path, you cannot guarantee components of the Uri correspond to machines, folders, files, extensions, use directories, separator characters, or anything else.
Long time after...I was looking for a solution and found this topic, so i decided to make my (very simple) code
string dirRootUpdate = string.Empty;
string fileNameupdate = string.Empty;
string pathToGetUpdate = string.Empty;
string[] _f = Properties.Settings.Default.AutoUpdateServerUrl.Split('/');
for (int i = 0; i < _f.Count() - 1; i++)
{
dirRootUpdate += _f[i];
if (i == 0) // is the first one
{
dirRootUpdate += "/";
}
else if (i != _f.Count() - 2) // not the last one ?
{
dirRootUpdate += "/";
}
}
fileNameupdate = _f[_f.Count() - 1];
the setting "Properties.Settings.Default.AutoUpdateServerUrl" contains the string to be verified
Works fine, may require some refination to look better.
Hope could help someone

Categories