如何从规范的 Windows 路径中删除 \?\ 前缀?

问题描述 投票:0回答:4

在 Windows 上,

Path::canonicalize()
以以下格式返回路径:

\\\\?\\C:\\projects\\3rdparty\\rust...

这是因为它是正确的规范路径,并且在 Windows 上允许“长”路径(请参阅为什么我的规范化路径以 \?\ 为前缀)。

然而,这不是一条用户友好的路径,人们不理解它。

出于 显示和日志目的 我怎样才能以通用平台无关的方式轻松删除此前缀?

Path::components
将返回一个组件
\\?\C:
作为第一个组件...

我应该将其转换为

&str
并使用正则表达式吗?是否有其他更符合人体工程学的方法来删除前缀,例如一些具有
Display
实现的类型可以自动做正确的事情?

我的具体要求是:

  • 正确显示
    X:\\...
    在 Windows 上的规范路径。
  • 不会搞砸非 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);
}
windows rust
4个回答
8
投票

使用

dunce
板条箱

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

只是剥离

\\?\
可能会给出错误/无效的路径。 dunce crate 检查 UNC 路径是否兼容,并尽可能准确地转换路径。它通过所有其他路径。它在非 Windows 上编译为纯
fs::canonicalize()


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 资源管理器可以很好地处理这些逐字路径,我认为用户也有能力处理它。

为了 [...] 记录目的

这听起来是个糟糕的主意。如果你正在记录一些东西,你想知道 exact 路径,而不是一些可能不正确的路径。


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

0
投票

您可以为 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();
© www.soinside.com 2019 - 2024. All rights reserved.