學習Rust 檔案與 IO

安全劍客發表於2020-12-27
Rust 語言是一種高效、可靠的通用高階語言。其高效不僅限於開發效率,它的執行效率也是令人稱讚的,是一種少有的兼顧開發效率和執行效率的語言。

學習Rust 檔案與 IO學習Rust 檔案與 IO
本章介紹 Rust 語言的 I/O 操作。

接收 行引數

行程式是計算機程式最基礎的存在形式,幾乎所有的作業系統都支援命令列程式並將視覺化程式的執行基於命令列機制。
命令列程式必須能夠接收來自命令列環境的引數,這些引數往往在一條命令列的命令之後以空格符分隔。
在很多語言中(如 Java 和 C/C++)環境引數是以主函式的引數(常常是一個字串陣列)傳遞給程式的,但在 Rust 中主函式是個無參函式,環境引數需要開發者透過 std::env 模組取出,過程十分簡單:

例項

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

現在直接執行程式:

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

也許你得到的結果比這個要長的多,這很正常,這個結果中 Args 結構體中有一個 inner 陣列,只包含唯一的字串,代表了當前執行的程式所在的位置。
但這個資料結構令人難以理解,沒關係,我們可以簡單地遍歷它:

例項

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

執行結果:

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

一般引數們就是用來被遍歷的,不是嗎。

現在我們開啟許久未碰的 launch.json ,找到 "args": [],這裡可以設定執行時的引數,我們將它寫成 "args": ["first", "second"] ,然後儲存、再次執行剛才的程式,執行結果:

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

作為一個真正的命令列程式,我們從未真正使用過它,作為語言教程不在此敘述如何用命令列執行 Rust 程式。但如果你是個訓練有素的開發者,你應該可以找到可執行檔案的位置,你可以嘗試進入目錄並使用命令列命令來測試程式接收命令列環境引數。

命令列輸入

早期的章節詳細講述瞭如何使用命令列輸出,這是由於語言學習的需要,沒有輸出是無法除錯程式的。但從命令列獲取輸入的資訊對於一個命令列程式來說依然是相當重要的。
在 Rust 中,std::io 模組提供了標準輸入(可認為是命令列輸入)的相關功能:

例項

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 環境支援命令列輸入是一個非常繁瑣的事情,牽扯到跨平臺的問題和不可除錯的問題,所以我們直接在 VSCode 終端中執行程式。在命令列中執行:

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

std::io::Stdio 包含 read_line 讀取方法,可以讀取一行字串到緩衝區,返回值都是 Result 列舉類,用於傳遞讀取中出現的錯誤,所以常用 expect 或 unwrap 函式來處理錯誤。
注意:目前 Rust 標準庫還沒有提供直接從命令列讀取數字或格式化資料的方法,我們可以讀取一行字串並使用字串識別函式處理資料。

檔案讀取

我們在計算機的 D:\ 目錄下建立檔案 text.txt,內容如下:

This is a text file.

這是一個將文字檔案內容讀入字串的程式:

例項

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

執行結果:

This is a text file.

在 Rust 中讀取記憶體可容納的一整個檔案是一件極度簡單的事情,std::fs 模組中的 read_to_string 方法可以輕鬆完成文字檔案的讀取。
但如果要讀取的檔案是二進位制檔案,我們可以用 std::fs::read 函式讀取 u8 型別集合:

例項

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

執行結果:

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

以上兩種方式是一次性讀取,十分適合 Web 應用的開發。但是對於一些底層程式來說,傳統的按流讀取的方式依然是無法被取代的,因為更多情況下檔案的大小可能遠超記憶體容量。
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);
}

執行結果:

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

std::fs 模組中的 File 類是描述檔案的類,可以用於開啟檔案,再開啟檔案之後,我們可以使用 File 的 read 方法按流讀取檔案的下面一些位元組到緩衝區(緩衝區是一個 u8 陣列),讀取的位元組數等於緩衝區的長度。
注意:VSCode 目前還不具備自動新增標準庫引用的功能,所以有時出現"函式或方法不存在"一樣的錯誤有可能是標準庫引用的問題。我們可以檢視標準庫的註釋文件(滑鼠放到上面會出現)來手動新增標準庫。
std::fs::File 的 open 方法是"只讀"開啟檔案,並且沒有配套的 close 方法,因為 Rust 編譯器可以在檔案不再被使用時自動關閉檔案。

檔案寫入

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

例項

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

這和一次性讀取一樣簡單方便。執行程式之後, D:\text.txt 檔案的內容將會被重寫為 FROM RUST PROGRAM 。所以,一次性寫入請謹慎使用!因為它會直接刪除檔案內容(無論檔案多麼大)。如果檔案不存在就會建立檔案。
如果想使用流的方式寫入檔案內容,可以使用 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();
}

這段程式與上一個程式等價。

注意:開啟的檔案一定存放在可變的變數中才能使用 File 的方法!

File 類中不存在 append 靜態方法,但是我們可以使用 OpenOptions 來實現用特定方法開啟檔案:

例項

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(())
}

執行之後,D:\text.txt 檔案內容將變成:

FROM RUST PROGRAM APPEND WORD

OpenOptions 是一個靈活的開啟檔案的方法,它可以設定開啟許可權,除append 許可權以外還有 read 許可權和 write 許可權,如果我們想以讀寫許可權開啟一個檔案可以這樣寫:

例項

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(())
}

執行之後,D:\text.txt 檔案內容將變成:

COVERRUST PROGRAM APPEND WORD


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2745755/,如需轉載,請註明出處,否則將追究法律責任。

相關文章