rand随机
rand = "0.8.3"
use rand::Rng;
// 每个线程都有一个初始化的生成器
let mut rng = rand::thread_rng();
// 整数范围是该类型的范围
let n1: u8 = rng.gen();
let n2: u16 = rng.gen();
// 浮点数的范围是0到1,但不包括1
let f1: f64 = rng.gen();
生成一个范围内的随机数
let f2_rnd: f64 = rng.gen_range(0.0..10.0);
let i2_rnd: i32 = rng.gen_range(0..10);
指定分布
// 整数
Uniform::from(1..7);
// 浮点数
Uniform::from(1.0..7.0);
// 更复杂的类型用 rand_distr
use rand_distr::{Normal, Binomial, Gamma};
// mean 2, standard deviation 3
let mut normal = Normal::new(2.0, 3.0).unwrap();
let val = normal.sample(&mut rand::thread_rng());
案例:随机生成密码
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789)(*&^%$#@!~";
const PASSWORD_LEN: usize = 30;
let mut rng = rand::thread_rng();
let password: String = (0..PASSWORD_LEN)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect();
sort
vec.sort 可以用于 int, &str
但不能用于 float
let mut vec = vec![1, 5, 10, 2, 15];
let mut vec = vec!["aa", "ab", "b"];
vec.sort();
print!("{:?}", vec);
下面这种写法很通用
let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0];
vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
案例:对 vec<struct>
排序
struct Person {
name: String,
age: u32,
}
impl Person {
fn new(name: String, age: u32) -> Self {
Person { name, age }
}
}
fn main() {
let mut people = vec![
Person::new("Zoe".to_string(), 25),
Person::new("Al".to_string(), 60),
Person::new("John".to_string(), 1),
];
people.sort_by(|a, b| b.age.cmp(&a.age));
}
命令行工具 clap
// clap = "*"
use clap::{Arg, App};
fn main() {
let matches = App::new("app名称")
.version("0.0.1")
.author("guofei9987 <me@guofei.site>")
.about("关于,这个app是干什么的")
.arg(Arg::with_name("file")
.short('f')
.long("file")
.takes_value(true)
.help("A cool file"))
.arg(Arg::with_name("num")
.short('n')
.long("number")
.takes_value(true)
.help("Five less than your favorite number"))
.get_matches();
let myfile = matches.value_of("file").unwrap_or("input.txt");
println!("The file passed is: {}", myfile);
let num_str = matches.value_of("num");
match num_str {
None => println!("No idea what your favorite number is."),
Some(s) => {
match s.parse::<i32>() {
Ok(n) => println!("Your favorite number must be {}.", n + 5),
Err(_) => println!("That's not a number! {}", s),
}
}
}
}
使用
# 正经使用
cargo run -- -f myfile.txt -n 251
# -V 和 --version 显示app名称+版本
cargo run -- -V
# -h, --help 显示详细的帮助信息
cargo run -- -h
ANSI:彩色命令行
// ansi_term = "*"
use ansi_term::Colour;
use ansi_term::Style;
fn main() {
println!("This is {}, {}, {} and {}. This is {}.",
Colour::Red.paint("red"),
Colour::Blue.paint("blue"),
Colour::Green.paint("green"),
Colour::Yellow.paint("yellow"),
Style::new().bold().paint("bold"),
);
}
time
时间差
let start = Instant::now();
thread::sleep(Duration::from_secs(1));
let duration = start.elapsed();
// duration 可以转化为很多种数字格式
// duration.as_secs_f32(); // 返回 f32,
now
// std::time::Duration 重复了,所以做个别名
use chrono::Duration as Duration2;
let now = Utc::now();
now.checked_add_signed(Duration2::days(-1))
// 还可以用checked_sub_signed
// Duration2:: 可以是:
// days, weeks,
// hours, minutes, milliseconds, microseconds
时区(略)
生成时间对象
let now = Utc::now();
let date_time = chrono::NaiveDate::from_ymd(2017, 11, 12).and_hms(17, 33, 44);
时间对象格式转字符串
let now = Utc::now();
// 标准格式时间
let time_str = format!("{}-{:02}-{:02} {:02}:{:02}:{:02}",
now.year(), now.month(), now.day(),
now.hour(), now.minute(), now.second());
// 其它格式时间
let (is_common_era, year) = now.year_ce();
println!(
"The current UTC date is {}-{:02}-{:02} {:?} ({})",
year,
now.month(),
now.day(),
now.weekday(),
if is_common_era { "CE" } else { "BCE" }
);
let (is_pm, hour) = now.hour12();
println!(
"The current UTC time is {:02}:{:02}:{:02} {}",
hour,
now.minute(),
now.second(),
if is_pm { "PM" } else { "AM" }
);
时间对象转 timestamp
now.timestamp()
字符串转日期
let no_timezone = chrono::NaiveDateTime::parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S")
.unwrap();
sleep
use std::thread;
use std::time::Duration
thread::sleep(Duration::from_secs(2));
正则
[dependencies]
regex = "*"
// 编译正则
let re = Regex::new("\\b[\\w\\-\\+\\.]+@[\\w\\-\\+\\.]+\\.\\w{2,18}\\b").unwrap();
let text = "aaa me@guofei.site guofei9987@foxmail.com";
// 是否有匹配,
let res: bool = re.is_match(text);
// true
// 单匹配
let mut res = Vec::new();
match re.captures(text) {
// 这里面的序号0代表整体,序号1代表分组1
Some(i) => { res.push(String::from(&i[0])) }
None => {}
}
// 多匹配
let mut res = Vec::new();
for cap in re.captures_iter(text) {
res.push(String::from(&cap[0]));
}
// 匹配并替换
let text_new = re.replace_all(text, "你");
关于分组运算
// 编译正则,定义老公两个组,分别命名为 user_name 和 domain
let re = Regex::new("\\b(?P<name>[\\w\\-\\+\\.]+)@[\\w\\-\\+\\.]+\\.(?P<domain>\\w{2,18})\\b").unwrap();
let text = "aaa me@guofei.site guofei9987@foxmail.com";
let text2 = "aaaaaaa";
// 单匹配:上面用 index 方便一些,如果想用name:
// re.captures(text) 是一个 `<Option<Captures<"user_name":Option>>>` 这种嵌套
let mut res: Vec<String> = Vec::new();
match re.captures(text) {
Some(i) => {
res.push(String::from(i.name("user_name").unwrap().as_str()));
}
None => {}
}
// 还可以这样,但是匹配不到的时候直接 Panic 了
let res: &str = cap.unwrap().name("user_name").unwrap().as_str();
// 多匹配
let mut res = Vec::new();
for cap in re.captures_iter(&text) {
match cap.name("user_name") {
None => {}
Some(i) => { res.push(String::from(i.as_str())) }
}
}
// 替换
let text_new = re.replace_all(text, "$user_name/$domain");
正则语法
. # 除新行以外的任何字符(包括带有s标志的新行)
\d # 数字 (\p{Nd})
\D # 非数字
\pN # 单字母名称Unicode字符类
\PN # Negated(非) 单字母名称Unicode字符类
\p{Greek} # Unicode字符类(常规类别或脚本)
\P{Greek} # negated(非) Unicode字符类(常规类别或脚本)
[xyz] # 匹配x、y或z(并集)的字符类。
[^xyz] # 匹配 除x、y和z以外的 任何字符的字符类。
[a-z] # 匹配A-z范围内任何字符的字符类。
[[:alpha:]] # ASCII 字符类 ([A-Za-z])
[[:^alpha:]] # Negated(非) ASCII 字符类 ([^A-Za-z])
[x[^xyz]] # ???嵌套/分组字符类(匹配除y和z以外的任何字符)
[a-y&&xyz] # 交集(匹配x或y)
[0-9&&[^4]] # 使用交集和求反的减法(匹配0-9,4除外)
[0-9--4] # 直接减法 (匹配0-9,4除外)
[a-g~~b-h] # 对称差异(范围异或)(仅匹配“a”和“h”)
[\[\]] # 字符类中的转义(匹配[ 或 ])
[[:alnum:]] # alphanumeric ([0-9A-Za-z])
[[:alpha:]] # alphabetic ([A-Za-z])
[[:ascii:]] # ASCII ([\x00-\x7F])
[[:blank:]] # blank ([\t ])
[[:cntrl:]] # control ([\x00-\x1F\x7F])
[[:digit:]] # digits ([0-9])
[[:graph:]] # graphical ([!-~])
[[:lower:]] # lower case ([a-z])
[[:print:]] # printable ([ -~]) 可打印
[[:punct:]] # punctuation ([!-/:-@\[-`{-~]) 标点符号,不包含汉字标点
[[:space:]] # whitespace ([\t\n\v\f\r ]) 空白
[[:upper:]] # upper case ([A-Z])
[[:word:]] # word characters ([0-9A-Za-z_])
[[:xdigit:]] # hex digit ([0-9A-Fa-f]) 十六进制数字
参考:https://blog.csdn.net/wsp_1138886114/article/details/116519242 (rust的正则和python的正则有些区别,功能更多)
IO
include_str&include_bytes
include_str!
、include_bytes!
它在编译时将文件的内容作为字符串直接嵌入到代码中。
- 优点
- 无需运行时文件IO,从而减少开销
- 简化部署: 文件就在编译后的文件中了。1)只需分发单个可执行文件。 2)无需担心路径和位置问题。 3)从而规避文件不存在、文件权限问题带来的 bug
- 提高应用安全性:文件内容是嵌入在代码中的,不能被随意访问、修改
- 缺点
- 如果文件很大:会增加编译时间、增加内存消耗、增加程序启动时间
返回的类型是 &str
, &[u8; ?]
std::fs 读取和写入
参考:
https://llever.com/rust-cookbook-zh/file/read-write.zh.html
https://blog.csdn.net/feiyanaffection/article/details/125575331?spm=1001.2014.3001.5502
读文件
let content = "hello, world!";
let filename = "output.txt";
// 最简易的方法
// 读
let data_bytes: Vec<u8> = fs::read(filename)
.map_err(|e| e.to_string())?;
// 写
fs::write(filename2, data_bytes)
.map_err(|e| e.to_string())?;
// 如果是文本文件,还可以直接读到 String
let data_string: String = fs::read_to_string("file.txt")
.map_err(|e| e.to_string())?;
// 使用 buffer。好处:可以预先分配内存,性能可能更高些。
let mut file = fs::File::open(filename)
.map_err(|e| e.to_string())?;
let mut buf = vec![0; 100]; // 一次读入的数量,不会比它大
// read_num: 这次读入的字节数
let read_num = file.read(&mut buf).
map_err(|e| e.to_string())?;
// 使用 buffer 读到 String
let mut file = fs::File::open(filename)
.map_err(|e| e.to_string())?;
let mut buf = String::new();
file.read_to_string(&mut buf)
.map_err(|e| e.to_string())?;
// 逐行读取
let mut file = std::fs::File::open("new_file.txt").unwrap();
let read = BufReader::new(file);
for line in read.lines() {
let line = line.unwrap();
println!("{}", line);
}
// 追加写
let mut file = std::fs::OpenOptions::new()
.write(true) // 写入
.create(true)// 或者 .create_new(true) :如果文件存在则报错
.append(true) // true 是追加写,false 是覆盖写
.open(filename).unwrap();
let n = file.write(content.as_bytes()).unwrap(); // 实际写入的字节数
目录
use std::{env, fs};
fn main() {
let current_dir = env::current_dir().unwrap();
println!(
"Entries modified in the last 24 hours in {:?}:",
current_dir
);
for entry in fs::read_dir(current_dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
let metadata = fs::metadata(&path).unwrap();
let last_modified = metadata.modified().unwrap().elapsed().unwrap().as_secs();
if last_modified < 24 * 3600 && metadata.is_file() {
println!(
"Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
last_modified,
metadata.permissions().readonly(),
metadata.len(),
path.file_name().ok_or("No filename").unwrap()
);
}
}
}
调用 cmd
use std::process::{Command, Output};
fn main() {
let res = Command::new("ls").arg("-l").output();
match res {
Ok(output) => {
println!("{}", output.status.success());
println!("{}", String::from_utf8(output.stdout).unwrap());
}
Err(_) => { println!("command 出错"); }
}
}
更多:https://llever.com/rust-cookbook-zh/os/external.zh.html
zip
下面的代码:
- 先读取文件到字节流
- 然后用 zip 读取字节流,若干处理后生成一个新的字节流,
- 然后把字节流保存到文件
let filename = "./files/file.zip";
let filename_new = "./files/file_new.zip";
// 读取文件到字节流
let data_bytes = fs::read(filename).unwrap();
// 从字节流读取 zip 文件
let cursor = Cursor::new(data_bytes);
let mut archive = zip::ZipArchive::new(cursor).unwrap();
// 新文件
let mut data_bytes_new: Vec<u8> = Vec::new();
let mut cursor_new = Cursor::new(&mut data_bytes_new);
let mut zip_writer = zip::ZipWriter::new(&mut cursor_new);
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let filename: String = file.name().to_string();
let options = zip::write::FileOptions::default()
.compression_method(file.compression()) // 还可以是别的值,例如 zip::CompressionMethod::Stored
.unix_permissions(file.unix_mode().unwrap_or(0o755));
if file.is_file() {
// 读原始文件
let mut buffer: Vec<u8> = Vec::new();
file.read_to_end(&mut buffer).unwrap();
// 新建一个文件
zip_writer.start_file(filename, options).unwrap();
// 写入
zip_writer.write_all(&buffer).unwrap();
} else if file.is_dir() {
// 创建目录
zip_writer.add_directory(filename, options).unwrap();
}
}
zip_writer.finish().unwrap();
drop(zip_writer); // 释放 zip_writer,从而释放 cursor_new 的借用状态
// 重置 cursor_new 以获取完整的 ZIP 字节流
cursor_new.seek(SeekFrom::Start(0)).unwrap();
// 得到字节流
let data_bytes_new = cursor_new.into_inner();
// 写入
fs::write(filename_new, data_bytes_new).unwrap();
注
- unix(包括Linux、macOS、IOS、Android) 的文件系统使用的是正斜线,windows 使用反斜线
- 一般情况下,为了保证跨平台兼容性,都是使用正斜线。但偶尔有人不遵守这个约定
let filename: String = file.mangled_name().as_path().display().to_string();
会根据当前操作系统来调整这个斜线- 而
let filename: String = file.name().to_string();
不会调整这个斜线(原始 zip 文件中是什么,这里就是什么)
下面的代码:读取一个 zip 文件,然后遍历它,把每个文件的内容都放到新的 zip 文件中
// zip = "*"
let filename = "files/file.xlsx";
let filename_new = "files/file_new.xlsx";
// 读取
let zip_file = fs::File::open(filename).unwrap();
let mut archive = zip::ZipArchive::new(zip_file).unwrap();
// 写入
let zip_file_new = fs::OpenOptions::new()
.create(true).write(true).truncate(true)
.open(filename_new).unwrap();
let mut zip_writer = zip::ZipWriter::new(zip_file_new);
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let filename = file.mangled_name();
let options = zip::write::FileOptions::default()
.compression_method(zip::CompressionMethod::Stored)
.unix_permissions(file.unix_mode().unwrap_or(0o755));
if file.is_file() {
// 读
let mut buffer: Vec<u8> = Vec::new();
file.read_to_end(&mut buffer).unwrap();
// 新建一个文件
zip_writer.start_file(filename.as_path().display().to_string(), options).unwrap();
// 写入
zip_writer.write_all(&buffer).unwrap();
} else if file.is_dir() {
// 创建目录
zip_writer.add_directory(filename.as_path().display().to_string(), options).unwrap();
}
}
// 结束并关闭
zip_writer.finish().unwrap();
csv(待补充)
https://llever.com/rust-cookbook-zh/encoding/csv.zh.html
Json
说明
- 可以把 struct 保存为 json
- 配合
include_str!
可以编译时把文件内容放入内存
如何把一个 struct 保存为 json
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: usize,
}
let p1 = Person {
name: "张三".to_string(),
age: 10,
};
// 序列化
let serialized:String = serde_json::to_string(&p1).unwrap();
// 反序列化
let p_read: Person = serde_json::from_str(&serialized).unwrap();
https://llever.com/rust-cookbook-zh/encoding/complex.zh.html
tar(待补充)
https://rust-lang-nursery.github.io/rust-cookbook/compression/tar.html
科学计算
常量
矩阵
use ndarray::Array;
fn main() {
let mut a = Array::from_vec(vec![1., 2., 3., 4., 5.]);
let mut b = Array::from_vec(vec![5., 4., 3., 2., 1.]);
// let z = a + b;// 最好不要这样写, a 被 borrow 了
// 改元素值
a[1] = 10.;
// 相加
let w = &a + &b;
println!("w = {}", w);
println!("w.sum() = {}", w.sum());
// println!()
// a.append()
// a.push();
// a.push_row();
// a.push_column();
//
// a.map();
println!("a = {}", a);
for elem in a.iter() {
println!("{}", elem);
}
}
多维矩阵
use ndarray::arr2;
fn main() {
let a = arr2(&[[1, 2, 3],
[4, 5, 6]]);
let b = arr2(&[[6, 5, 4],
[3, 2, 1]]);
println!("矩阵加: {}", &a + &b);
println!("矩阵加标量: {}", &a + 3);
let a = arr2(&[[1, 2, 3],
[4, 5, 6]]);
let b = arr2(&[[6, 3],
[5, 2],
[4, 1]]);
println!("矩阵积: {}", a.dot(&b));
println!("标量积: {}", 5 * &a);
}
三角函数
let angle: f64 = 3.14 / 6.0; //弧度
let sin_angle: f64 = angle.sin();
println!("sin: {}, acos = {}, atan = {}", sin_angle, angle.acos(), angle.atan());
// 转度数/弧度
// angle.to_radians()
// angle.to_degrees()
复数
// num = "*"
let complex_integer = num::complex::Complex::new(10, 20);
let complex_float = num::complex::Complex::new(10.1, 20.1);
println!("Complex integer: {}", complex_integer);
println!("Complex float: {}", complex_float);
jieba
https://github.com/messense/jieba-rs
jieba-rs = "*"
使用
let jieba = Jieba::new();
let sentence = "来到了南京市长江大桥";
// 分词
let words: Vec<&str> = jieba.cut(sentence, false);
assert_eq!(words, vec!["我们", "中", "出", "了", "一个", "叛徒"]);
// 搜索引擎方式分词
let words: Vec<&str> = jieba.cut_for_search(sentence, false);
// 词+词性
let tags = jieba.tag(sentence, false);
for tag in tags {
println!("{},{}", tag.word, tag.tag);
}
// 词+索引
let tokens: Vec<Token> = jieba.tokenize(sentence, TokenizeMode::Default, false);
// TokenizeMode::Search
添加自定义词典
// 方式1: 读取 .txt 格式的文件作为词典
// dict.txt 内容:
// 创新办 100 nt
// 云计算 100 n
let dict_filename = "dict.txt";
let dict = fs::read_to_string(dict_filename).unwrap();
jieba.load_dict(&mut io::Cursor::new(dict.as_bytes())).unwrap();
// 方式2:在代码里面指定
let dict = "创新办 100 nt\n云计算 100 n";
jieba.load_dict(&mut io::Cursor::new(dict.as_bytes())).unwrap();
// 方式3: 使用 add_word
jieba.add_word("创新办", Some(100), Some("nr"));
jieba.add_word("云计算", Some(100), Some("n"));
// 使用:
let words: Vec<&str> = jieba.cut("李小福是创新办主任也是云计算方面的专家", true);
硬件相关
cpu 数量
// num_cpus = "*"
use num_cpus;
fn main() {
println!("Number of logical cores is {}", num_cpus::get());
}
测试性能
[dependencies]
bench = "*"
lib.rs
#![feature(test)]
pub mod benchmarks;
benchmarks.rs
#![feature(test)]
extern crate test;
use test::Bencher;
#[bench]
fn bench_pcre2_is_match(b: &mut Bencher) {
let pcre_re: pcre2::bytes::Regex = pcre2::bytes::Regex::new(r#"he.{0,2}o"#).unwrap();
b.iter(|| pcre2_is_match(&pcre_re));
}
额外
// 如何匹配 Option
match opt {
Some(i)=>{},
None=>{}
}
// 如何匹配 Result
match res {
Ok{t}=>{}
Err(e)=>{}
}
改一个数字
fn myfun(a: &mut i32){
*a+=1;
}
fn main() {
let mut a=1;
myfun(&mut a);
println!("{}",a); // 为 2
}
密码学(待补充)
https://rust-lang-nursery.github.io/rust-cookbook/cryptography/encryption.html
ring::pbkdf2
SQLite 和 Postgres(待补充)
https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html
调试/打log(待补充)
https://rust-lang-nursery.github.io/rust-cookbook/development_tools.html
网页编程、爬虫(待补充)
https://llever.com/rust-cookbook-zh/web/mime.zh.html
socket
https://llever.com/rust-cookbook-zh/net/server.zh.html
serde:序列化工具
// serde = { version = "1.0", features = ["derive"] }
// serde_json="*"
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 1, y: 2 };
// 序列化后的结果 {"x":1,"y":2}
let serialized: String = serde_json::to_string(&point).unwrap();
// 反序列化
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
参考资料
- https://llever.com/rust-cookbook-zh/
- rust的12个杀手级库 https://zhuanlan.zhihu.com/p/426329188