简体   繁体   中英

C# combining absolute and relative paths, Path.GetFullPath() inconsistent

I notice that when I use Path.GetFullPath(Path.Combine(@"c:\\folder\\","\\subfolder\\file.txt")) it returns a path c:\\subfolder\\file.txt, instead of the expected combined path c:\\folder\\subfolder\\file.txt. It seems like having a '\\' on the second combined input isn't handled by these methods. Has anyone come across this and found a better solution for merging paths?

var test1 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @".\test1.txt"));
var test2 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @"\test2.txt"));//
var test3 = Path.GetFullPath(Path.Combine(@"C: \users\dev\desktop\testfiles\", @"test3.txt"));
var test4 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\",@".\XXX\test4.txt"));
var test5 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @"\XXX\test5.txt"));//
var test6 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @"XXX\test6.txt"));
var test7 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @"\somefile\that\doesnt\exist.txt"));//

Results in

test1 is "C:\users\dev\desktop\testfiles\test1.txt"
test2 is wrong "\test2.txt"
test3 is "C: \users\dev\desktop\testfiles\test3.txt"
test4 is "C:\users\dev\desktop\testfiles\XXX\test4.txt"
test5 is wrong "c:\XXX\test5.txt"
test6 is "C:\users\dev\desktop\testfiles\XXX\test6.txt"
test7 is wrong "c:\somefile\that\doesnt\exist.txt"

Basically what I'm doing is combining a source path with individual file paths from a text file so that I can copy these files to a destination, and I want to maintain subfolder hierarchy.

eg common scenario of the files list being the same, but wanting to reference a specific source folder version.

source path + individual file path
\\myserver\project\1.x + \dll\important.dll

which should be copied to a destination in the same manner

c:\myproject\ + \dll\important.dll

So there would be variables; source, destination, files[array or list]

My current way forward is to use String.TRIM() method to remove special chars . \\ / before combining paths with Path.Combine, Path.GetFullPath. But I thought maybe someone has realized a better way for a similar scenario.

It's also a pain creating the subfolders in the destination, basically im using String.LastIndexOf(@'\\') to separate the filename from the foldername. Which is also seems a little sketchy, but a better way is beyond my experience.

Similar question: Path.Combine absolute with relative path strings


Thanks to the feedback I now know that \\file.txt is considered a fully qualified (absolute) path, which is why the combine method works that way.

A file name is relative to the current directory if it does not begin with one of the following:

A UNC name of any format, which always start with two backslash characters ("\\"). For more information, see the next section.
A disk designator with a backslash, for example "C:\" or "d:\".
A single backslash, for example, "\directory" or "\file.txt". This is also referred to as an absolute path.

https://msdn.microsoft.com/en-nz/library/windows/desktop/aa365247(v=vs.85).aspx#fully_qualified_vs._relative_paths

Based on the MSDN Documentation , this is expected behavior.

If path2 does not include a root (for example, if path2 does not start with a separator character or a drive specification), the result is a concatenation of the two paths, with an intervening separator character. If path2 includes a root, path2 is returned.

Seperator characters are discussed in this MSDN article .

So let's take a look at your test cases:

var test1 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @".\test1.txt"));
var test2 = Path.GetFullPath(Path.Combine(@"C:\users\dev\desktop\testfiles\", @"\test2.txt"));

Test1 is behaving correctly because it is taking the . as part of a valid file path and using it in combination with the first parameter to generate the combined path.

Test2 is behaving as expected because the \\ is a separator character, so as described it is returned without the first parameter.

There is no functions that you like to use in .Net framework - trimming path or ensuring that relative path starts with ".\\" your real options (probably library that does it exists somewhere).


Why code does not behave the way you like (not "wrong", just not meeting your unexpected expectations).

Combine need to decide which part of left path will stay and which will be replaced. It uses regular file system conventions to decide that (".", "..", "\\" have special meaning for file paths)

Meaning of path starting with:

  • @"\\" or "/" - starting from the root of current device - this covers most cases that you don't like
  • @".\\" - means from current directory (hence after combining and normalizing it will behave exactly as you want)
  • `@"..\\" (possible multiple "go up") - go up one folder - this is another case you'll likely disagree with.
  • any character that is not '/' or @'\\' - file name or path from current folder

Also note that path ending on anything but "/" or @"\\" is treated by Path.Combine as file path and when combined with other path it will lose last segment.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM