字符串格式化
format!() 宏用于生成格式化的字符串, 它类似于 C 语言中的 printf() 以及 Python 中的 str.format() 函数.
let name = "Julia";
let greating = format!("Hello, {}", name);
如果是结构体的话, 使用 {:#?} 可以实现类似 "pretty printing" 的效果, 多行打印结构体信息:
let value = [1, 1, 2, 3, 5];
println!("value: {:#?}", value);
位置参数 Positional parameters
使用参数的位置索引, 比如 {0}, {1}, {2} 这种的.
如果省略了里面的位置标记, 那就会提供一个计数器, 从0开始计数.
看一个例子:
fn main() {
let s = format!("{} {} {}", 1, 2, 3);
assert_eq!(s, "1 2 3");
// `{}` 从 0 开始计数
let s = format!("{1} {} {2}", 1, 2, 3);
assert_eq!(s, "2 1 3");
let s = format!("{1} {2} {0}", 1, 2, 3);
assert_eq!(s, "2 3 1");
}
具名参数 Named parameters
还支持类似于 python 中的那种格式化, 使用参数名称, 实现更灵活更易读的参数引用:
fn main() {
println!("Jolia {age}", age = 25);
let value = [1, 1, 2, 3, 5];
println!("value: {value:?}");
}
格式化参数 Formatting parameters
宽度 Width
可以指定参数所占用的最小宽度 (最少字符数), 如果宽度不够, 可以使用空格或者指定的字符来填充.
指定参数占用的宽度值, 该值需要是 usize 类型, 比如 {name:width$}, 这里的 name 是要被
格式化的参数, 而 width 参数就指定了它占用的宽度, 注意 width 后面那个 $ 是作为后缀存在的.
fn main() {
let expected = "Hello x !";
// 注意这里是直接指定宽度值 5
let s = format!("Hello {:5}!", 'x');
assert_eq!(s, expected);
// 这里 `{:1$}` 引用了位置参数 1 的值, 作为字符串的宽度值
let s = format!("Hello {0:1$}!", "x", 5);
assert_eq!(s, expected);
// 这里交换了被格式化的参数与位置参数的索引位置.
let s = format!("Hello {1:0$}!", 5, 'x');
assert_eq!(s, expected);
// 使用了具名参数作为宽度值
let s = format!("Hello {:width$}!", 'x', width = 5);
assert_eq!(s, expected);
let width: usize = 5;
// 使用了具名参数作为宽度值
let s = format!("Hello {:width$}!", 'x');
assert_eq!(s, expected);
}
对齐方式与填充字符 Alignment and fill
在设定了字符串的宽度值时, 同时可以指定字符串的对齐方式以及多余空间要填充的字符.
字符串的对齐方式有三种:
<左对齐, 如果不指定对齐方式, 默认就是左对齐^居中>右对齐
字符串宽度与对齐方式的语法是 string:char<width, 可以这样解析:
- 左对齐, 也可以是右中对齐或者右对齐, 要注意的是这里并没有考虑有些语言中从右到左的布局方式 (R2L), 我们默认是L2R
- 字符串的宽度值是
width, 比如5 - 空白处要填充的字符是
char, 比如-或者. - 如果指定了要填充的字符, 则必须同时指定对齐方式
- 如果没有指定对齐方式, 默认是左对齐
- 默认的填充字符是空格
看一些代码示例:
fn main() {
let width: usize = 5;
// 左对齐
let s = format!("Hello {:<width$}!", 'x');
assert_eq!(s, "Hello x !");
// 居中对齐
let s = format!("Hello {:^width$}!", 'x');
assert_eq!(s, "Hello x !");
// 右对齐
let s = format!("Hello {:>width$}!", 'x');
assert_eq!(s, "Hello x!");
// 左对齐, 使用 `.` 填充
let s = format!("Hello {:.<width$}!", 'x');
assert_eq!(s, "Hello x....!");
// 居中对齐, 使用 `-` 填充
let s = format!("Hello {:-^width$}!", 'x');
assert_eq!(s, "Hello --x--!");
// 右对齐, 用 `0` 填充
let s = format!("Hello {:0>5}", 'x');
assert_eq!(s, "Hello 0000x");
// 整数的十六进制形式, 右对齐, 字符串宽度值为8, 使用 `0` 填充空位
let s = format!("Hello 0x{:0>8x}", 12345678);
assert_eq!(s, "Hello 0x00bc614e");
}
数值的符号与填充
上面讲到的对齐与字符宽度等, 都是通用的格式化手段. 这里要介绍的是数值类型特有的格式化手段.
+, 默认情况下, 只要负数才会被打印-符号, 而无符号数以及正数, 都会忽略掉符号位. 使用+可以强制打印数值的符号-, 目前不支持, 会被忽略掉0, 使用0进行数值填充, 而且对齐方式被强制为左对齐#表示要使用另外一种格式化形式:?, 使用 pretty-printing 形式调用fmt::Debugtrait, 会添加换行符和缩进x, 使用小写的十六进制格式, 并且添加0x前缀X, 使用大写的十六进制格式, 并且添加0X前缀b, 使用小写的二进制格式, 并且添加0b前缀o, 使用八进制格式, 并且添加0o前缀
看下面的示例代码:
fn main() {
let x: i32 = 42;
// 强制指定 `+` 符号
let s = format!("Hello, {x:+}");
assert_eq!(s, "Hello, +42");
// 使用 `0` 作为填充字符, 需要指定字符串的宽度值
let s = format!("Hello, {x:#04}");
assert_eq!(s, "Hello, 0042");
// 小写的十六进制
let s = format!("Hello, {x:#x}");
assert_eq!(s, "Hello, 0x2a");
// 大写的十六进制
let s = format!("Hello, {x:#X}");
assert_eq!(s, "Hello, 0x2A");
// 二进制
let s = format!("Hello, {x:#b}");
assert_eq!(s, "Hello, 0b101010");
// 八进制
let s = format!("Hello, {x:#o}");
assert_eq!(s, "Hello, 0o52");
}
数值精度 Precision
常用格式化符号 Formatting Traits
println!("{formatting}", arg);, 这里的 formatting 就是本节要讨论的 Formatting traits,
标准库里定义了一些 traits, 用于修饰被格式化的参数, 以得到期望的形式.
| 符号 | 描述 |
|---|---|
| 空白 | 调用 fmt::Display trait |
:? | 调用 fmt::Debug trait |
:x? | 调用 fmt::Debug trait, 并使用小写的十六进制整数 |
:X? | 调用 fmt::Debug trait, 并使用大写的十六进制整数 |
:o | 调用 fmt::Octal trait, 转换成八进制 |
:x | 调用 fmt::LowerHex trait, 转换成小写的十六进制 |
:X | 调用 fmt::UpperHex trait, 转换成大写的十六进制 |
:b | 调用 fmt::Binary trait, 转换成二进制 |
:e | 调用 fmt::LowerExp trait, 转换成小写的科学计数法格式 |
:E | 调用 fmt::UpperExp trait, 转换成大写的科学计数法格式 |
:p | 调用 fmt::Pointer trait, 转换成指针形式 |
在标准库中已经为很多基础数据类型实现了, 上表中列出来的 fmt 模块中的各个 traits.
下面的代码示例展示了如何使用格式化参数:
fn main() {
let x = 42;
println!("八进制: 0o{:o}", x);
println!("十进制: {}", x);
println!("小写的十六进制: 0x{:x}", x);
println!("大写的十六进制: 0x{:X}", x);
println!("二进制: 0b{:b}", x);
println!("x 的地址: {:p}", &x);
let x2 = 1234567890;
println!("小写的科学计数法: {:e}", x2);
println!("大写的科学计数法: {:E}", x2);
let s = "Hello, Rust";
println!("Display: {}", s);
println!("Debug: {:?}", s.as_bytes());
}
相关的宏定义
标准库中定义了一系列与字符串格式化相关的宏, 它们分别是:
format!write!writeln!print!println!eprint!eprintln!format_args!
format! 宏
TODO