隱私權政策

搜尋此網誌

技術提供:Blogger.

關於我自己

我的相片
目前從事軟體相關行業,喜歡閱讀、健身、喝調酒。習慣把遇到的問題記下來,每天做一些整理方便自己以後查。 Python、Rust、Kotlin等程式語言皆為自學,目前比較著重在Rust語言,歡迎一起討論。

2023年12月29日 星期五

Rust File概述2 - 撰寫讀取檔案


File2 - 撰寫讀取檔案

前一篇文章[1]有介紹過File常見的一些功能

這篇會介紹讀寫的方法

讀寫都是一個trait

常見寫有兩種,write和write_all

讀則是有三種,read、read_to_end、read_to_string


write[2]

fn write(&mut self, buf: &[u8]) -> Result<usize>

write為將緩衝區buf寫入資料

回傳result,內容為寫入資料的byte值

例如

use std::{fs::File, io::Write};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let buf_write = String::from("1234567890");
    let write_byte = file.write(buf_write.as_bytes()).unwrap();
    println!("{}", write_byte);
}
10

就是將1234567890寫入example.txt

並且回傳寫入的byte值

也就是10


要注意write會嘗試將整個緩衝區寫入資料

但有時候只會寫入部分、甚至寫入失敗


write_all[3]

fn write_all(&mut self, buf: &[u8]) -> Result<()>

write_all會將整個緩衝區寫進資料裡

此方法會持續呼叫write直到緩衝區沒資料

因此不會發生緩衝區寫到一半的情況

use std::{fs::File, io::Write};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let buf_write = String::from("1234567890");
    file.write_all(buf_write.as_bytes()).unwrap();
}

最後檔案內會長這樣


read[4]

fn read(&mut self, buf: &mut [u8]) -> Result<usize>

read可以讀取文件內的東西

輸入一個緩衝區,用來存取檔案內的東西

假設檔案內容為1234567890

則緩衝區大小至少要10以上,才可以完整存取

而輸出則是ASCII碼

use std::{fs::File, io::Read};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let mut buf_read = [0; 10];
    file.read(&mut buf_read).unwrap();
    println!("{:?}", buf_read);
}
[49, 50, 51, 52, 53, 54, 55, 56, 57, 48]

如果buf_read大於10則會出現

    let mut buf_read = [0; 20];
    file.read(&mut buf_read).unwrap();
    println!("{:?}", buf_read);
[49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

小於10則出現只出現字串長度的數字

    let mut buf_read = [0; 2];
    file.read(&mut buf_read).unwrap();
    println!("{:?}", buf_read);
[49, 50]

因此使用read的時候要注意buf的長度


read_to_end[5]

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize>

read_to_end為此時游標位置到文件末端

游標位置的觀念要看Seek[6]

輸入為一個數組,輸出一樣是ASCII碼

use std::{fs::File, io::Read};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let mut buf_read = Vec::new();
    file.read_to_end(&mut buf_read).unwrap();
    println!("{:?}", buf_read);
}
[49, 50, 51, 52, 53, 54, 55, 56, 57, 48]

那現在假設buf_read裡面本來就有數據

read_to_end會繼續往後加數據

use std::{fs::File, io::Read};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let mut buf_read = vec![0];
    file.read_to_end(&mut buf_read).unwrap();
    println!("{:?}", buf_read);
}
[0, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48]


read_to_string[6]

fn read_to_string(&mut self, buf: &mut String) -> Result<usize>

read_to_string為將資料讀成string

輸出則直接是字串

而不是像上面一樣是ASCII

use std::{fs::File, io::Read};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let mut buf_read = String::new();
    file.read_to_string(&mut buf_read).unwrap();
    println!("{:?}", buf_read);
}
1234567890

如果說這次example.txt改成有換行的


則輸出會長這樣

1234567890\r\n9876543210

會增加換行符號


延伸問題

use std::{
    fs::File,
    io::{Read, Write},
};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let buf_write = String::from("1234567890");
    file.write(buf_write.as_bytes()).unwrap();
    let mut buf_read = String::new();
    file.read_to_string(&mut buf_read).unwrap();
    println!("{}", buf_read);
}

我用File::options打開一個read-write模式的檔案

我先把1234567890寫入檔案後

用read_to_string讀取

此時結果為


甚麼東西都沒讀取到




Ans:

那是因為我write寫完後

游標會在檔案最後面

此時讀取不會有東西

必須把游標往前移動才可以

use std::{
    fs::File,
    io::{Read, Write, SeekFrom, Seek},
};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let buf_write = String::from("1234567890");
    file.write(buf_write.as_bytes()).unwrap();
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut buf_read = String::new();
    file.read_to_string(&mut buf_read).unwrap();
    println!("{}", buf_read);
}
1234567890


延伸閱讀

Rust File概述1 - 存取檔案 ~ LaGee-Blog (lageeblog.blogspot.com)

Rust Seek概述 - 移動檔案內游標位置 ~ LaGee-Blog (lageeblog.blogspot.com)




參考資料

[1] https://lageeblog.blogspot.com/2023/12/Rust-File1.html

[2] https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write

[3] https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all

[4] https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read

[5] https://doc.rust-lang.org/std/io/trait.Read.html#method.read_to_end

[6] https://lageeblog.blogspot.com/2023/12/rust-seek.html

[7] https://doc.rust-lang.org/std/io/trait.Read.html#method.read_to_string


2023年12月27日 星期三

Rust File概述1 - 存取檔案


Struct std::fs::File[1]

File是一種開啟檔案後結構

可以修改或查看檔案詳細內容等等

此篇文章主要講述File常用到的method

另一篇[2]則會講述如何讀寫File


這篇中有很多地方會跟OpenOption類似

也可以看看這篇[3]


open[4]

pub fn open<P: AsRef<Path>>(path: P) -> Result<File>

凡事都有一個起點

open大概就是File的起點

open只能開啟一個唯讀的檔案

也就是開起來後不能編輯,只能讀取

open輸入一個路徑,輸出Result

Result裡面是File 所以要unwrap()

如果檔案不存在會發生Err

其他會發生Err的可以跟OpenOption的open[5]比較


假設有個檔案example.txt內容為abcdefghijklmnopqrstuvwxyz


可以用open打開後

以read_to_string讀取

use std::{fs::File, io::Read};

fn main() {
    let file_path = "example.txt";
    let mut file = File::open(file_path).unwrap();
    let mut buf = String::new();
    file.read_to_string(&mut buf).unwrap();
    println!("{}", buf);
}
abcdefghijklmnopqrstuvwxyz


create[6]

pub fn create<P: AsRef<Path>>(path: P) -> Result<File>

open是打開現有文件

create則是創建文件

如果檔案不存在會創建文件

如果檔案存在則是會截斷,也就是把現有資料去除

create打開的文件只能寫不能讀

回傳Result<File>,因此也要unwrap()


use std::{fs::File, io::{Read, Write}};

fn main() {
    let file_path = "example.txt";
    let mut file = File::create(file_path).unwrap();
    let buf = String::from("123456789");
    file.write_all(buf.as_bytes()).unwrap();
}

透過create創建文件

以write_all把資料寫進文件

最後呈現這樣結果


options[7]

pub fn options() -> OpenOptions

假設一個文件想要讀取又想要撰寫怎麼辦

可以使用options輔助

使用後會輸出OpenOptions

就可以照著這篇文章[3]使用

例如

use std::{
    fs::File,
    io::{Read, Write},
};

fn main() {
    let file_path = "example.txt";
    let mut file = File::options()
        .write(true)
        .read(true)
        .open(file_path)
        .unwrap();
    let buf_write = String::from("1234567890");
    file.write(buf_write.as_bytes()).unwrap();
}

同時開啟write和read

下面一樣可以寫入而不是只能讀取


set_len[8]

pub fn set_len(&self, size: u64) -> Result<()>

輸入一個數字

選擇要截斷的長度

例如我輸入10

use std::fs::File;

fn main() {
    let file_path = "example.txt";
    let file = File::options().write(true).open(file_path).unwrap();
    file.set_len(10).unwrap();
}

會把10以後的數字的取消

只出現abcdefghij


要注意如果write沒有設定成true則會Error

但假設我用create開啟一個只能寫的檔案

使用create的同時他會自動把裡面內容清空

因此只能以option另外打開write權限


metadata[9]

pub fn metadata(&self) -> Result<Metadata>

metadata會輸出一個Metadata[10]

Metadata也是一個結構

可以判斷檔案是否為資料夾、是否為檔案、最後修改時間等等

use std::fs::File;

fn main() {
    let file_path = "example.txt";
    let file = File::open(file_path).unwrap();
    println!("{:#?}", file.metadata().unwrap());
}
Metadata { file_type: FileType( FileType { attributes: 32, reparse_tag: 0, }, ), is_dir: false, is_file: true, permissions: Permissions( FilePermissions { attrs: 32, }, ), modified: Ok( SystemTime { intervals: 133481583167447378, }, ), accessed: Ok( SystemTime { intervals: 133481583168297370, }, ), created: Ok( SystemTime { intervals: 133480766508570384, }, ), .. }


延伸閱讀

Rust File概述2 - 撰寫讀取檔案 ~ LaGee-Blog (lageeblog.blogspot.com)

Rust OpenOptions概述 - 文件打開的權限 ~ LaGee-Blog (lageeblog.blogspot.com)

參考資料

[1] https://doc.rust-lang.org/std/fs/struct.File.html#

[2] https://lageeblog.blogspot.com/2023/12/Rust-File2.html

[3] https://lageeblog.blogspot.com/2023/12/rust-openoptions.html

[4] https://doc.rust-lang.org/std/fs/struct.File.html#method.open

[5] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open

[6] https://doc.rust-lang.org/std/fs/struct.File.html#method.create

[7] https://doc.rust-lang.org/std/fs/struct.File.html#method.options

[8] https://doc.rust-lang.org/std/fs/struct.File.html#method.set_len

[9] https://doc.rust-lang.org/std/fs/struct.File.html#method.metadata

[10] https://doc.rust-lang.org/std/fs/struct.Metadata.html


2023年12月26日 星期二

Rust OpenOptions概述 - 文件打開的權限


Struct std::fs::OpenOptions[1]

OpenOptions主要是可以修改文件打開的權限

例如讀取(read)、撰寫(write)、創建(create)等等

沒有開啟權限會無法做相對應的事

本文介紹new、open和六種權限

權限分別是

讀取(read)

撰寫(write)

追加(append)

創建(create)

創建新的(create_new)

截斷(truncate)

new[2]

pub fn new() -> Self

new為創建新的空白選項

所有預設值為false


read[3]

設定檔案內容可以被讀取

可以以read_to_string等方式讀取檔案內容

假設有一個example.txt,內容為abcdefghijklmnopqrstuvwxyz


程式例子

use std::{fs::OpenOptions, io::Read};

fn main() {
    let file_path = "example.txt";
    let mut file = OpenOptions::new().read(true).open(file_path).unwrap();
    let mut buf = String::new();
    file.read_to_string(&mut buf).unwrap();
    println!("buf: {}", buf);
}
buf: abcdefghijklmnopqrstuvwxyz


write[4]

設定檔案可以被寫入

use std::{fs::OpenOptions, io::Write};

fn main() {
    let file_path = "example.txt";
    let mut file = OpenOptions::new().write(true).open(file_path).unwrap();
    let buf = String::from("1234567890");
    file.write_all(buf.as_bytes()).unwrap();
}

要小心write_all為直接覆蓋

最後會長



append[5]

append為在內容後方新增資料

當.write(true).append(true)和只有append(true)效果是一樣的

use std::{fs::OpenOptions, io::Write};

fn main() {
    let file_path = "example.txt";
    let mut file = OpenOptions::new().append(true).open(file_path).unwrap();
    let buf = String::from("1234567890");
    file.write_all(buf.as_bytes()).unwrap();
}



create[6]

當檔案不存在時create可以創建新的檔案

當檔案存在時會自動開啟

使用create要和write或append也必須要啟用

use std::fs::OpenOptions;

fn main() {
    let file_path = "example.txt";
    let file = OpenOptions::new()
        .write(true)
        .create(true)
        .open(file_path)
        .unwrap();
}


create_new[7]

創建一個新的文件

如果該文件已經存在會fail

一樣write或append必須啟用才可以使用create_new

而create_new啟用時,create和truncate會被忽略

use std::fs::OpenOptions;

fn main() {
    let file_path = "example.txt";
    let file = OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(file_path)
        .unwrap();
}


truncate[8]

truncate為截斷

如果文件已存在且裡面有內容

會將長度截斷成0

意思就是會把裡面內容全部刪掉

因為會修改到裡面內容

write也必須是啟用的

use std::fs::OpenOptions;

fn main() {
    let file_path = "example.txt";
    let file = OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(file_path)
        .unwrap();
}



open[9]

pub fn open<P: AsRef<Path>>(&self, path: P) -> Result<File>

最後則是open

open為輸入檔案路徑

輸出Result,裡面內容為File

File的設定跟上述的六個有關

而有幾種狀況會Err


參考資料

[1] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#

[2] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.new

[3] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read

[4] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write

[5] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append

[6] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create

[7] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new

[8] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate

[9] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open

2023年12月25日 星期一

Rust Seek概述 - 移動檔案內游標位置


Trait std::io::Seek [1]

Seek是一個Trait

主要作用可以使檔案中的游標移動


讀取例子

假設我有一個文件example.txt

裡面內容是abcdefghijklmnopqrstuvwxyz



希望從第六個開始讀

也就是讀取fghijklmnopqrstuvwxyz

可以這樣做

use std::{
    fs::OpenOptions,
    io::{Read, Seek, SeekFrom},
};

fn main() {
    let mut file = OpenOptions::new()
        .read(true)
        .open("example.txt")
        .unwrap();

    file.seek(SeekFrom::Start(5)).unwrap();
    let mut buf = String::new();
    file.read_to_string(&mut buf).unwrap();
    println!("{:?}", buf);
}
"fghijklmnopqrstuvwxyz"

先使用OpenOptions[2]開啟檔案

OpenOptions為主要為設定開啟檔案的方式

因為這次需要讀取檔案

要把read設定為true

再用open輸入路徑,最後unwrap


之後就可以控制游標了

file.seek(SeekFrom::Start(5))表示游標從第六個開始

SeekFrom[3]有三種枚舉

分別是Start, End, Current

pub enum SeekFrom {
    Start(u64),
    End(i64),
    Current(i64),
}

SeekFrom::Start

從0開始設定游標位置


SeekFrom::End

從最後開始設定游標位置

use std::{
    fs::OpenOptions,
    io::{Read, Seek, SeekFrom},
};

fn main() {
    let mut file = OpenOptions::new()
        .read(true)
        .open("example.txt")
        .unwrap();
    file.seek(SeekFrom::End(-5)).unwrap();
    let mut buf = String::new();
    file.read_to_string(&mut buf).unwrap();
    println!("{:?}", buf);
}
"vwxyz"

使用-5這樣表示從最後面數來第五個開始


SeekFrom::Current

將游標從現在位置移到幾個數字後的位置

例如

use std::{
    fs::OpenOptions,
    io::{Read, Seek, SeekFrom},
};

fn main() {
    let mut file = OpenOptions::new()
        .read(true)
        .open("example.txt")
        .unwrap();

    file.seek(SeekFrom::Start(0)).unwrap();
    file.seek(SeekFrom::Current(5)).unwrap();
    let mut buf = String::new();
    file.read_to_string(&mut buf).unwrap();
    println!("{:?}", buf);
}
"fghijklmnopqrstuvwxyz"

設定初始為0,要移到第五個位置

可以使用Current(5)表示移到五個後的位置

另外Current也可以是負的

use std::{
    fs::OpenOptions,
    io::{Read, Seek, SeekFrom},
};

fn main() {
    let mut file = OpenOptions::new()
        .read(true)
        .open("example.txt")
        .unwrap();
    file.seek(SeekFrom::Start(10)).unwrap();
    file.seek(SeekFrom::Current(-5)).unwrap();
    let mut buf = String::new();
    file.read_to_string(&mut buf).unwrap();
    println!("{:?}", buf);
}
"fghijklmnopqrstuvwxyz"

負的表示現在位置往前多少


撰寫例子

use std::{
    fs::OpenOptions,
    io::{Seek, SeekFrom, Write},
};

fn main() {
    let mut file = OpenOptions::new()
        .write(true)
        .open("example.txt")
        .unwrap();
    file.seek(SeekFrom::Start(5)).unwrap();
    let buf = String::from("1234567890");
    file.write_all(buf.as_bytes()).unwrap();
}

一樣把游標先移到第五個

write_all會把原本文件的文字覆蓋

也就是文件會變成這個樣子




參考資料

[1] https://doc.rust-lang.org/std/io/trait.Seek.html

[2] https://doc.rust-lang.org/std/fs/struct.OpenOptions.html

[3] https://doc.rust-lang.org/std/io/enum.SeekFrom.html