如何从一个Windows规范路径中移除\\?\前缀?

7
在Windows平台上,Path::canonicalize()返回的路径格式为:
\\\\?\\C:\\projects\\3rdparty\\rust...

这是因为这是正确的规范路径,并允许在Windows上使用“长”路径(参见为什么我的规范化路径前缀为\\?\)。
然而,这不是用户友好的路径,人们无法理解它。
为了显示和记录日志,如何以通用平台无关的方式轻松地删除此前缀? Path :: components将返回组件\\?\C:作为第一个组件...
我应该将其转换为&str并使用正则表达式吗?是否有其他更符合人体工程学的方法来删除前缀,例如一些具有Display实现的类型自动执行正确的操作?
我的要求具体包括:
- 在Windows上,对于规范路径,正确显示X:\\...。 - 不会损坏非Windows平台(例如剥离或更改路径组件)。
示例:
use std::path::{Path, PathBuf};

fn simple_path<P: AsRef<Path>>(p: P) -> String {
    String::from(p.as_ref().to_str().unwrap()) // <-- ?? What to do here?
}

pub fn main() {
    let path = PathBuf::from("C:\temp").canonicalize().unwrap();
    let display_path = simple_path(path);
    println!("Output: {}", display_path);
}

2
\\?\UNC\server\share\\?\cat_pics 应该发生什么? - Shepmaster
“Useless” 这个词有点过了,但我同意在这里使用正则表达式肯定是杀鸡焉用牛刀。 - Shepmaster
4个回答

11

使用 dunce crate

extern crate dunce;
…
let compatible_path = dunce::canonicalize(&any_path);

仅仅去掉\\?\可能会导致错误或无效的路径。dunce crate检查UNC路径是否兼容,并在可能的情况下准确地转换路径。它会通过所有其他路径。在非Windows系统上,它编译为普通的fs::canonicalize()


尽可能准确地转换路径,因此它并不总是删除“\?”?听起来像是原帖的用户仍然会生气。 - Shepmaster
@Shepmaster,你是在说它不能进行不可能的转换是一个缺陷吗? - Kornel
根本不是这样的;只是OP / OP的用户想要一些不可能的东西,因此他们总是会失望。 - Shepmaster

6
简单明了的答案是进行特定于平台的字符串操作:
use std::path::{Path, PathBuf};

#[cfg(not(target_os = "windows"))]
fn adjust_canonicalization<P: AsRef<Path>>(p: P) -> String {
    p.as_ref().display().to_string()
}

#[cfg(target_os = "windows")]
fn adjust_canonicalization<P: AsRef<Path>>(p: P) -> String {
    const VERBATIM_PREFIX: &str = r#"\\?\"#;
    let p = p.as_ref().display().to_string();
    if p.starts_with(VERBATIM_PREFIX) {
        p[VERBATIM_PREFIX.len()..].to_string()
    } else {
        p
    }
}

pub fn main() {
    let path = PathBuf::from(r#"C:\Windows\System32"#)
        .canonicalize()
        .unwrap();
    let display_path = adjust_canonicalization(path);
    println!("Output: {}", display_path);
}

就记录而言,我不认为你的前提是一个好主意。Windows资源管理器可以很好地处理这些字面路径,我认为用户也能够应对。

出于[...]记录的目的

这听起来像是个糟糕的想法。如果你要记录某些东西,你需要知道确切的路径,而不是可能不正确的路径。


我收到了一份关于内部cli工具的具体反馈,其中路径渲染为\\?\c:会令人困惑,并且在复制粘贴到notepad++时无法正常工作。除此之外,我还能提供什么现实可行的解决方案呢? - Doug
1
听起来这个问题应该在notepad++中得到解决,而不是在其他地方出现故障,他们的跟踪器上是否有关于此问题的问题? - mcarton
有趣的是,Notepad++确实支持这些内容 ;) - hellow
@hellow,这真的不行。尝试将其复制并粘贴到“文件名”文本框中的“文件”->“打开”对话框中。即使您手动输入它,它也会自动完成,但仍无法打开文件。 - Doug
Notead++ v7.5.6(64位)。你的版本是什么? - hellow
@hellow 7.5.4(32位)。不过,如果我的应用程序由于另一个应用程序中的错误而“无法工作”,那仍然是我的问题要解决。我不能只是说“哦,我会向应用程序XXX提出错误报告,因为那是一个错误”,不用担心。显示该前缀假定每个其他应用程序和用户都将正确处理它,并知道仅复制和粘贴其中的相关部分,这是可以证明是错误的。/耸肩 - Doug

1
这里有一个版本,可以从组件中重构路径
它对 Windows 上的 std::fs::canonicalize 有帮助,但是在 play.rust-lang.org 上使用一个简单的 Path::new(r"\\?\C:\projects\3rdparty\rust") 将会生成一个单组件的 Path
use std::path::{Component, Path, PathBuf, Prefix};

// Should remove the “\\?” prefix from the canonical path
// in order to avoid CMD bailing with “UNC paths are not supported”.
let head = path.components().next().ok_or("empty path?")?;
let diskˢ;
let head = if let Component::Prefix(prefix) = head {
    if let Prefix::VerbatimDisk(disk) = prefix.kind() {
        diskˢ = format!("{}:", disk as char);
        Path::new(&diskˢ).components().next().ok_or("empty path?")?
    } else {
        head
    }
} else {
    head
};
println!("{:?}", head);
let path = std::iter::once(head)
    .chain(path.components().skip(1))
    .collect::<PathBuf>();

-1

你可以为 PathBuf 实现一个 StripCanonicalization 特质,这有助于保持可链接的调用。

pub trait StripCanonicalization where Self: AsRef<Path> {
    #[cfg(not(target_os = "windows"))]
    fn strip_canonicalization(&self) -> PathBuf {
        self.as_ref().to_path_buf()
    }

    #[cfg(target_os = "windows")]
    fn strip_canonicalization(&self) -> PathBuf {
        const VERBATIM_PREFIX: &str = r#"\\?\"#;
        let p = self.as_ref().display().to_string();
        if p.starts_with(VERBATIM_PREFIX) {
            PathBuf::from(&p[VERBATIM_PREFIX.len()..])
        } else {
            self.as_ref().to_path_buf()
        }
    }
}

impl StripCanonicalization for PathBuf {}

调用方式

let p = Path::new(s).canonicalize()?;
let p = p.strip_canonicalization();

这段代码对于非UTF8文件名是不正确的。 - mineichen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接