Rust におけるファイルシステム操作

RustRustBeginner
オンラインで実践に進む

This tutorial is from open-source community. Access the source code

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

この実験では、Rust の std::fs モジュールを紹介します。このモジュールは、ファイルシステム操作に関する関数を提供します。この実験では、ディレクトリの作成、ファイルの作成、ファイルの内容の読み取り、シンボリック リンクの作成、ディレクトリの内容の一覧表示、ファイルの削除、およびディレクトリの削除など、さまざまなファイルシステム操作の例を示します。コード スニペットは、std::fs モジュールの関数を使用してこれらの操作を実行する方法を示しており、各操作の予想される出力も提供されています。また、エラー処理に ? 表記を使用した cat 関数の代替実装についても言及しています。

注: 実験でファイル名が指定されていない場合は、好きなファイル名を使用できます。たとえば、main.rs を使用して、rustc main.rs &&./main でコンパイルして実行できます。

ファイルシステム操作

std::fs モジュールには、ファイルシステムを扱ういくつかの関数が含まれています。

use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::os::unix;
use std::path::Path;

// `% cat path` の単純な実装
fn cat(path: &Path) -> io::Result<String> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// `% echo s > path` の単純な実装
fn echo(s: &str, path: &Path) -> io::Result<()> {
    let mut f = File::create(path)?;

    f.write_all(s.as_bytes())
}

// `% touch path` の単純な実装 (既存のファイルは無視)
fn touch(path: &Path) -> io::Result<()> {
    match OpenOptions::new().create(true).write(true).open(path) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn main() {
    println!("`mkdir a`");
    // ディレクトリを作成し、`io::Result<()>` を返す
    match fs::create_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(_) => {},
    }

    println!("`echo hello > a/b.txt`");
    // 前のマッチは `unwrap_or_else` メソッドを使って簡略化できます
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`mkdir -p a/c/d`");
    // 再帰的にディレクトリを作成し、`io::Result<()>` を返す
    fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`touch a/c/e.txt`");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`ln -s../b.txt a/c/b.txt`");
    // シンボリック リンクを作成し、`io::Result<()>` を返す
    if cfg!(target_family = "unix") {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
            println!("! {:?}", why.kind());
        });
    }

    println!("`cat a/c/b.txt`");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("`ls a`");
    // ディレクトリの内容を読み取り、`io::Result<Vec<Path>>` を返す
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("`rm a/c/e.txt`");
    // ファイルを削除し、`io::Result<()>` を返す
    fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`rmdir a/c/d`");
    // 空のディレクトリを削除し、`io::Result<()>` を返す
    fs::remove_dir("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });
}

期待される正常な出力は以下の通りです。

$ rustc fs.rs && ./fs
$(mkdir a)
$(echo hello > a/b.txt)
$(mkdir -p a/c/d)
$(touch a/c/e.txt)
$(ln -s../b.txt a/c/b.txt)
$(cat a/c/b.txt)
> hello
$(ls a)
> "a/b.txt"
> "a/c"
$(rm a/c/e.txt)
$(rmdir a/c/d)

そして、a ディレクトリの最終状態は以下の通りです。

$ tree a
a
|-- b.txt
`-- c
    `-- b.txt ->../b.txt

1 directory, 2 files

関数 cat を定義する別の方法は、? 表記を使うことです。

fn cat(path: &Path) -> io::Result<String> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

まとめ

おめでとうございます!あなたはファイルシステム操作の実験を完了しました。あなたのスキルを向上させるために、LabEx でさらに多くの実験を行って練習してください。