rust输入输出

一、获取命令行参数

很多语言获取命令行参数,是通过主函数的参数获得的。

但Rust主函数是个无参数函数,命令行参数只能通过std::env::args()函数获得。

std::env::args()返回一个迭代器,其中包含了程序名和后面所有参数。

实例

rust 复制代码
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,返回值是读取的字节数。

实例

rust 复制代码
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);
}

实例

rust 复制代码
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类型转换

实例

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特质

实例

rust 复制代码
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!()宏

rust 复制代码
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开始)

rust 复制代码
println!("{}{}", 1, 2); // =>"12"
println!("{1}{0}", 1, 2); // =>"21"

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

rust 复制代码
println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2"

(2)命名参数

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

rust 复制代码
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! 将引用当前作用域中的同名变量

rust 复制代码
let argument = 2 + 2;
println!("{argument}"); // => "4"

2.总宽度width

总宽度表示输出的总长度,如果实际长度不够,则要填充和对齐

有两种方法指定宽度

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

实例

rust 复制代码
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是位置参数或命名参数
.*:.* 表示第一个参数表示精度,第二个参数才是要输出的值

实例

rust 复制代码
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

参数位数不够时,要填充。默认填充空格

实例

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

5.对齐align

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

< 左对齐
^ 居中对齐
> 右对齐
rust 复制代码
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是不同的。

实例

rust 复制代码
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

rust 复制代码
以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.转义

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

{{ 转义为 {
}} 转义为 }
\" 转义为 "
rust 复制代码
println!(" Hello \"{{World}}\" ");     // => Hello "{World}"

9.Debug特性

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

rust 复制代码
#[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特征

rust 复制代码
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解决此问题。

rust 复制代码
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文件读写

相关推荐
confiself9 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
XiaoLeisj20 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
杜杜的man24 分钟前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*25 分钟前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家26 分钟前
go语言中package详解
开发语言·golang·xcode
llllinuuu26 分钟前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s27 分钟前
Golang--协程和管道
开发语言·后端·golang
王大锤439129 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
为什么这亚子30 分钟前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
39 分钟前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习