Path路径

news/2025/11/12 0:12:02/文章来源:https://www.cnblogs.com/lxd670/p/19211953

1.Path vs PathBuf 区别

类型 特性 适用场景 类比关系
Path 不可变、无所有权(&Path 临时引用路径、只读操作 &str(不可变字符串)
PathBuf 可变、有所有权 动态拼接、修改路径 String(可变字符串)

2 路径创建

2.1 Path

不可变,因为是引用类型,无法修改其指向的路径数据(返回&Path对象)

2.1.1 Path::new创建

use std::path::Path;fn main() {let p: &Path = Path::new("/home/user");
}

2.2 PathBuf

可变(需用 mut 声明),可以修改路径内容(如追加、替换组件)

2.2.1 PathBuf::new创建

初始化一个空的Vec<u8>Vec::new()的默认容量就是 0。

use std::path::PathBuf;fn main() {let mut buf1 = PathBuf::new();// 动态扩容buf1.push("/a/b/c");println!("{:?}", buf1);
}
"/a/b/c"

2.2.2 PathBuf::from创建

从字符串初始化

use std::path::{Path, PathBuf};fn main() {let buf:PathBuf = PathBuf::from("/home/user");
}

2.2.3 PathBuf::with_capacity创建

预分配n字节容量的空PathBuf 已知路径大致长度,追求性能优化

use std::path::PathBuf;fn main() {// 预分配 20 字节容量的 PathBuf(适合已知路径大致长度的场景)let mut path_buf = PathBuf::with_capacity(20);path_buf.push("/a/b/c/new_dir"); // 缓冲区足够,无需扩容println!("{:?}", path_buf);
}
"/a/b/c/new_dir"

2.3 转换

2.3.1 Path

Path转换PathBuf

p.into()p.to_path_buf()都能将 &Path 转换为 PathBuf

  • 实现机制
    • to_path_buf():基于&Path的数据创建一个新的PathBuf
    • into():Into trait实现。PathBuf为&Path实现了Into<PathBuf>,而这个实现的内部本质上就是调用to_path_buf()
use std::path::{Path, PathBuf};fn main() {let p = Path::new("/home/user");// into()转换// let buf: PathBuf = p.into();let buf: PathBuf = p.to_path_buf();
}

2.3.2 PathBuf

PathBuf转换Path

pb.as_path()进行转化

use std::path::{Path, PathBuf};fn main() {let buf = PathBuf::from("/home/user");// as_path转换let p: &Path = buf.as_path();
}

3.路径拼接

使用join()进行拼接,会创建新PathBuf

3.1Path拼接路径

3.1.1 join

返回新PathBuf,适合临时使用基础路径拼接场景

use std::path::Path;fn main() {let path = Path::new("/home/user");let new_path:PathBuf = path.join("subdir").join("file.txt");println!("path: {:?}", path);println!("new_path: {:?}", new_path);
}
path: "/home/user"
new_path: "/home/user/subdir/file.txt"

3.2 PathBuf拼接路径

3.2.1 join

Path.join一样,返回新的PathBuf,对原有的Path/PathBuf不进行修改

use std::path::PathBuf;fn main() {let path = Path::new("/home/user");let new_path:PathBuf = path.join("subdir").join("file.txt");println!("path: {:?}", path);println!("new_path: {:?}", new_path);
}
path: "/home/user"
new_path: "/home/user/subdir/file.txt"

3.2.2 push

直接修改原PathBuf,在自身末尾拼接路径组件

use std::path::PathBuf;fn main() {let mut buf = PathBuf::from("/home/user");buf.push("subdir");buf.push("file.txt");println!("buf: {:?}", buf);
}
buf: "/home/user/subdir/file.txt"

4. 提取路径组件

4.1 文件名与扩展名

PathPathBuf操作一样

4.1.1 提取

file_nameextensionfile_stem

use std::path::Path;fn main() {let path = Path::new("/home/user/docs/note.txt");// 获取文件名(含扩展名)println!("文件名: {:?}", path.file_name()); // Some("note.txt")// 获取扩展名(不含".")println!("扩展名: {:?}", path.extension()); // Some("txt")// 获取文件名(不含扩展名)println!("纯文件名: {:?}", path.file_stem()); // Some("note")
}
文件名: Some("note.txt")
扩展名: Some("txt")
纯文件名: Some("note")

4.1.2 替换扩展名

with_extension会返回新的PathBuf

use std::path::Path;fn main() {let path = Path::new("/home/user/docs/note.txt");let new_path = path.with_extension("mp4");println!("替换扩展名:{:?}", new_path);
}
替换扩展名:"/home/user/docs/note.mp4"

4.1.3 替换文件名

会返回新的PathBuf

use std::path::{Path};fn main() {let path = Path::new("/home/user/docs/aaa.txt");let new_path = path.with_file_name("summary.pdf");println!("替换扩展名:{:?}", new_path); // 输出 "/home/user/docs/report.txt"
}
替换扩展名:"/home/user/docs/summary.pdf"

4.2 提取路径

4.2.1 提取父目录

parent()(其实等价于 .ancestors().nth(1))

use std::path::{Path, PathBuf};fn main() {let path = Path::new("/home/user/docs/aaa.txt");println!("文件名: {:?}", path.parent());let path_buf = PathBuf::from("/home/user/docs/bbb.txt");println!("文件名: {:?}", path_buf.parent());
}
文件名: Some("/home/user/docs")
文件名: Some("/home/user/docs")

4.2.2 获取多层父目录

Path::parent() 每调用一次,只会返回 上一级目录
如果你想向上多级,就需要多次调用,或者更优雅地用 ancestors()

4.2.2.1 ancestors

ancestors() 会自动生成一个迭代器Iterator<Item = &Path>

use std::path::Path;fn main() {let path = Path::new("/usr/local/bin/rustc");for ancestor in path.ancestors() {println!("{:?}", ancestor);}
}
"/usr/local/bin/rustc"
"/usr/local/bin"
"/usr/local"
"/usr"
"/"

4.2.2.2 ancestors + nth

use std::path::Path;fn main() {let path = Path::new("/usr/local/bin/rustc");let parent_1 = path.ancestors().nth(1).unwrap(); // 上一级let parent_2 = path.ancestors().nth(2).unwrap(); // 上两级let parent_3 = path.ancestors().nth(3).unwrap(); // 上三级println!("上一级: {:?}", parent_1);println!("上两级: {:?}", parent_2);println!("上三级: {:?}", parent_3);
}
上一级: "/usr/local/bin"
上两级: "/usr/local"
上三级: "/usr"

4.2.3 components

返回一个迭代器Iterator<Item = Component>

Component::RootDir:表示根目录(如 Unix 下的 /、Windows 下的 \)。

Component::Normal(s):表示普通的目录名或文件名(如 home、note.txt)。

Windows 特有Component::Prefix(...):表示盘符前缀(如 C:)。

  • ancestors区别是
    • path.ancestors(): 从尾到头(子 → 父)
    • path.components():从头到尾(根 → 子)

4.2.3.1 遍历所有组件

use std::path::Path;fn main() {let path = Path::new("/usr/local/bin/rustc");for comp in path.components() {println!("{:?}", comp);}
}
RootDir
Normal("usr")
Normal("local")
Normal("bin")
Normal("rustc")

4.2.3.2 配合match匹配

use std::path::{Path, Component};fn main() {// Unix 风格路径示例let unix_path = Path::new("/home/user/docs/note.txt");println!("Unix 路径组件:");for comp in unix_path.components() {match comp {Component::RootDir => println!("  根目录: /"),Component::Normal(name) => println!("  普通组件: {}", name.to_string_lossy()),_ => println!("  其他组件: {:?}", comp),}}
}

4.2.3.3 take(n)

只取前 n 个元素,之后迭代器就结束

path.components().take(n).collect();可以截取前n个组件

use std::path::{Component, Path, PathBuf};fn main() {let path = std::path::Path::new("/home/user/docs/file.txt");for comp in path.components().take(3) {println!("{:?}", comp);}
}
RootDir
Normal("home")
Normal("user")

4.2.3.4 skip(n)

跳过前 n 个元素,之后迭代器就结束

use std::path::{Component, Path, PathBuf};fn main() {let path = std::path::Path::new("/home/user/docs/file.txt");for comp in path.components().skip(3) {println!("{:?}", comp);}
}
Normal("docs")
Normal("file.txt")

5.路径属性判断

PathPathBuf 这两个类型都可以使用这些方法

5.1 检测路径开头starts_with

use std::path::{Path, PathBuf};fn main() {let path = Path::new("/home/user/photo.jpg");let head_path = Path::new("/home/user");let res = path.starts_with(head_path);println!("{}", res);
}
true

5.2 路径/文件是否存在exists

use std::path::{Path, PathBuf};fn main() {let path = Path::new("/a/b/c.txt");println!("exists: {}", path.exists());
}
exists: false

5.3 判断文件is_file

文件不存在、目录、无权限访问都会返回false

use std::path::{Path, PathBuf};fn main() {// 文件不存在let path = Path::new("/a/b/c.txt");println!("is_file: {}", path.is_file());let path = 	Path::new("/Users/my_space/Code/rust_code/rc_test/data.txt");println!("is_file: {}", path.is_file());
}
is_file: false
is_file: true

5.4 判断目录is_dir

如果路径不存在无权限访问的也返回false

use std::path::{Path, PathBuf};fn main() {let path = 	Path::new("/Users/my_space/Code/rust_code/rc_test/data.txt");println!("is_dir: {}", path.is_dir());let path = 	Path::new("/Users/my_space/Code/rust_code/rc_test");println!("is_dir: {}", path.is_dir());
}
is_dir: false
is_dir: true

5.5 获取元数据metadata

use std::path::{Path, PathBuf};fn main() {let path = 	Path::new("/Users/my_space/Code/rust_code/rc_test/data.txt");println!("metadata: {:?}", path.metadata());
}
metadata: Ok(Metadata { file_type: FileType { is_file: true, is_dir: false, is_symlink: false, .. }, permissions: Permissions(FilePermissions { mode: 0o100644 (-rw-r--r--) }), len: 12, modified: SystemTime { tv_sec: 1762789499, tv_nsec: 901106416 }, accessed: SystemTime { tv_sec: 1762789501, tv_nsec: 567357216 }, created: SystemTime { tv_sec: 1762789499, tv_nsec: 901025875 }, .. })

5.5.1 获取元数据

meta.accessed()获取的是SystemTime

如果需要转化为年月日时分秒,使用第三方chrono

use std::fs;
use std::path::{Path, PathBuf};fn main() {let path = 	Path::new("/Users/my_space/Code/rust_code/rc_test/data.txt");match fs::metadata(path) {Ok(meta) => {println!("是否文件: {}", meta.is_file());println!("是否目录: {}", meta.is_dir());println!("文件大小: {} 字节", meta.len());println!("最后修改时间: {:?}", meta.modified());println!("最后访问时间: {:?}", meta.accessed());println!("文件创建时间: {:?}", meta.created());println!("文件类型: {:?}", meta.file_type());}Err(e) => eprintln!("读取元数据失败: {}", e),}
}
是否文件: true
是否目录: false
文件大小: 12 字节
最后修改时间: Ok(SystemTime { tv_sec: 1762789499, tv_nsec: 901106416 })
最后访问时间: Ok(SystemTime { tv_sec: 1762789501, tv_nsec: 567357216 })
文件创建时间: Ok(SystemTime { tv_sec: 1762789499, tv_nsec: 901025875 })
文件类型: FileType { is_file: true, is_dir: false, is_symlink: false, .. }

创建一个软链接 ln -s data.txt data_ln_s.txt

use std::path::{Path, PathBuf};fn main() {let path = 	Path::new("/Users/my_space/Code/rust_code/rc_test/data.txt");println!("data.txt 是符号链接吗? {}", path.is_symlink());let ln_s_path = 	Path::new("/Users/my_space/Code/rust_code/rc_test/data_ln_s.txt");println!("ata_ln_s.txt 是符号链接吗? {}", ln_s_path.is_symlink());
}
data.txt 是符号链接吗? false
ata_ln_s.txt 是符号链接吗? true

5.7 判断绝对路径is_absolute

use std::path::PathBuf;fn main() {let path_buf = PathBuf::from("/a/b/c.txt");let is_absolute = path_buf.is_absolute();println!("is_absolute:{}", is_absolute);
}

5.8 判断绝对路径is_relative

use std::path::PathBuf;fn main() {let path_buf = PathBuf::from("../a/b/c.txt");let is_relative = path_buf.is_relative();println!("is_relative:{}", is_relative);
}
is_relative:true

5.9 判断有根路径has_root

use std::path::PathBuf;fn main() {let path_buf1 = PathBuf::from("../a/b/c.txt");let has_root1 = path_buf1.has_root();println!("has_root:{}", has_root1);let path_buf2 = PathBuf::from("/a/b/c.txt");let has_root2 = path_buf2.has_root();println!("has_root:{}", has_root2);
}
has_root:false
has_root:true

6.路径/文件替换

6.1文件替换

6.1.1 Path替换

返回一个新的PathBuf

use std::path::Path;fn main() {let mut path = Path::new("/home/user/note.txt");// 替换文件名(含扩展名)let p1 = path.with_file_name("new_note.md");println!("替换文件名后: {:?}", p1);// 替换扩展名(仅修改扩展名部分)let p2 = path.with_extension("txt");println!("替换扩展名后: {:?}", p2);
}
替换文件名后: "/home/user/new_note.md"
替换扩展名后: "/home/user/note.txt"

6.1.2 PathBuf替换

原地修改,不返回新的PathBuf

Path没有set_file_nameset_extension方法

如果需要替换Path,先通过 to_path_buf() 转换成 PathBuf

use std::path::PathBuf;fn main() {let mut path = PathBuf::from("/home/user/note.txt");// 替换文件名(含扩展名)path.set_file_name("new_note.md");println!("替换文件名后: {:?}", path);// 替换扩展名(仅修改扩展名部分)path.set_extension("txt");println!("替换扩展名后: {:?}", path);
}
替换文件名后: "/home/user/new_note.md"
替换扩展名后: "/home/user/new_note.txt"

6.2 路径替换

6.2.1 替换路径前缀strip_prefix

strip_prefix(old_prefix):检查原始路径是否以 old_prefix 开头,若符合则返回 移除前缀后的子路径。若不匹配则返回 None(需处理错误)

6.2.1.1 strip_prefix用法

use std::path::{Path, PathBuf};fn main() {let path = Path::new("/home/user/photo.jpg");let old_p1 = Path::new("/home/user");let old_p2 = Path::new("/home/abc");let s1 = path.strip_prefix(old_p1);println!("{:?}", s1);let s2 = path.strip_prefix(old_p2);println!("{:?}", s2);
}
Ok("photo.jpg")
Err(StripPrefixError(()))

6.2.1.2 结合if判断处理

use std::path::{Path, PathBuf};fn main() {let path = Path::new("/home/user/photo.jpg");let old_p = Path::new("/home/user");let new_p = Path::new("/home/user/abc");if let Ok(new_path) = path.strip_prefix(old_p) {println!("替换后: {:?}", new_p.join(new_path));} else {println!("替换失败");}
}
替换后: "/home/user/abc/photo.jpg"

6.2.2 替换中间路径

配合Component,这里没有适配window的环境

Component+map

use std::path::{Path, PathBuf, Component};fn main() {let path = Path::new("/a/b/c/old_dir/d/file.txt");let old = "old_dir";let new = "new_dir";let new_path:PathBuf = path.components().map(|comp| if let Component::Normal(os_str) = comp {// os_str.to_str()将&OsStr转为Option<&str>// Some(old)将&str转换成Option<&str>if os_str.to_str() == Some(old) {Component::Normal(new.as_ref())} else {comp}} else {comp}).collect();println!("{:?}", new_path)
}
"/a/b/c/new_dir/d/file.txt"

Component+for循环

use std::path::{Component, Path, PathBuf};fn main() {let path = Path::new("/a/b/c/old_dir/d/file.txt");let old = "old_dir";let new = "new_dir";// 创建一个新的pathbuf对象let mut new_path = PathBuf::with_capacity(path.as_os_str().len());for comp in path.components() {match comp {Component::RootDir => {new_path.push(comp);}Component::Normal(name) => {// 安全处理:先将 &OsStr 转为 &str(非UTF-8路径直接保留原组件)if let Some(name_str) = name.to_str() {if name_str == old {// 匹配旧组件,push 新组件new_path.push(new);} else {// 不匹配,push 原组件new_path.push(name);}} else {// 非 UTF-8 组件,直接保留new_path.push(name);}}_ => {}}}println!("{:?}", new_path)
}
"/a/b/c/new_dir/d/file.txt"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/962842.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI编程软件三强:谁才是2025年你的最佳AI编程软件拍档?

AI编程软件三强:谁才是2025年你的最佳AI编程软件拍档?背景 近两年,智能编程助手的崛起彻底改变了开发者的工作方式。从代码补全到全流程生成,主流市场已形成几类代表性工具:以深度集成为特色的 Trae、以传统编辑器…

研发效率新标杆:五款AI编程工具实战榜单

研发效率新标杆:五款AI编程工具实战榜单​ 序言:开发效率的新拐点 当“写代码”逐渐演变为“描述需求、AI来写”,研发效率迎来了全新的天花板。2025 年,一些智能工具已经帮助众多企业缩短了项目周期并减少了人力投…

全景洞察:2025年AI编程工具生态与最佳组合策略

全景洞察:2025年AI编程工具生态与最佳组合策略一、宏观格局:AI编程进入全流程时代 根据行业研究,2025年全球AI编程市场规模已突破数万亿元,年增长率达到60%以上,各大云厂商和科技企业不断推出自己的智能编程方案。…

编程小白的福音:十款AI编程助手助你轻松入门

编程小白的福音:十款AI编程助手助你轻松入门导语 还在担心不会写代码?随着人工智能的发展,从没有编程经验到能够搭建简易应用的距离正在缩短。许多工具不仅提供代码补全,还能生成整套前端和后端逻辑,甚至将设计稿…

径向基函数概率神经网络

径向基函数概率神经网络(Radial Basis Function Probabilistic Neural Network, RBF-PNN)通常指概率神经网络(PNN),因其核心就是使用高斯径向基函数作为核函数。这是一种基于贝叶斯决策理论和核密度估计的神经网络…

11月10日日记

1.今天学习java 2.明天工程实训 3.批量操作的优化方案(PreparedStatement + addBatch + 关闭自动提交)如何实现?

11月11日日记

1.今天工程实训 2.明天离散数学考试 3.如何排查连接泄露?连接池的核心参数(maxActive、maxIdle、minIdle、maxWait)如何合理配置?

别被热潮迷惑:八款AI智能编程助手全景对比

别被热潮迷惑:八款AI智能编程助手全景对比人工智能已经深度渗透到软件开发领域。Gartner 预计,到2028年全球约有 75% 的代码将由 AI 自动生成。这意味着开发者需要的不仅仅是单个插件,而是一套覆盖分析、编码、调试…

每日一导2

Problem 已知函数 $ f(x) =x \ln x - a x^2 -x +a \hspace {0.1cm} (a\in R)$ . 若 \(f(x)\) 有两个极值点 \(x_1,x_2(x_1<x_2)\) ,证明:当 $ \lambda \geq 1$ 时, $ \ln x_1 + \lambda \ln x_2 > 1+\lambda…

如何计算并发及机器

如何计算并发及机器首先要知道整个系统需要多少个线程干活: 系统总并发数计算: 线程数据=QPS*平均响应时间; 这是总资源目标,接下来我们把总的线程数分配到每台机器上。 设计单机线程池: 线程池:参数里面:分为“…

CF2164 VP 记录

A 猜结论. 找到最大值和最小值,显然可以把整个序列删的只剩这两个数,那么只要询问的数在这之间就一定可以. B 脑筋急转弯,非常的 tricky,我不喜欢. 首先若存在两个偶数,那么直接输出即可. 其次若有一个偶数,可以…

超参数

超参数(Hyperparameter) 是机器学习模型的 "外部设定" ,由人预先指定,不通过训练自动学习。核心区别:超参数 vs 模型参数 表格 复制对比项超参数模型参数谁来定 你(开发者) 设定 算法自动学习例子 学…

Weblate审计日志IP泄露漏洞分析

本文详细分析了CVE-2025-64326漏洞,该漏洞导致Weblate在审计日志中泄露邀请用户参与项目审查的管理员IP地址,涉及敏感信息处理不当的安全问题。Weblate泄露项目成员IP地址的审计日志漏洞 漏洞详情 包信息包管理器: p…

flask: flask-httpauth做登录验证

一,安装第三方库 auth $ pip install flask-httpauth jwt $ pip install Authlib 二,代码: from flask import Blueprint,jsonify,render_template,request from flask import request, Flask, redirect, url_for, …

每日一导1

Problem 已知 $ f(x) =ae^{x-1}+\frac{1}{a}-3 \ln x - 3 ,a>0 $ . 证明: $ \forall \theta \in (0,\frac{\pi}{2}) , f(\sin \theta) + f(\cos \theta ) >-1$ .分析 和同学看了参考答案之后大为震撼。 两个超越…

HBuilderX快速安装与使用指南 - 从下载到项目创建全流程

一篇搞定:零基础也能5分钟跑起第一个HBuilderX项目(含uni-app、Vue3、TS、原生小程序全流程示例)一、HBuilderX 是什么?定位 亮点国产「前端全端」IDE 一套代码编到 6 端(H5、小程序、App、iOS、Android、桌面)u…

java使用正则表达式替换并保留原字符串,在原字符串前后添加符号

java使用正则表达式替换并保留原字符串,在原字符串前后添加符号sql = sql.replaceAll(":admdivcode(\\d{6})", "$1"); 实现结果是如:sql = "select * from tbb where admdivcode = :admdiv…