Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

here is the problem: take a relative folder structure on Windows, package it in a JSON file, and replicate it on a linux distro, say Ubuntu 20.04, using .NET 5.

Here's my (very much simplified) approach:

using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
using Internal;

public class FileData
{
   public string Path { get; set; }
   public string FileName { get; set; }
}

public static void Main(string[] args)
{
    var rootPath = Path.GetFullPath(@"C:/tempFolder");

    var files = Directory.GetFiles(rootPath, "*.*", SearchOption.AllDirectories);

    List<FileData> fileData = new List<FileData>();

    foreach (var file in files)
    {
        fileData.Add(new FileData
        {
            FileName = Path.GetFileName(file),
            Path = Path.GetRelativePath(rootPath, Path.GetDirectoryName(file))
        });
    }
    
    Console.WriteLine(JsonConvert.SerializeObject(fileData));
}

Now this outputs something like

[
    {
        "Path":"InnerFolder\Subfolder",
        "FileName":"TestFile.txt"
    }
]

Writing the JSON to a file and transferring it to linux, and executing something along the lines of:

var rootPath = Path.GetFullPath("/home/user/tempFolder2/");

var fileData = JsonConvert.DeserializeObject<List<FileData>>(dataReadFromFile);

foreach(var file in fileData)
{
   var path = Path.Combine(rootPath, file.Path, file.FileName);
   //...proceed to create file and path
}

The problem is, the path here returns /home/user/tempFolder2/InnerFolderSubfolder/TestFile, as a backslash is a valid path character in linux and it does not get parsed by the Path.Combine.

I figured two options here:

  1. Somehow force windows to use the AltPathSeparatorChar ('/')
  2. Go the poor man's way and simply replace all "\" and '' occurences with / in relative paths, if the system is not WinNT32.

Now I severely dislike the second option, in case the paths are generated using linux and a backslash is indeed a backslash. I would much prefer all output files have a standard format for the "Path" variables, but I cannot for the life of me figure out, how to force the Path namespace to use forward slashes for paths instead of backslashes in Windows.

Is there a way achieving this using the Path api or will I have to go the if(OS)... way?

EDIT: After a short debate with @Olivier Rogier, I've decided to update the problem, in case someone knows of an integrated solution.

What it boils down to:

var relativePath = Path.GetRelativePath("./folder", "./folder/sf1/sf2");

returns "sf1sf2" in Windows and "sf1/sf2" in Linux. So, without additional code on the programmer's end, the line of code is non-deterministic, if the code is run on the two different systems.

Running a simple example like the one here

//WRITER
var relativePath = Path.GetRelativePath("./folder", "./folder/sf1/sf2");
File.WriteAllText("transferFile", relativePath);

//READER, on another OS
var relativePath = File.ReadAllText("transferFile");
var combinedPath = Path.Combine("./folder", relativePath);

does not produce the same result in both environments.

1: Writer: WIN, Reader: WIN
Writer output: sf1\sf2
Combined path in reader: folder\sf1\sf2
Dir create result:

-folder
  -sf1
    -sf2
2: Writer: LNX, Reader: WIN
Writer output: sf1/sf2
Combined path in reader: folder\sf1\sf2
Dir create result:
-folder
  -sf1
    -sf2
3: Writer: WIN, Reader: LNX
Writer output: sf1\sf2
Combined path in reader: folder/sf1\sf2/
Dir create result:
-folder
  -sf1\sf2

Another example is using the Linux directory path

-folder
  -sf1stillavalidfolde
name
Writer: LNX, Reader: WIN
Writer output: sf1stillavalidfolde
name
Combined path in reader: folder\sf1\stillavalidfolde\rname
Dir create result:
-folder
  -sf1
    -stillavalidfolde
      -rname

So far, the only viable solution is something along the lines of

 //WRITER
var relativePath = Path.GetRelativePath("./folder", "./folder/sf1/sf2");
File.WriteAllLines("transferFile", relativePath.Split(Path.DirectorySeparatorChar));

//READER, on another OS
var relativePaths = File.ReadAllLines("transferFile");
var combinedPath = Path.Combine("./folder", relativePaths);

This of course means updates to all serializers, serialization code an so on, so they use the split/join method anywhere the path is serialized, and less human readability.

One would expect at least an overload for the Path namespace methods, where one can either specify a directory separation character (forcing the Path.GetFullPath and Path.Combine methods to always use the same format), a RelativePath class, where this is handled internally, or perhaps another way I haven't thought of.

To reiterate, is there a way to extract, store, and read relative pathnames in strings than can be both read and written on Windows and Linux, without converting the string path to an array of directories?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
184 views
Welcome To Ask or Share your Answers For Others

1 Answer

To be OS independent, I would save the relative path as an array in your JSON by using the Path.DirectorySeparatorChar property:

// Example: notepad.exe relative to C:
var pathParts = Path.GetRelativePath(@"C:", Path.GetDirectoryName(@"C:WindowsSystem32
otepad.exe"))
    .Split(Path.DirectorySeparatorChar);

With Path.Combine you can restore your OS dependent part:

var restoredPath = Path.Combine(pathParts);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...