Rust 文件與 IO

本章介紹 Rust 語言的 I/O 操作。

接收命令行參數(shù)

命令行程序是計(jì)算機(jī)程序最基礎(chǔ)的存在形式,幾乎所有的操作系統(tǒng)都支持命令行程序并將可視化程序的運(yùn)行基于命令行機(jī)制。

命令行程序必須能夠接收來自命令行環(huán)境的參數(shù),這些參數(shù)往往在一條命令行的命令之后以空格符分隔。

在很多語言中(如 Java 和 C/C++)環(huán)境參數(shù)是以主函數(shù)的參數(shù)(常常是一個(gè)字符串?dāng)?shù)組)傳遞給程序的,但在 Rust 中主函數(shù)是個(gè)無參函數(shù),環(huán)境參數(shù)需要開發(fā)者通過 std::env 模塊取出,過程十分簡單:

fn main() {
    let args = std::env::args();
    println!("{:?}", args);
}

現(xiàn)在直接運(yùn)行程序:

Args { inner: ["D:\\rust\\greeting\\target\\debug\\greeting.exe"] }

也許你得到的結(jié)果比這個(gè)要長的多,這很正常,這個(gè)結(jié)果中 Args 結(jié)構(gòu)體中有一個(gè) inner 數(shù)組,只包含唯一的字符串,代表了當(dāng)前運(yùn)行的程序所在的位置。

但這個(gè)數(shù)據(jù)結(jié)構(gòu)令人難以理解,沒關(guān)系,我們可以簡單地遍歷它:

fn main() {
    let args = std::env::args();
    for arg in args {
        println!("{}", arg);
    }
}

運(yùn)行結(jié)果:

D:\rust\greeting\target\debug\greeting.exe

一般參數(shù)們就是用來被遍歷的,不是嗎。

現(xiàn)在我們打開許久未碰的 launch.json ,找到 "args": [],這里可以設(shè)置運(yùn)行時(shí)的參數(shù),我們將它寫成 "args": ["first", "second"] ,然后保存、再次運(yùn)行剛才的程序,運(yùn)行結(jié)果:

D:\rust\greeting\target\debug\greeting.exe
first
second

作為一個(gè)真正的命令行程序,我們從未真正使用過它,作為語言教程不在此敘述如何用命令行運(yùn)行 Rust 程序。但如果你是個(gè)訓(xùn)練有素的開發(fā)者,你應(yīng)該可以找到可執(zhí)行文件的位置,你可以嘗試進(jìn)入目錄并使用命令行命令來測試程序接收命令行環(huán)境參數(shù)。

命令行輸入

早期的章節(jié)詳細(xì)講述了如何使用命令行輸出,這是由于語言學(xué)習(xí)的需要,沒有輸出是無法調(diào)試程序的。但從命令行獲取輸入的信息對于一個(gè)命令行程序來說依然是相當(dāng)重要的。

在 Rust 中,std::io 模塊提供了標(biāo)準(zhǔn)輸入(可認(rèn)為是命令行輸入)的相關(guān)功能:

use std::io::stdin;

fn main() {
let mut str_buf = String::new();

    stdin().read_line(&mut str_buf)
        .expect("Failed to read line.");

    println!("Your input line is \n{}", str_buf);
}

令 VSCode 環(huán)境支持命令行輸入是一個(gè)非常繁瑣的事情,牽扯到跨平臺的問題和不可調(diào)試的問題,所以我們直接在 VSCode 終端中運(yùn)行程序。在命令行中運(yùn)行:

D:\rust\greeting> cd ./target/debug
D:\rust\greeting\target\debug> ./greeting.exe
nhooo
Your input line is 
nhooo

std::io::Stdio 包含 read_line 讀取方法,可以讀取一行字符串到緩沖區(qū),返回值都是 Result 枚舉類,用于傳遞讀取中出現(xiàn)的錯誤,所以常用 expect 或 unwrap 函數(shù)來處理錯誤。

注意:目前 Rust 標(biāo)準(zhǔn)庫還沒有提供直接從命令行讀取數(shù)字或格式化數(shù)據(jù)的方法,我們可以讀取一行字符串并使用字符串識別函數(shù)處理數(shù)據(jù)。

文件讀取

我們在計(jì)算機(jī)的 D:\ 目錄下建立文件 text.txt,內(nèi)容如下:

This is a text file.

這是一個(gè)將文本文件內(nèi)容讀入字符串的程序:

use std::fs;

fn main() {
    let text = fs::read_to_string("D:\\text.txt").unwrap();
    println!("{}", text);
}

運(yùn)行結(jié)果:

This is a text file.

在 Rust 中讀取內(nèi)存可容納的一整個(gè)文件是一件極度簡單的事情,std::fs 模塊中的 read_to_string 方法可以輕松完成文本文件的讀取。

但如果要讀取的文件是二進(jìn)制文件,我們可以用 std::fs::read 函數(shù)讀取 u8 類型集合:

use std::fs;

fn main() {
    let content = fs::read("D:\\text.txt").unwrap();
    println!("{:?}", content);
}

運(yùn)行結(jié)果:

[84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 120, 116, 32, 102, 105, 108, 101, 46]

以上兩種方式是一次性讀取,十分適合 Web 應(yīng)用的開發(fā)。但是對于一些底層程序來說,傳統(tǒng)的按流讀取的方式依然是無法被取代的,因?yàn)楦嗲闆r下文件的大小可能遠(yuǎn)超內(nèi)存容量。

Rust 中的文件流讀取方式:

use std::io::prelude::*;
use std::fs;

fn main() {
    let mut buffer = [0u8; 5];
    let mut file = fs::File::open("D:\\text.txt").unwrap();
    file.read(&mut buffer).unwrap();
    println!("{:?}", buffer);
    file.read(&mut buffer).unwrap();
    println!("{:?}", buffer);
}

運(yùn)行結(jié)果:

[84, 104, 105, 115, 32] 
[105, 115, 32, 97, 32]

std::fs 模塊中的 File 類是描述文件的類,可以用于打開文件,再打開文件之后,我們可以使用 File 的 read 方法按流讀取文件的下面一些字節(jié)到緩沖區(qū)(緩沖區(qū)是一個(gè) u8 數(shù)組),讀取的字節(jié)數(shù)等于緩沖區(qū)的長度。

注意:VSCode 目前還不具備自動添加標(biāo)準(zhǔn)庫引用的功能,所以有時(shí)出現(xiàn)"函數(shù)或方法不存在"一樣的錯誤有可能是標(biāo)準(zhǔn)庫引用的問題。我們可以查看標(biāo)準(zhǔn)庫的注釋文檔(鼠標(biāo)放到上面會出現(xiàn))來手動添加標(biāo)準(zhǔn)庫。

std::fs::File 的 open 方法是"只讀"打開文件,并且沒有配套的 close 方法,因?yàn)?Rust 編譯器可以在文件不再被使用時(shí)自動關(guān)閉文件。

文件寫入

文件寫入分為一次性寫入和流式寫入。流式寫入需要打開文件,打開方式有"新建"(create)和"追加"(append)兩種。

一次性寫入:

use std::fs;

fn main() {
    fs::write("D:\\text.txt", "FROM RUST PROGRAM")
        .unwrap();
}

這和一次性讀取一樣簡單方便。執(zhí)行程序之后, D:\text.txt 文件的內(nèi)容將會被重寫為 FROM RUST PROGRAM 。所以,一次性寫入請謹(jǐn)慎使用!因?yàn)樗鼤苯觿h除文件內(nèi)容(無論文件多么大)。如果文件不存在就會創(chuàng)建文件。

如果想使用流的方式寫入文件內(nèi)容,可以使用 std::fs::File 的 create 方法:

use std::io::prelude::*;
use std::fs::File;

fn main() {
    let mut file = File::create("D:\\text.txt").unwrap();
    file.write(b"FROM RUST PROGRAM").unwrap();
}

這段程序與上一個(gè)程序等價(jià)。

注意:打開的文件一定存放在可變的變量中才能使用 File 的方法!

File 類中不存在 append 靜態(tài)方法,但是我們可以使用 OpenOptions 來實(shí)現(xiàn)用特定方法打開文件:

use std::io::prelude::*;
use std::fs::OpenOptions;

fn main() -> std::io::Result<()> {
    
    let mut file = OpenOptions::new()
            .append(true).open("D:\\text.txt")?;

    file.write(b" APPEND WORD")?;

    Ok(())
}

運(yùn)行之后,D:\text.txt 文件內(nèi)容將變成:

FROM RUST PROGRAM APPEND WORD

OpenOptions 是一個(gè)靈活的打開文件的方法,它可以設(shè)置打開權(quán)限,除append 權(quán)限以外還有 read 權(quán)限和 write 權(quán)限,如果我們想以讀寫權(quán)限打開一個(gè)文件可以這樣寫:

use std::io::prelude::*;
use std::fs::OpenOptions;

fn main() -> std::io::Result<()> {
    
    let mut file = OpenOptions::new()
            .read(true).write(true).open("D:\\text.txt")?;

    file.write(b"COVER")?;

    Ok(())
}

運(yùn)行之后,D:\text.txt 文件內(nèi)容將變成:

COVERRUST PROGRAM APPEND WORD
丰满人妻一级特黄a大片,午夜无码免费福利一级,欧美亚洲精品在线,国产婷婷成人久久Av免费高清