Path路径
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()
- 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 文件名与扩展名
Path和PathBuf操作一样
4.1.1 提取
file_name、extension、file_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.路径属性判断
Path和PathBuf这两个类型都可以使用这些方法
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, .. }
5.6 判断是否是软连接is_symlink
创建一个软链接
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_name和set_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"

浙公网安备 33010602011771号