Windows 与 .NET 中传输数据的 unix 路径对比

Windows vs. unix path on transfer data in .NET

这里是问题所在:在 Windows 上获取一个相对文件夹结构,将其打包到 JSON 文件中,然后将其复制到 linux 发行版,比如 Ubuntu 20.04,使用.NET 5.

这是我的(非常简化的)方法:

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));
}

现在输出类似

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

将 JSON 写入文件并将其传输到 linux,并按照以下行执行某些操作:

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
}

问题是,这里的路径 returns /home/user/tempFolder2/\InnerFolder\Subfolder/TestFile,因为反斜杠是 linux 中的有效路径字符,它不会被 Path.Combine 解析.

我在这里想到了两个选择:

  1. 以某种方式强制 windows 使用 AltPathSeparatorChar ('/')
  2. 走穷人的路,如果系统不是 WinNT32,只需将相对路径中所有出现的“\\”和“\”替换为 /。

现在我非常不喜欢第二个选项,以防路径是使用 linux 生成的并且反斜杠确实是反斜杠。我更希望所有输出文件都具有“路径”变量的标准格式,但我终生无法弄清楚如何强制路径命名空间对路径使用正斜杠而不是 Windows 中的反斜杠.

有没有办法使用路径 api 实现此目的,或者我是否必须采用 if(OS)... 方式?

编辑: 在与@Olivier Rogier 进行了简短的辩论之后,我决定更新这个问题,以防有人知道集成解决方案。

归结为:

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

returns Windows 中的“sf1\sf2”和 Linux 中的“sf1/sf2”。因此,如果程序员端没有额外的代码,代码行是不确定的,如果代码在两个不同的系统上是 运行。

运行 一个像这里的简单例子

//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);

在两种环境中不会产生相同的结果。

1: 作者: WIN, Reader: WIN
编写器输出:sf1\\sf2
reader 中的组合路径:文件夹\\sf1\\sf2
目录创建结果:

-文件夹
  -sf1
    -sf2
2:作者:LNX,Reader:WIN
编写器输出:sf1/sf2
reader 中的组合路径:文件夹\\sf1\\sf2
目录创建结果:
-文件夹
  -sf1
    -sf2
3:作者:WIN,Reader:LNX
编写器输出:sf1\\sf2
reader中的组合路径:folder/sf1\\sf2/
目录创建结果:
-文件夹
  -sf1\\sf2

另一个例子是使用Linux目录路径

-文件夹
  -sf1\stillavalidfolde\rname
作者:LNX,Reader:WIN
编写器输出:sf1\stillavalidfolde\rname
reader 中的组合路径:folder\\sf1\\stillavalidfolde\\rname
目录创建结果:
-文件夹
  -sf1
    -stillavalidfolde
      -rname

到目前为止,唯一可行的解​​决方案是

 //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);

这当然意味着更新所有序列化器、序列化代码等等,因此他们在路径被序列化的任何地方使用 split/join 方法,并且人类可读性较低。

人们会期望至少重载 Path 命名空间方法,其中可以指定目录分隔字符(强制 Path.GetFullPath 和 Path.Combine 方法始终使用相同的格式),一个 RelativePath class,这是在内部处理的,或者可能是我没有想到的另一种方式。

重申一下,有没有一种方法可以提取、存储和读取字符串中的相对路径名,而无需将字符串路径转换为在 Windows 和 Linux 上可以读写的相对路径名目录数组?

为了 OS 独立,我会使用 Path.DirectorySeparatorChar 属性:

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

使用 Path.Combine 您可以恢复 OS 依赖部分:

var restoredPath = Path.Combine(pathParts);