✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:Rust语言通关之路
景天的主页:景天科技苑
文章目录
- Rust获取命令行参数以及IO操作
- 1、接收命令行参数
- 1.1 读取参数值
- 1.2 将参数值保存进变量
- 2、Rust读取文件
- 2.1. 基本文件读取
- 2.1.1 一次性读取整个文件到字符串
- 2.1.2 一次性读取整个文件到字节向量
- 2.2. 逐行读取文件
- 2.2.1 使用 BufReader 逐行读取
- 2.2.2 使用 BufReader 读取字节块
- 2.3 使用内存映射文件 (memmap2)
- 3、Rust向文件中写内容
- 4、Rust序列化与反序列化
Rust获取命令行参数以及IO操作
Rust作为一门系统编程语言,提供了强大且安全的I/O操作支持。与C/C++不同,Rust通过所有权系统和丰富的类型系统,在编译期就能避免许多常见的I/O错误。Rust的标准库std::io模块包含了大多数I/O功能,而std::fs模块则专门处理文件系统操作。
1、接收命令行参数
1.1 读取参数值
Rust 标准库提供的std::env::args 能够获取传递给它的命令行参数的值
这个函数返回一个传递给程序的命令行参数的 迭代器(iterator)。
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
}
首先使用 use 语句来将 std::env 模块引入作用域以便可以使用它的 args 函数。
注意 std::env::args 函数被嵌套进了两层模块中。
当所需函数嵌套了多于一层模块时,通常将父模块引入作用域,而不是其自身。
这便于我们利用 std::env 中的其他函数。这比增加了 use std::env::args; 后仅仅使用 args 调用函数要更明确一些,因为 args 容易被错认成一个定义于当前模块的函数。
得到的第一个参数是生成的程序本身,后面都是命令行参数
在 main 函数的第一行,我们调用了 env::args,并立即使用 collect 来创建了一个包含迭代器所有值的 vector。
collect 可以被用来创建很多类型的集合,所以这里显式注明 args 的类型来指定我们需要一个字符串 vector。
虽然在 Rust 中我们很少会需要注明类型,然而 collect 是一个经常需要注明类型的函数,因为 Rust 不能推断出你想要什么类型的集合。
最后,我们使用调试格式 :? 打印出 vector。
注意 vector 的第一个值是 “target\debug\myargs.exe”,它是我们二进制文件的名称。这与 C 中的参数列表的行为相匹配,让程序使用在执行时调用它们的名称。
如果要在消息中打印它或者根据用于调用程序的命令行别名更改程序的行为,通常可以方便地访问程序名称,不过考虑到本章的目的,我们将忽略它并只保存所需的两个参数。
注意args 函数和无效的 Unicode
注意 std::env::args 在其任何参数包含无效 Unicode 字符时会 panic。
如果你需要接受包含无效 Unicode 字符的参数,使用 std::env::args_os 代替。这在某些操作系统中可能发生
这个函数返回 OsString 值而不是 String 值。
OsString 值每个平台都不一样而且比 String 值处理起来更为复杂。
use std::env;
fn main() {
for arg in env::args_os() {
println!("{:?}", arg);
}
}
1.2 将参数值保存进变量
打印出参数 vector 中的值展示了程序可以访问指定为命令行参数的值。现在需要将这两个参数的值保存进变量这样就可以在程序的余下部分使用这些值了。
use std::env;
fn main() {
//获取命令行参数
let args: Vec<String> = env::args().collect();
//获取查询字符串和文件名
let query = &args[1];
let filename = &args[2];
println!("Searching for {}", query);
println!("In file {}", filename);
}
正如之前打印出 vector 时所看到的,程序的名称占据了 vector 的第一个值 args[0],所以我们从索引 1 开始。
myargs2 获取的第一个参数是需要搜索的字符串,所以将其将第一个参数的引用存放在变量 query 中。第二个参数将是文件名,所以将第二个参数的引用放入变量 filename 中。
2、Rust读取文件
在 Rust 中读取文件是常见的 I/O 操作,标准库提供了多种方法来处理文件读取。下面我将详细介绍各种文件读取方法及其适用场景
2.1. 基本文件读取
2.1.1 一次性读取整个文件到字符串
use std::fs;
use std::io;
//读取文件内容
fn read_file(filename: &str) -> io::Result<String> {
// 使用?来处理错误
// ?会将错误传递给调用者
// ?只能在返回Result的函数中使用
// ?会将错误类型从io::Error转换为io::Result<String>
//fs::read_to_string返回Result<String, io::Error>
let content = fs::read_to_string(filename)?;
Ok(content)
}
fn main() {
let filename = "hello.txt";
match read_file(filename) {
Ok(content) => println!("File content: {}", content),
Err(e) => println!("Error: {}", e),
}
}
2.1.2 一次性读取整个文件到字节向量
用于读取二进制文件
//读取文件到字节向量
use std::fs;
use std::io;
//读取二进制文件内容
fn read_file(filename: &str) -> io::Result<Vec<u8>> {
// 使用?来处理错误
// ?会将错误传递给调用者
// ?只能在返回Result的函数中使用
// ?会将错误类型从io::Error转换为io::Result<Vec<u8>>
//fs::read返回Result<Vec<u8>, io::Error>
let content = fs::read(filename)?;
Ok(content)
}
fn main() {
let filename = "123.vip";
match read_file(filename) {
//打印文件的字节长度
Ok(content) => println!("File clength: {}", content.len()),
Err(e) => println!("Error: {}", e),
}
}
2.2. 逐行读取文件
对于大文件,逐行读取更高效且内存友好。
2.2.1 使用 BufReader 逐行读取
//读取大文件
use std::fs::File;
use std::io::{ BufRead, BufReader };
//读取大文件内容
fn read_file(filename: &str) -> std::io::Result<()> {
let file = File::open(filename)?;
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line?);
}
Ok(())
}
fn main() {
let filename = "hello.txt";
match read_file(filename) {
Ok(()) => println!("File content: {}", filename),
Err(e) => println!("Error: {}", e),
}
}
特点:
内存效率高
自动处理换行符
lines() 返回 Result<String>
,需要处理可能的错误
2.2.2 使用 BufReader 读取字节块
//使用 BufReader 读取字节块
use std::io::{ self, Read };
use std::fs::File;
//读取字节块
fn read_file(filename: &str) -> io::Result<()> {
let file = File::open(filename)?;
let mut reader = io::BufReader::new(file);
//定义一个字节缓冲区
//缓冲区的大小为1024字节
let mut buffer = [0; 10];
//循环读取字节块
//read方法会将字节块读取到缓冲区中
//read方法返回读取的字节数
//如果读取到文件末尾,就退出循环
loop {
let n = reader.read(&mut buffer)?;
if n == 0 {
break; //读取到文件末尾,退出循环
}
println!("Read {} bytes: {:?}", n, &buffer[..n]);
// 处理 buffer[..n]
}
Ok(())
}
fn main() {
match read_file("Cargo.toml") {
Ok(()) => println!("Read file successfully"),
Err(e) => println!("Error: {}", e),
}
}
特点:
适用于二进制文件
可以控制缓冲区大小
适合网络传输或处理大文件
2.3 使用内存映射文件 (memmap2)
对于超大文件,内存映射可以提高性能:
use std::fs::File;
use memmap2::Mmap;
fn main() -> std::io::Result<()> {
let file = File::open("Cargo.toml")?;
//使用memmap2映射文件到内存,需要使用unsafe,需要注意
let mmap = unsafe { Mmap::map(&file)? };
// 假设我们搜索某个字节模式
let pattern = b"\xDE\xAD\xBE\xEF";
if let Some(pos) = mmap.windows(pattern.len()).position(|w| w == pattern) {
println!("Pattern found at offset: {}", pos);
} else {
println!("Pattern not found");
}
Ok(())
}
注意:内存映射涉及 unsafe 代码,需要谨慎使用。
3、Rust向文件中写内容
//写入文件
use std::fs;
use std::io;
fn write_file(filename: &str, content: &str) -> io::Result<()> {
fs::write(filename, content)
}
fn main() {
let filename = "hello.txt";
let content = "你好,景天";
match write_file(filename, content) {
Ok(()) => println!("Write file successfully"),
Err(e) => println!("Error: {}", e),
}
}
4、Rust序列化与反序列化
使用 serde 库可以方便地进行 I/O 操作:
需要安装第三方库
cargo add serde
cargo add serde_json
并且在Cargo.toml中配置
//序列化和反序列化
use serde::{ Serialize, Deserialize };
use std::fs;
use serde_json;
#[derive(Serialize, Deserialize, Debug)]
#[allow(dead_code)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
fn main() {
let person = Person {
name: "John".to_string(),
age: 30,
phones: vec!["1234567890".to_string(), "0987654321".to_string()],
};
//序列化
let json = serde_json::to_string(&person).unwrap(); //将结构体序列化为json字符串
println!("json: {}", json);
//将序列化后的数据写入文件
fs::write("person.json", json).unwrap(); //将数据写入文件,json的所有权已经移动到write中了
//反序列化
// let person2: Person = serde_json::from_str(&json).unwrap(); //这里json所有权已不存在
// println!("person2: {:?}", person2);
//从文件中读取数据并反序列化
let json2 = fs::read_to_string("person.json").unwrap();
let person3: Person = serde_json::from_str(&json2).unwrap();
println!("person3: {:?}", person3);
}