当前位置: 首页 > news >正文

.NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径

Windows 下的路径分隔符是 \ 而 Linux 和 Mac 下的路径分隔符是 \。正常如果你的数据不跨 Windows 和 Linux 平台流通的话,不怎么会遇到多种换行符并存的问题的。但如果真发生了流通,那么如何将它们格式化为统一的当前平台认识的分隔符呢?


本文内容

    • 现有方案
      • 没有原生方案(.NET)
      • 为什么 .NET 原生不做统一化?
    • 自己实现
      • 简单省事型
      • 高性能型
    • 如何避免

现有方案

没有原生方案(.NET)

System.IO.Path 带了一堆方法用来处理路径。各大文档博客和书籍也都推荐大家使用 Path 来处理路径字符串的拼接、拆分和提取等,这可以很大程度避免不同遭遇不同平台下路径分隔字符串不一致导致的各种问题。

不过,本文想告诉大家的是,Path 处理路径字符串也不是万能的,这体现在处理跨操作系统的路径字符串时。

现在,我列举了 6 个不同的路径字符串:

var part0 = @"/mnt/d/walterlv/";
var part1 = @"D:\walterlv\";

var part2 = @"Foo/Bar.cs";
var part3 = @"Foo\Bar.cs";

var part4 = @"/mnt/d/walterlv/Foo/Bar.cs";
var part5 = @"D:\walterlv\Foo\Bar.cs";

分成三组。前两个是路径的前半部分,中间两个是路径的后半部分,最后两个是完整路径。每组里面,前者是 Linux 风格的路径分隔符,后者是 Windows 风格的路径分隔符。

现在,我将试图将以下几种混合情况下的路径拼接使用 Path 可能格式化的方法输出出来:

// 看看 Linux 风格和 Windows 风格直接拼接的换行符使用 Path.Combine 能否格式化成功。
var pathFromCombine0 = Path.Combine(part0, part3);
var pathFromCombine1 = Path.Combine(part1, part2);
Console.WriteLine($"Path.Combine(part0, part3) = {pathFromCombine0}");
Console.WriteLine($"Path.Combine(part1, part2) = {pathFromCombine0}");

// 通过 Path.GetFullPath 转相对路径到完整路径时,看看能否将路径格式化成当前平台。
var pathFromFull0 = Path.GetFullPath(part2);
var pathFromFull1 = Path.GetFullPath(part3);
Console.WriteLine($"Path.GetFullPath(part2) = {pathFromFull0}");
Console.WriteLine($"Path.GetFullPath(part3) = {pathFromFull1}");

// 通过 new FileInfo(file).FullName 的一层转换看看能否将混合路径格式化成当前平台。
var pathFromFileInfo0 = new FileInfo(pathFromCombine0).FullName;
var pathFromFileInfo1 = new FileInfo(pathFromCombine1).FullName;
Console.WriteLine($"FileInfo(part0 + part3).FullName = {pathFromFileInfo0}");
Console.WriteLine($"FileInfo(part1 + part2).FullName = {pathFromFileInfo1}");

// 通过 new FileInfo(file).FullName 的一层转换看看能否将非当前平台的路径格式化成当前平台。
var pathFromFileInfo2 = new FileInfo(part4).FullName;
var pathFromFileInfo3 = new FileInfo(part5).FullName;
Console.WriteLine($"Path.GetFullPath(part4) = {pathFromFileInfo2}");
Console.WriteLine($"Path.GetFullPath(part5) = {pathFromFileInfo3}");

猜猜以上代码在 Windows 和 Linux 平台会输出什么?

看图!

Windows 和 Linux 平台下的输出

图是拼接的,上面一半是 Windows 平台下的运行结果,下面一半是 Linux Ubuntu 18.04 发行版的运行结果。运行时是 .NET Core 3.1。

可以发现这些点:

  1. Path.Combine 的路径拼接仅决定如何合并两段字符串,不会将已有的路径格式化成当前平台的路径分隔符。
  2. Path.GetFullPath 在生成完整路径的时候,虽然补全的部分是当前平台的,但已有的部分依然是原本字符串。
  3. new FileInfo().FullName 在 Windows 平台下可以完美将路径字符串统一成 Windows 平台的风格;但在 Linux 平台上不会统一,已有的 \ 不会变成 /;无论是拼接的字符串,还是原本别的平台的字符串,都是一样的结论。

为什么 .NET 原生不做统一化?

看前面结论可知,在 Windows 平台下是可以将 /\ 全部格式化成 Windows 平台的 \ 的,但 Linux 下却不行。

这并不是因为 .NET 没去做,而是无法做!

在 Linux 下,\ 是合理的文件名

另外,路径经常使用在 Shell 中,而在 Shell 中,\ 是个转义字符

例如,你可以有一个文件,名字是 foo\bar.txt

所以,.NET 绝对不能擅自给你将 \ 当作路径分隔符进行格式化!

关于 \ 在 Linux Shell 中的转义,你可以阅读我的另外两篇博客了解:

  • 了解 Windows/Linux 下命令行/Shell 启动程序传参的区别,这下不用再担心 Windows 下启动程序传参到 Linux 下挂掉了 - walterlv

自己实现

知道了 Linux 是合理的文件名后,当然不能再指望有某个通用的解决方法了。因为通用代码不可能知道在你的上下文下,\ 是否是合理的文件名。在信息不足的情况下,前面 .NET 的 new FileInfo().FullName 已经是最好的解决方案了。

所以,如果你明确这些不同种类的路径字符串的来源你都清楚(没错,就是你自己挖出来的坑),拼接出来之后的后果你才能知道是否是符合业务的。这时你才应该决定是否真的要做路径的格式化。

简单省事型

var path = path
    .Replace('/', Path.DirectorySeparatorChar)
    .Replace('\\', Path.DirectorySeparatorChar);

高性能型

自己实现去。

如何避免

从前面的分析可以知道,如果每个框架、库还有业务开发者都不去作死把平台特定的路径传递到其他平台,那么根本就不会存在不同平台的路径会拼接的情况。

另外,开发者也不应该随便在代码中写死 / 或者 \\ 作为路径的分隔符。

就这样……


参考资料

  • How to enable linux support double backslashes “\” as the path delimiter - Stack Overflow

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • git 乱改你的换行符?一句话设置让 git 不再碰你某个文件的换行符
  • Linux Shell 中需要转义的字符
  • Unity3D 入门:Unity Editor 编辑器常用快捷键
  • Unity3D 入门:Unity 项目版本管理建议使用的 .gitignore 忽略文件和 .gitattributes 文件(2020年4月更新)
  • Unity3D 入门:让 C# 脚本公开可在 Unity 编辑器中设置的属性
  • Unity3D 入门:如何管理 Unity 项目中的 NuGet 包?使用第三方 NuGet 包管理器——NuGetForUnity
  • Unity3D 入门:如何在脚本中找到游戏对象的父子级 祖孙级对象和它们的组件
  • Unity3D 入门:如何制作天空效果?天空盒的使用
  • Unity3D 入门:使用 Visual Studio 开发 Unity C# 脚本,说说根目录的那些 sln 和 csproj 文件
  • Unity3D 入门:最简单的控制视角,以及控制角色前进、转向的脚本
  • 比较 Windows 上四种不同的文件(夹)链接方式(NTFS 的硬链接、目录联接、符号链接,和大家熟知的快捷方式)
  • 了解 Windows Linux 下命令行 Shell 启动程序传参的区别,这下不用再担心 Windows 下启动程序传参到 Linux 下挂掉了
  • 适合 .NET 开发者用的 GitHub Actions(时不时更新)
  • 在 CMD 里根据进程名杀掉进程
  • 在 PowerShell 里根据进程名杀掉进程
  • Google 是如何开发 Web 框架的
  • JavaScript 如何正确处理 Unicode 编码问题!
  • [Vue CLI 3] 配置解析之 css.extract
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • EventListener原理
  • GitUp, 你不可错过的秀外慧中的git工具
  • isset在php5.6-和php7.0+的一些差异
  • JavaScript 一些 DOM 的知识点
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • leetcode讲解--894. All Possible Full Binary Trees
  • Less 日常用法
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • Python学习之路13-记分
  • SAP云平台里Global Account和Sub Account的关系
  • ubuntu 下nginx安装 并支持https协议
  • vue 配置sass、scss全局变量
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • Zepto.js源码学习之二
  • 分享一份非常强势的Android面试题
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 设计模式 开闭原则
  • NLPIR智能语义技术让大数据挖掘更简单
  • 交换综合实验一
  • ​水经微图Web1.5.0版即将上线
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #HarmonyOS:基础语法
  • $forceUpdate()函数
  • (07)Hive——窗口函数详解
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (js)循环条件满足时终止循环
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)VirtualBox安装增强功能
  • (原創) 未来三学期想要修的课 (日記)
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .bat文件调用java类的main方法