格式化输出 std::fmt
1. 基础用法
基础的格式化输出主要使用 print!
、println!
和 format!
宏。这些宏可以接受一个格式字符串和一个或多个值。
fn main() {
let name = "Alice";
let age = 30;
println!("Hello, {}!", name);
println!("{} is {} years old.", name, age);
}
2. 格式字符串
格式字符串包含普通文本和由花括号包围的格式说明符 {}
。每个 {}
将会被后面的相应参数替换。
fn main() {
let a = 1;
let b = 2;
println!("a: {}, b: {}", a, b);
}
3. 格式说明符
格式说明符可以指定如何格式化值。常见的格式说明符包括:
{:?}
:使用Debug
格式化。{:#?}
:使用多行Debug
格式化。{}
:使用Display
格式化。
fn main() {
let v = vec![1, 2, 3];
println!("Debug: {:?}", v);
println!("Pretty Debug: {:#?}", v);
}
4. 常用格式选项
格式选项允许你进一步控制输出格式。格式说明符可以包含多个选项,如填充、对齐、宽度、精度和类型。
4.1 填充和对齐
你可以指定填充字符和对齐方式:
<
:左对齐>
:右对齐^
:居中对齐
fn main() {
const LEN: usize = 8; // 使用 usize 代替 u8,因为格式化宽度需要 usize 类型
println!("{:*<1$}", "left", LEN); // 使用 '*' 填充,左对齐
println!("{:*>1$}", "right", LEN); // 使用 '*' 填充,右对齐
println!("{:*^1$}", "center", LEN); // 使用 '*' 填充,居中对齐
println!("{:*<1$}", "left", LEN); // 使用 '*' 填充,左对齐
}
4.2 宽度和精度
宽度指定最小字段宽度,精度用于浮点数和字符串的最大字符数。
fn main() {
println!("{:8}", "hello"); // 最小宽度为 8
println!("{:.3}", "hello"); // 最大宽度为 3
println!("{:8.3}", "hello"); // 最小宽度为 8,最大宽度为 3
let pi = 3.141592;
println!("{:.2}", pi); // 保留 2 位小数
}
4.3 类型
b
:二进制格式x
:十六进制格式(小写)X
:十六进制格式(大写)o
:八进制格式p
:指针格式
fn main() {
let num = 255;
println!("Binary: {:b}", num);
println!("Hexadecimal: {:x}", num);
println!("Octal: {:o}", num);
println!("Pointer: {:p}", &num);
}
5. 自定义格式化
你可以通过实现 std::fmt::Display
或 std::fmt::Debug
trait 来定义自定义类型的格式化方式。
5.1 实现 Display
trait
Display
用于用户友好的格式化输出。
use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("Point: {}", p);
}
5.2 实现 Debug
trait
Debug
用于调试友好的格式化输出。
use std::fmt;
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("Point: {:?}", p);
}
6. format!
宏
format!
宏类似于 println!
,但它返回格式化后的字符串,而不是输出到控制台。
fn main() {
let name = "Alice";
let greeting = format!("Hello, {}!", name);
println!("{}", greeting);
}
7. 高级用法
7.1 动态格式化参数
使用 format_args!
宏,可以动态生成格式字符串和参数。
use std::fmt::Write;
fn main() {
let mut output = String::new();
write!(&mut output, "Hello, {}!", "world").unwrap();
println!("{}", output);
}
7.2 自定义格式化函数
你可以定义一个接受 fmt::Formatter
参数的函数,以自定义复杂格式化逻辑。
use std::fmt;
fn custom_format(f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Custom formatted string")
}
fn main() {
println!("{}", format_args!("{}", custom_format));
}
8. 更多高级用法和技巧
8.1 条件格式化
在实际开发中,可能会遇到需要根据某些条件动态调整格式化输出的情况。
fn main() {
let number = 42;
let formatted = if number < 10 {
format!("{:02}", number) // 用零填充
} else {
format!("{}", number)
};
println!("Formatted number: {}", formatted);
}
在这个例子中,根据number
的值选择不同的格式化方式。
8.2 多行格式化
有时你需要将格式化结果分成多行输出,这在输出结构化数据时特别有用。
fn main() {
let name = "Alice";
let age = 30;
let address = "1234 Wonderland";
let info = format!(
"Name: {}\nAge: {}\nAddress: {}",
name, age, address
);
println!("User Info:\n{}", info);
}
8.3 捕获格式化结果
你可以使用format!
宏将格式化结果捕获到字符串中,以便后续处理。
fn main() {
let name = "Alice";
let greeting = format!("Hello, {}!", name);
// 现在 `greeting` 变量包含了格式化后的字符串,可以进行进一步处理
println!("{}", greeting);
}
8.4 格式化多个值
在某些情况下,你可能需要格式化多个值并将它们组合成一个字符串。
fn main() {
let width = 4;
let height = 7;
let area = width * height;
let description = format!(
"The rectangle is {} units wide and {} units high, with an area of {} square units.",
width, height, area
);
println!("{}", description);
}
8.5 自定义格式化宏
如果你经常需要使用特定的格式化逻辑,可以创建自定义宏来简化代码。
macro_rules! greet {
($name:expr) => {
format!("Hello, {}!", $name)
};
}
fn main() {
let greeting = greet!("Alice");
println!("{}", greeting);
}
8.6 实现 fmt::Display
和 fmt::Debug
的高级用法
为了更加灵活地处理复杂的数据结构,可以深入了解和使用fmt::Display
和fmt::Debug
的高级特性。
use std::fmt;
struct Complex {
real: f64,
imag: f64,
}
impl fmt::Display for Complex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} + {}i", self.real, self.imag)
}
}
impl fmt::Debug for Complex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Complex {{ real: {}, imag: {} }}", self.real, self.imag)
}
}
fn main() {
let complex = Complex { real: 3.3, imag: 7.2 };
println!("Display: {}", complex);
println!("Debug: {:?}", complex);
}