rust输入输出

2023-09-13 23:50:07

一、获取命令行参数

很多语言获取命令行参数,是通过主函数的参数获得的。
但Rust主函数是个无参数函数,命令行参数只能通过std::env::args()函数获得。
std::env::args()返回一个迭代器,其中包含了程序名和后面所有参数。

实例

fn main() {
    let args = std::env::args();
    for arg in args {
        println!("{}", arg);
    }
}
运行结果:
/home/yt/src/foo/target/debug/foo

可以在launch.json中的"args": []设置命令行参数。
我们将它改成"args": [“first”, “second”] ,然后保存、再次运行刚才的程序,运行结果:

/home/yt/src/foo/target/debug/foo
first
second

二、输入输出

Rust标准库通过两个特质组织输入输出
Read特质包含了许多方法用于读取数据
Write特质包含了许多方法用于写入数据

Read特质

fn read(&mut self, buf: &mut [u8]) -> Result<usize>
读取一些字节到指定的缓冲区中,返回读取的字节数
fn read_to_string(&mut self, buf: &mut String) -> Result<usize>
读取所有字节,直到EOF为止,然后将它们追加到 buf

Write特质

fn write(&mut self, buf: &[u8]) -> Result<usize>
write()  方法把参数  buf写入输出流。返回Result枚举,如果成功则返回写入的字节数。
fn write_fmt(&mut self, args: Arguments<'_>) -> Result<()>
将格式化的字符串写入输出流,返回遇到的任何错误

(一)标准输入
std::io::stdin()函数返回一个std::io::Stdin的实例,这是一个结构体,代表标准输入流。
结构体Stdin虽然实现了Read特性,但还实现了一个read_line方法,我们常用这个方法,而不是Read特性的方法。

pub fn read_line(&self, buf: &mut String) -> Result<usize>
read_line方法读取一行字符串(包括换行符)追加到buf,返回值是读取的字节数。

实例

use std::io;
fn main() {
     let mut str_buf = String::new();
     io::stdin().read_line(&mut str_buf).expect("Failed to read line.");
     println!("Your input line is \n{}", str_buf);
}

实例

fn main(){
     let mut line = String::new();
     println!("请输入你的名字:");
     let b1 = std::io::stdin().read_line(&mut line).unwrap();
     println!("你好 , {}", line);
     println!("读取的字节数为:{}", b1);
}
输出结果如下
请输入你的名字:
简单教程
你好 , 简单教程
读取的字节数为:13

注意:
目前Rust标准库还没有读取数字或格式化数据的方法,我们只能读取字符串,然后自己把字符串转换成数字,通常是使用parse把字符串转成数字。参考rust类型转换

实例

一行只有一个数
let mut buf = String::new();
io::stdin().read_line(&mut buf).unwrap();
let num1: i32 = buf.trim().parse().unwrap();

一行有多个数
let mut buf = String::new();
io::stdin().read_line(&mut buf).unwrap();
let mut nums = buf.split_whitespace();
let num1: f64 = nums.next().unwrap().parse().unwrap();
let num2: f64 = nums.next().unwrap().parse().unwrap();
let num3: f64 = nums.next().unwrap().parse().unwrap();

读入数组
let mut buff = String::new();
io::stdin().read_line(&mut buff).unwrap();
let ns: Vec<i32> = buff.split_whitespace().map(|x| x.parse().unwrap()).collect();
for v in ns {
     print!("{} ", v);
}

(二)标准输出
std::io::stdout()会返回一个std::io::Stdout的实例,这是个结构体,表示标准输出流。
结构体Stdout实现了Write特质

实例

use std::io::Write;
fn main() {
     let b1 = std::io::stdout().write("简单教程 ".as_bytes()).unwrap();
     let b2 = std::io::stdout().write(String::from("www.twle.cn").as_bytes()).unwrap();
     std::io::stdout().write(format!("\n写入的字节数为:{}\n",(b1+b2)).as_bytes()).unwrap();
}
输出结果如下
简单教程www.twle.cn
写入的字节数为:24

write_fmt方法通常与format_args!()搭配使用。应优先使用write!()宏

std::io::stdout().write_fmt(format_args!("{:.*}", 2, 1.234567)).unwrap();
write!(std::io::stdout(), "{:.*}", 2, 1.234567).unwrap();

(三)格式化输出
std::fmt 模块定义了一系列宏,包括:
format! 从格式化文本创建字符串。
print! 与format! 类似,但将文本输出到标准输出。
println! 与print! 类似,但输出结果追加一个换行符。
eprint! 与print! 类似,但将文本输出到标准错误。
eprintln! 与eprint! 类似,但输出结果追加一个换行符。
write! 第一个参数是 &mut io::Write或 &mut fmt::Write,后面参数与format!一样
writeln! 与write!相同,但追加了一个换行符

以println!为例
第一个参数是格式字符串,它必须是字符串字面量
后面是参数列表,依次对应格式字符串中的"占位符",这一点与C语言中的printf函数很相似。但是,Rust的占位符是{}。

占位符的完整格式为

'{' [ argument] [ ':' [[fill]align][sign]['#']['0'][width]['.' precision]type ]'}'

1.argument
分两种
(1)位置参数
除了依次替换占位符之外,还能在占位符中指定位置,表示让此位置的参数去替换占位符,例如 {1},表示用第二个参数替换该占位符(索引从0开始)

println!("{}{}", 1, 2); // =>"12"
println!("{1}{0}", 1, 2); // =>"21"

一旦开始将两种类型的位置说明符混合在一起,事情就会变得有些棘手

println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2"

(2)命名参数
还可以为参数指定名称,带名称的参数必须放在不带名称参数的后面

println!("{argument}", argument = "test"); // => "test"
println!("{name} {}", 1, name = 2); // => "2 1"
println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"

如果命名参数没有出现在参数列表中,println! 将引用当前作用域中的同名变量

let argument = 2 + 2;
println!("{argument}"); // => "4"

2.总宽度width
总宽度表示输出的总长度,如果实际长度不够,则要填充和对齐
有两种方法指定宽度

N: N 是整数。
N$:N是位置参数或命名参数

实例

println!("Hello {:5}!", "x");     //Hello x    !
println!("Hello {:1$}!", "x", 5);     //Hello x    !
println!("Hello {1:0$}!", 5, "x");     //Hello x    !
println!("Hello {:width$}!", "x", width = 5);     //Hello x    !
let width = 5;
println!("Hello {:width$}!", "x");     //Hello x    !

3.精度.precision
如果是整数,则忽略
如果是小数,精度表示小数点后面位数。默认是6
如果是字符串,精度表示字符数。如果实际大于精度,后面的字符会截掉
有三种可能的方法来指定精度:

.N:N是整数。
.N$:N是位置参数或命名参数
.*:.* 表示第一个参数表示精度,第二个参数才是要输出的值

实例

let v = 3.1415926;
println!("{:.2}", v);     // 保留小数点后两位 => 3.14
println!("{:.0}", v);     // 不带小数 => 3
println!("{:.1$}", v, 4);     // 通过位置参数来设定精度 => 3.1416,相当于{:.4}
println!("{:.prec$}", v, prec=4);     // 通过命名参数来设定精度 => 3.1416,相当于{:.4}
let s = "hi你好";
println!("{:.3}", s);     // 保留字符串前三个字符 => hi你
println!("Hello {:.*}", 3, "abcdefg");     // {:.*}接收两个参数,第一个是精度,第二个是要输出的值 => Hello abc

4.填充fill
参数位数不够时,要填充。默认填充空格
实例

println!("Hello {:*>5}!", "x");     //Hello ****x!
println!("Hello {:.<5}!", "x");     //Hello x....!
println!("Hello {:0<5}!", "x");     //Hello x0000!

5.对齐align
字符串默认默认左对齐,数字是右对齐

< 左对齐
^ 居中对齐
> 右对齐
println!("Hello {:<5}!", "x");     //Hello x    !
println!("Hello {:^5}!", "x");     //Hello   x  !
println!("Hello {:>5}!", "x");     //Hello     x!

6.Sign/#/0
sign可以是+或者-
+表示数字应打印符号。默认情况下从不打印正号,仅打印负号。 使用+之后,就会打印正号。
-当前未使用
#单独使用无效,必须与type一起使用。见type
0表示整数使用0填充,并且是符号感知的。像 {:08} 这样的格式将为整数 1 产生 00000001,而相同格式将为整数 -1 产生 -0000001。 请注意,负版本的零比正版本的少零。 请注意,填充零总是放在符号之后和数字之前。当与 # 标志一起使用时,将应用类似的规则:在前缀之后但在数字之前插入填充零。 前缀包括在总宽度中。这个0是放在对齐之后的,与对齐之前的0是不同的。

实例

println!("Hello {:0<5}!", 5); //Hello 50000!
println!("Hello {:<05}!", 5); //Hello 00005!
println!("Hello {:0<5}!", -5); // 负号也要占用一位宽度 => Hello -5000!
println!("Hello {:<05}!", -5); // 负号也要占用一位宽度 => Hello -0005!
println!("Hello {:+}!", 5);     // 显式的输出正号 => Hello +5!
println!("{:#x}!", 27);     //0x1b!
println!("{:#010x}!", 27);     //0x0000001b!

7.类型type
nothing ⇒ Display,以Display 格式打印
? ⇒ Debug,以Debug 格式打印
x? ⇒ Debug ,以Debug 格式打印,整数以小写十六进制
X? ⇒ Debug ,以Debug 格式打印,整数以大写十六进制
o ⇒ Octal
x ⇒ LowerHex
X ⇒ UpperHex
p ⇒ Pointer
b ⇒ Binary
e ⇒ LowerExp
E ⇒ UpperExp

Display 格式打印
println!("{}", 27); // 十进制 => 27
println!("{:b}", 27); // 二进制 => 11011
println!("{:o}", 27); // 八进制 => 33
println!("{:x}", 27); // 小写十六进制 => 1b
println!("{:X}", 27); // 大写十六进制 => 1B

当前面有#时,添加前缀
println!("{:#}", 27); // 十进制 => 27
println!("{:#b}", 27); // 二进制 => 0b11011
println!("{:#o}", 27); // 八进制 => 0o33
println!("{:#x}", 27); // 小写十六进制 => 0x1b
println!("{:#X}", 27); // 大写十六进制 => 0x1B

以以Debug 格式打印
let v= vec![1, 2, 3, 10, 11, 12];
println!("{:?}", v);      //[1, 2, 3, 10, 11, 12]
println!("{:x?}", v);      //[1, 2, 3, a, b, c]
println!("{:X?}", v);      //[1, 2, 3, A, B, C]

当前面有#时,添加换行和缩进
let v= vec![1, 2, 3, 10, 11, 12];
println!("{:#?}", v);
println!("{:#x?}", v); 
println!("{:#X?}", v); 
打印结果如下
[
    1,
    2,
    3,
    10,
    11,
    12,
]
[
    0x1,
    0x2,
    0x3,
    0xa,
    0xb,
    0xc,
]
[
    0x1,
    0x2,
    0x3,
    0xA,
    0xB,
    0xC,
]

以科学计数法
println!("{:2e}", 1000000000); // => 1e9
println!("{:2E}", 1000000000); // => 1E9

以指针
let v= vec![1, 2, 3];
println!("{:p}", v.as_ptr()); // => 0x600002324050

8.转义
有时需要输出 {和},但这两个字符是特殊字符,需要转义:

{{ 转义为 {
}} 转义为 }
\" 转义为 "
println!(" Hello \"{{World}}\" ");     // => Hello "{World}"

9.Debug特性
要想以Debug格式打印,类型必须要实现了Debug特性才行。大多数Rust类型都实现了Debug特性,但是对于自定义类型,需要自己实现Debug特性。有一种实现Debug特性的简单方法,就是使用derive属性

#[derive(Debug)]
struct Person {
     name: String,
     age: u8
}
fn main() {
     let i = 3.1415926;
     let s = String::from("hello");
     let v = vec![1, 2, 3];
     let p = Person{name: "sunface".to_string(), age: 18};
     println!("{:?}, {:?}, {:?}, {:?}", i, s, v, p);
}

10.Display特性
要想以Display 格式打印,类型必须要实现了Display特性才行。实现了Display特征的Rust类型并不多,往往需要我们自己实现。为类型实现Display特性不像Debug一样能使用derive属性。

为自定义类型实现Display特征

struct Person {
     name: String,
     age: u8,
}
use std::fmt;
impl fmt::Display for Person {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
          write!(
              f,
              "大佬在上,请受我一拜,小弟姓名{},年芳{},家里无田又无车,生活苦哈哈",
              self.name, self.age
          )
     }
}
fn main() {
     let p = Person {
          name: "xx".to_string(),
          age: 18,
     };
     println!("{}", p);
}

为外部类型实现Display特性
由于孤儿原则,无法直接为外部类型实现外部特性,但是可以使用newtype解决此问题。

struct Array(Vec<i32>);
use std::fmt;
impl fmt::Display for Array {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
          write!(f, "数组是:{:?}", self.0)
     }
}
fn main() {
     let arr = Array(vec![1, 2, 3]);
     println!("{}", arr);
}

Array就是我们的newtype,它封装了Vec,为Array实现Display特性,相当于间接为Vec实现了Display。

三、文件读写

Rust文件读写

更多推荐

Linux下安装和使用MySQL的详细教程

✅作者简介:2022年博客新星第八。热爱国学的Java后端开发者,修心和技术同步精进。🍎个人主页:JavaFans的博客🍊个人信条:不迁怒,不贰过。小知识,大智慧。💞当前专栏:MySQL数据库学习之旅✨特色专栏:国学周更-心性养成之路🥭本文内容:Linux下安装和使用MySQL的详细教程文章目录Linux下My

Git的ssh方式如何配置,如何通过ssh方式拉取和提交代码

git的ssh配置HTTPS和SSH的区别设置SSH方式配置单个仓库配置账户公钥大家通过git拉取代码的时候,一般都是通过http的方式,简单方便。但是细心的童鞋肯定也注意到Git也是支持ssh方式的。可能很多人也试过使用这个方式,但是好像没有那么简单。那么什么是SSH呢?为啥要使用SSH方式呢?HTTPS和SSH的区

C++ 多线程

多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下两种类型的多任务处理:基于进程和基于线程:基于进程的多任务处理是程序的并发执行。基于线程的多任务处理是同一程序的片段的并发执行。多线程程序包含可以同时运行的两个或多个部分,这样的程序中的每个部分称为一个线程,每个线程定义了一个

c++多态

目录多态的概念多态实现计算器案例c++如何实现动态绑定纯虚函数和抽象类纯虚函数和多继承虚析构函数虚析构函数作用纯虚析构函数重载重定义重写多态的概念多态:一种接口,多种形态静态多态:如果函数的调用,在编译阶段就可以确定函数的调用地址,并产生代码,就是静态多态(编译时多态)动态多态:调用地址不能编译不能在编译期间确定,而需

电脑摄像头录像软件推荐,总有一款适合你!

“有没有好用的电脑摄像头录像软件推荐呀,最近因为工作原因,需要用到电脑摄像头录像,但是因为不会操作,导致进度一直跟不上,想问问大家,帮忙推荐一款好用的电脑摄像头录像软件!”电脑摄像头是我们在日常工作和娱乐中不可或缺的工具,它可以用于视频通话、拍摄照片和录制视频等多种用途。然而,很多人对于如何使用电脑摄像头进行录像并不是

【HTTP】Cookie 和 Session 详解

Cookie和Session一.Cookie1.什么是Cookie2.Cookie的作用3.Cookie的组成4.Cookie的组织形式5.Cookie的传输6.如何提高Cookie的安全性7.Cookie类二.Session1.理解会话机制(Session)2.Sessoin的组织形式3.HttpSession类三.

单例模式-饿汉模式、懒汉模式

单例模式,是设计模式的一种。在计算机这个圈子中,大佬们针对一些典型的场景,给出了一些典型的解决方案。目录单例模式饿汉模式懒汉模式线程安全单例模式单例模式又可以理解为是单个实例(对象)在有些场景中,有特定的类,只能创建出一个实例,不应该创建多个实例。使用了单例模式以后,此时想要创建多个实例就变得很困难~Java中的单例模

算法通过村第八关-树(深度优先)青铜笔记|经典算法题目

文章目录前言1.二叉树里面的双指针1.1判断两棵树是否相同1.2对称二叉树1.3合并二叉树2.路径专题2.1二叉树的所有路径2.2路径总和3.翻转的妙用总结前言提示:人类的底里是悲伤,我们都在用厚重的颜料,覆盖那些粗糙的线稿。--张皓宸《抬头看二十九次月亮》前面的练习才是开始,这理才是真正的进入算法的门槛,来迎接下一波

ELK 企业级日志分析系统

----------------------ELK概述----------------------------------------1、ELK简介ELK平台是一套完整的日志集中处理解决方案,将ElasticSearch、Logstash和Kiabana三个开源工具配合使用,完成更强大的用户对日志的查询、排序、统计需求

[刷题记录]牛客面试笔刷TOP101(二)

(一)传送门:[刷题记录]牛客面试笔刷TOP101(一)_HY_PIGIE的博客-CSDN博客目录1.合并二叉树2.二叉树的镜像3.判断是否为二叉搜索树4.判断是不是完全二叉树1.合并二叉树合并二叉树_牛客题霸_牛客网(nowcoder.com)思路:在后序遍历的基础上进行,两颗二叉树可能会有位置有空缺的情况.在一个子

Python基础学习笔记1(AI Studio)

地址:飞桨AIStudio星河社区-人工智能学习与实训社区课程地址:飞桨AIStudio星河社区-人工智能学习与实训社区课程地址:飞桨AIStudio星河社区-人工智能学习与实训社区课程地址:飞桨AIStudio星河社区-人工智能学习与实训社区AIStudio的Notebook项目的基本操作项目启停执行和调试多文件代码

热文推荐