章节目录

文件、路径、字节与编码

本章目标

这一章学习 Rust 如何读写文件、处理路径、理解字节和文本编码。你会知道为什么 Web 服务不能随便把 URL 拼成文件路径,以及 UTF-8 对中文内容为什么重要。

它是什么

文件是操作系统管理的持久化数据。路径是定位文件的位置。字节是最底层的数据单位。字符串则是在字节之上按编码解释出来的文本。

Rust 标准库常用类型:

  • std::fs:读写文件和目录。
  • std::path::Path:借用路径。
  • std::path::PathBuf:拥有路径。
  • Vec<u8>:字节数组。
  • String:UTF-8 文本。

为什么需要

当前项目有两类文件:

  • data/posts 保存用户发布的文章。
  • docs 保存教材 Markdown。

它们都在磁盘上,但安全策略不同。文章通过数字 id 映射文件,文档通过静态章节清单映射文件。这样做是为了避免路径穿越,例如 /docs/../../Cargo.toml 不应该能读取项目配置。

怎么使用

读取文本文件:

let markdown = std::fs::read_to_string("docs/00-preface.md")?;

写入文本文件:

std::fs::write("data/example.txt", "hello")?;

构造路径:

use std::path::Path;

let path = Path::new("docs").join("00-preface.md");

逐行解释

fn chapter_path(slug: &str) -> std::path::PathBuf {
    std::path::Path::new("docs").join(format!("{slug}.md"))
}
  • fn chapter_path 定义一个把章节 slug 转成路径的函数。
  • slug: &str 借用章节标识。
  • -> PathBuf 返回拥有所有权的路径缓冲区。
  • Path::new("docs") 创建一个借用路径,表示 docs 目录。
  • .join(...) 拼接子路径,会使用当前操作系统的路径分隔规则。
  • format!("{slug}.md") 生成文件名。

在真实项目里,slug 必须来自可信清单或经过严格校验,不能直接使用用户输入。

常见坑

  • 不要用字符串拼接处理跨平台路径,优先用 PathPathBuf
  • read_to_string 要求文件是有效 UTF-8。
  • 用户输入不能直接参与文件路径,必须白名单或严格校验。
  • 文本长度和字节长度不同,中文字符通常占多个字节。

练习

  1. fs::write 写入一个 UTF-8 中文文件。
  2. read_to_string 读回来并打印。
  3. Path::new("docs").join("00-preface.md") 构造路径。
  4. 尝试读取不存在文件,处理 Result

造轮子任务

写一个安全文件读取器 read_registered_file(name: &str)。它只允许读取内置数组中登记过的文件名。传入未知文件名时返回 404 风格错误。这个任务和文档站章节白名单是同一个思想。

小结

文件系统 API 看似简单,但路径安全和编码细节非常重要。Rust 给了你 PathResultStringVec<u8> 等明确类型,帮助你把文本、字节、路径和错误边界分清楚。