以下代码_从文件夹中的 png 文件中删除该字符:
use std::fs;
use std::path::Path;
fn main() {
let dir = Path::new("/home/alex/Desktop");
for entry in fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() && path.extension().unwrap() == "png" {
let new_path = path.with_file_name(path.file_name().unwrap().to_str().unwrap().replace("_",""));
fs::rename(path, new_path).unwrap();
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,unwrap()它被大量使用。可以在这段代码中删除它们,并使用更干净的方法吗?
您在这里用于unwrap多种不同的用途。让我们把它们分解一下。
fs::read_dir(dir).unwrap()
Run Code Online (Sandbox Code Playgroud)
read_dir如果发生 IO 错误,可能会失败。这不是你能控制的事情,也不是你能处理的事情。使用出色的令人烦恼的异常类比,这个错误将是一个外生错误:不是你的错,也不是你可以阻止的。unwrap这里是有道理的。在较大的程序中,我们可能会让我们的函数返回io::Result<_>,并可以编写fs::read_dir(dir)?让调用者尝试从错误中恢复。但对于一个小型main程序来说,unwrap这里是有意义的。
let entry = entry.unwrap();
Run Code Online (Sandbox Code Playgroud)
一样。这是你无法控制的 IO 错误。在较大的程序中,您可以编写entry?将错误传播给调用者的代码,但在这种小规模的情况下,unwrap就可以了。
path.extension().unwrap()
Run Code Online (Sandbox Code Playgroud)
这就是事情变得有趣的地方。extension不会失败。它会None在文件没有扩展名的完全正常、合理的情况下返回。例如,如果文件名为Rakefile或.gitignore. 在这种情况下恐慌确实是不幸的。相反,我们只是希望该if语句失败。您的if声明现在所说的是“断言扩展存在,如果存在则执行某些操作png”。你真正想说的是“如果扩展存在并且是png”。无需断言。考虑
if let Some(extension) = path.extension() {
if extension == "png" {
...
}
}
Run Code Online (Sandbox Code Playgroud)
在 Rust 的未来版本中,可以if let与 一起编写&&,因此我们可以将其缩短为
if let Some(extension) = path.extension() && extension == "png" {
...
}
Run Code Online (Sandbox Code Playgroud)
但该功能目前不稳定。
unwrap继续,我现在正在跳过线路,打几个电话。我们稍后会回到这个话题。
fs::rename(path, new_path).unwrap();
Run Code Online (Sandbox Code Playgroud)
fs::rename是一个 IO 操作,可能会像任何 IO 操作一样失败。让它失败,或者在包含函数的情况下传播,就像前两个一样。
现在我们来谈谈最后一行。
path.with_file_name(path.file_name().unwrap().to_str().unwrap().replace("_",""));
Run Code Online (Sandbox Code Playgroud)
file_name()None如果没有文件名则返回。在这种情况下,我们甚至不应该尝试重命名该文件,因此这应该是我们在到达这里if let 之前检查的内容。
if let Some(filename) = path.file_name() {
...
}
Run Code Online (Sandbox Code Playgroud)
接下来,您将使用to_str. 您需要这样做的原因是文件名使用OsStr,它可能是也可能不是有效的 UTF-8。因此,如果您想对此类文件名感到恐慌,那也没关系。就我个人而言(考虑到这种情况是多么罕见和奇怪),我可能也会恐慌(或传播,类似于其他 IO 异常)。如果您想恢复,可以使用to_string_lossy,它将无效的 UTF-8 序列替换为U+FFFD。
如果要传播,可以转换Option为io::Resultwith ok_or_else。
最后,由于这里确实进行了大量 IO,因此我实际上建议继续将其分解为一个单独的函数,该函数会生成io::Result. 然后main可以对结果调用unwrap(或) 一次以指示任何 IO 错误,但其他调用者理论上可以处理或从这些相同的错误中恢复。expect
考虑到所有这些,我们将归结为一个expect调用main(统一)处理所有 IO 错误,如下所示。
use std::fs;
use std::io;
use std::path::Path;
fn replace_files(dir: &Path) -> io::Result<()> {
for entry in fs::read_dir(dir)? {
let path = entry?.path();
if let Some(extension) = path.extension() {
if let Some(filename) = path.file_name() {
if path.is_file() && extension == "png" {
let filename_utf8 =
filename.to_str()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Non-UTF-8 filename"))?;
let new_path = path.with_file_name(filename_utf8.replace("_",""));
fs::rename(path, new_path)?;
}
}
}
}
Ok(())
}
fn main() {
let dir = Path::new("/home/alex/Desktop");
replace_files(dir).expect("I/O error occurred!");
}
Run Code Online (Sandbox Code Playgroud)