File文件
1.读取文件内容
| 读取方法 | 底层操作(核心) | 上层处理(对字节的后续操作) | 适用场景 |
|---|---|---|---|
read |
读取字节到 &mut [u8] 数组 |
直接使用字节(不做任何转换) | 二进制文件分块读、大文件 |
read_to_end |
读取所有字节到Vec<u8> |
直接使用字节(保留原始字节流) | 二进制文件全读、小文件 |
read_to_string |
读取所有字节到Vec<u8> |
按 UTF-8 编码解码成 String |
文本文件全读(.txt、.rs) |
1.1 read_to_end读取
读取变为
Vec<u8>
use std::fs::File;
use std::io::Read;
fn main() {
let path = "a.txt";
let mut fs = File::open(path).unwrap();
let mut b = Vec::new();
fs.read_to_end(&mut b).unwrap();
// 获取的是byte类型
println!("{:?}", b);
// 把读取的内容转为String
println!("{}",String::from_utf8(b).unwrap());
}
[104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
hello world
1.2 read_to_sting
读取所有字节到
Vec<u8>转为String
use std::fs::File;
use std::io::Read;
fn main() {
let path = "a.txt";
let mut fs = File::open(path).unwrap();
let mut s = String::new();
fs.read_to_string(&mut s).unwrap();
// 获取的是String类型
println!("{:?}", s);
}
"hello world"
1.3 read
读取字节到
&mut [u8]数组
1.3.1 注意
⚠️注意这样和 read_to_end 没有本质区别,还是会导致s内存溢出
use std::fs::File;
use std::io::Read;
fn main() {
let path = "a.txt";
let mut fs = File::open(path).unwrap();
let mut buf = [0u8; 4]; // 每次读 4 字节的缓冲区
let mut s = Vec::new();
loop {
match fs.read(&mut buf) {
Ok(0) => break, // 字节读完(EOF)
Ok(n) => {
// println!("本次读 {} 字节:{:x?}", n, &buf[0..n]);
s.extend_from_slice(&buf[0..n]);
}
Err(e) => panic!("读取失败:{}", e),
}
}
println!("{:?}", s);
}
1.3.2 正确做法
- 不需要解析字符(仅处理字节):保持当前代码,无问题
- 需要解析字符(文本处理):避免直接分块转字符串,用「按行读取」或「缓冲编码解析」
use std::fs::File;
use std::io::Read;
fn main() {
let path = "a.txt";
let mut fs = File::open(path).unwrap();
let mut buf = [0u8; 4]; // 每次读 4 字节的缓冲区
loop {
match fs.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
let b = &buf[0..n];
// to do someting
}
Err(e) => panic!("读取失败:{}", e),
}
}
}
1.3.3 二进制文件入取
分块处理二进制文件(视频、压缩包等)
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
fn main() {
// 原图片路径和目标保存路径
let src_path = "input.png"; // 任意图片格式(PNG/JPG/WEBP 等)
let dest_path = "output.png";
// 打开源文件(只读)和目标文件(创建/覆盖写入)
let mut src_file = File::open(src_path).expect("无法打开源图片");
let mut dest_file = OpenOptions::new()
.create(true) // 不存在则创建
.write(true) // 可写入
.truncate(true) // 存在则覆盖
.open(dest_path)
.expect("无法创建目标图片");
// 缓冲区:4KB(优化 IO 效率,契合磁盘页大小)
let mut buf = [0u8; 4096];
loop {
// 分块读取源图片字节
match src_file.read(&mut buf) {
Ok(0) => break, // 读取完毕(EOF)
Ok(n) => {
// 写入当前块的有效字节(&buf[0..n]),确保字节完整
dest_file.write_all(&buf[0..n]).expect("写入图片失败");
dest_file.flush().expect("刷新缓冲区失败"); // 可选:确保数据写入磁盘
}
Err(e) => panic!("读取图片失败:{}", e),
}
}
println!("图片复制成功!");
}
1.4 大文件读写BufReader
解决read读取字符分割问题
use std::fs::File;
use std::io::{BufRead, BufReader}; // 引入 BufReader 和 BufRead trait
fn main() {
let path = "a.txt";
let file = File::open(path).unwrap();
let reader = BufReader::new(file); // 包装成带缓冲的读取器
// 按行读取,每行都是完整的 UTF-8 字符串
for line in reader.lines() {
let line = line.unwrap(); // 处理可能的 IO 错误
println!("完整行:{}", line);
}
}
完整行:hello world
2.文件写入
2.1 OpenOptions
参数
读写
- read(true)
- 开启读取权限
- write(true)
- 开启写入权限
写入控制
truncate和append互斥:不能同时设置为true,否则编译报错
- truncate(true)
- 文件存在时,清空原有内容(覆盖模式)
- append(true)
- 文件存在时,在末尾追加内容(不覆盖)
- create(true)
- 文件存在则复用,不存在则创建;
- create_new(true)
- 仅当文件不存在时创建,存在则报错(禁止覆盖)
权限控制
- mode(0oXXX)
0o644(默认):所有者可读写,其他用户只读;0o755:所有者可读写执行,其他用户只读执行(适用于可执行文件);0o600:仅所有者可读写(敏感文件,如密码)。
use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut file = OpenOptions::new()
.read(true) // 开启读取模式
.write(true) // 开启追加模式
.create(true) // 不存在创建文件
.append(true) // 开启追加模式(默认true)
.open("data1.txt")
.unwrap();
file.write_all(b"Hello, Rust!").unwrap();
file.write_all(b"Hello, Rust!").unwrap();
file.write_all(b"Hello, Rust!").unwrap();
}
append用法
append(true)时,在文件末尾添加内容
.append(false)时,在文件开头添加内容,如果原有文件有内容,会出现替换场景
use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(false) // 关闭追加
.open("data1.txt")
.unwrap();
file.write_all(b"Hello, Rust1!").unwrap();
// 多次写入不受append的影响
// 开启了 write(true)(写入权限),就可以多次调用 write_all,写入位置由文件指针控制
// file.write_all(b"Hello, Rust2!").unwrap();
// file.write_all(b"Hello, Rust3!").unwrap();
}
2.2 File::create
等于
OpenOptions::new().write(true).create(true).truncate(true).open(path)
File::create没有读取权限,如果读取的话会报错
use std::fs::File;
use std::io::Write;
fn main() {
let mut file = File::create("hello.txt").unwrap();
file.write_all(b"Hello, Rust!").unwrap();
// 编译通过,但运行报错:不支持读取
// file1.read_to_string(&mut String::new()).unwrap();
}
2.3 fs::write
若只需写一次,
fs::write更简洁
- 文件不存在,创建
- 内容覆盖
use std::fs;
fn main() {
fs::write("data.txt", "Hello, Rust!").unwrap();
}
3.文件删除
使用
std::fs::remove_file;
3.1 简单删除
use std::fs::remove_file;
fn main() {
let file_path = "data1.txt"; // 要删除的文件路径
// 删除文件(文件不存在或权限不足会 panic)
remove_file(file_path).unwrap();
println!("文件 {:?} 删除成功!", file_path);
}
3.2 优雅错误处理
remove_file返回io::Result<()>,实际项目中应避免unwrap,而是处理可能的错误(如文件不存在、权限不足):
use std::fs::remove_file;
use std::io::ErrorKind;
fn main() {
let file_path = "data1.txt";
match remove_file(file_path) {
Ok(_) => println!("文件 {:?} 删除成功!", file_path),
Err(e) => match e.kind() {
ErrorKind::NotFound => eprintln!("错误:文件 {:?} 不存在", file_path),
ErrorKind::PermissionDenied => eprintln!("错误:没有删除 {:?} 的权限", file_path),
_ => eprintln!("删除文件失败:{}", e),
},
}
}
e.kind()
kind()是std::io::Error类型特有的方法—— 只有当错误是「IO 相关错误」(比如文件操作、网络请求、权限问题等)时,才能调用e.kind()获取ErrorKind(错误类别枚举);其他类型的错误(比如解析错误、自定义错误)没有这个方法,直接调用会编译报错
- 文件操作:
fs::remove_file、File::open、OpenOptions::open、fs::read_to_end等; - 网络操作:
std::net::TcpStream::connect、udp::send_to等; - 标准输入输出:
std::io::stdin().read_line()、std::io::stdout().write_all()等; - 管道、进程通信等其他 IO 操作。
std::io::ErrorKind常用错误
ErrorKind 取值 |
含义 |
|---|---|
NotFound |
文件 / 路径不存在 |
PermissionDenied |
权限不足(读 / 写 / 删除权限不够) |
InvalidInput |
无效输入(如传入非法路径格式) |
BrokenPipe |
管道断开 |
ConnectionRefused |
网络连接被拒绝 |
IsADirectory |
操作对象是目录(如用 remove_file 删目录) |
NotADirectory |
操作对象不是目录(如用 remove_dir 删文件) |
AlreadyExists |
文件 / 目录已存在(如 create_new(true) 时) |
4.文件权限调整
需要
sudo的情况:文件属于其他用户(如root),或你没有修改权限,比如修改/etc/passwd(root 所有),必须用sudo cargo run运行程序,否则报PermissionDenied错误编译后也一样,
sudo ./target/debug/your_program
use std::fs;
use std::os::unix::fs::PermissionsExt; // 必须引入,否则没有 from_mode 方法
use std::io::ErrorKind;
fn main() {
let file_path = "data.txt";
// 1. 定义目标权限(八进制,0o 开头)
let new_perms = fs::Permissions::from_mode(0o644); // 0o644 = rw-r--r--
// 2. 应用权限到文件
match fs::set_permissions(file_path, new_perms) {
Ok(_) => println!("文件 {:?} 权限修改为 0o644 成功", file_path),
Err(e) => match e.kind() {
ErrorKind::NotFound => eprintln!("错误:文件 {:?} 不存在", file_path),
ErrorKind::PermissionDenied => eprintln!("错误:权限不足,需用 sudo 运行程序"),
_ => eprintln!("修改权限失败:{}", e),
},
}
}

浙公网安备 33010602011771号