Rust语言|Rust格式化输出 | 字数总计: 3.3k | 阅读时长: 14分钟 | 阅读量:
print!
将格式化文本输出到标准输出,不带换行符 。println!
用于向标准输出设备打印信息并在末尾自动添加换行符 。format!
将格式化文本存入字符串。在实际的项目中,最常用的还是 println!
和 format!
,前者常用来调试输出,后者常用来生成格式化字符串。
1 2 3 4 5 6 fn main () { let s = "hello" ; println! ("{}, world" , s); let s1 = format! ("{}, world" , s); print! ("{}" , s1); }
输出结果:
1 2 hello, world hello, world
eprint!
和eprintln!
eprint!
输出到标准错误输出,不带换行符 。eprintln!
输出到标准错误输出,在末尾自动添加换行符 。1 2 3 4 fn main () { eprint!("Error: Could not complete task1" ); eprintln!("Error: Could not complete task2" ); }
输出结果:
1 2 Error: Could not complete task1Error: Could not complete task2
{}
格式化占位符Rust 选择 {}
作为格式化占位符,无需再为特定的类型选择特定的占位符,统一用 {}
来替代,类型推导的细节则交给 Rust 编译器去做。
{}
是普通的占位符,它会将传入的值按照顺序进行替换。{:?}
是调试占位符,除了将值进行替换以外,还会对值进行漂亮的打印,使得输出结果更易于阅读。{:#?}
是更为详细的调试占位符,提供比 {:?}
更加深入的调试信息。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #[derive(Debug)] struct Apple { x: i32 , y: char } fn main () { let x = 42 ; let y = "Hello, world!" ; let z = Apple { x: 10 , y: 'a' }; println! ("普通占位符: {}" , x); println! ("普通占位符: {}" , y); println! ("调试占位符: {:?}" , x); println! ("调试占位符: {:?}" , y); println! ("调试占位符: {:?}" , z); println! ("详细调试占位符: {:#?}" , x); println! ("详细调试占位符: {:#?}" , y); println! ("详细调试占位符: {:#?}" , z); }
执行结果:
1 2 3 4 5 6 7 8 9 10 11 普通占位符: 42 普通占位符: Hello, world! 调试占位符: 42 调试占位符: "Hello, world!" 调试占位符: Apple { x: 10, y: 'a' } 详细调试占位符: 42 详细调试占位符: "Hello, world!" 详细调试占位符: Apple { x: 10, y: 'a' , }
Debug特性 Debug 是为开发者调试打印数据结构所设计的,在使用的时候,对于数值、字符串、数组,可以直接使用 {:?}
输出;但是对于结构体、元组和枚举,需要派生 Debug 特性后,才能进行输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 #[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); }
执行结果:
1 3.1415926, "hello" , [1, 2, 3], Person { name: "sunface" , age: 18 }
Display特性 Display 是给用户显示数据结构所设计的,在使用的时候,Display 用 {}
打印。与大部分类型实现了 Debug 特性不同,实现了 Display 特性的 Rust 类型没有那么多,自定义类型的 Display 必须自己实现。
1 2 3 4 5 6 7 8 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);
执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 error[E0277]: `Vec<{integer }>` doesn't implement `std::fmt::Display` --> demo.rs:14:38 | 14 | println!("{}, {}, {}, {}", i, s, v, p); | ^ `Vec<{integer}>` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `Vec<{integer}>` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Person` doesn' t implement `std::fmt ::Display` --> demo.rs:14:41 | 14 | println!("{}, {}, {}, {}" , i, s, v, p); | ^ `Person` cannot be formatted with the default formatter | = help : the trait `std::fmt ::Display` is not implemented for `Person` = note: in format strings you may be able to use `{:?}` (or {: = note: this error originates in the macro `$crate ::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`.
从运行结果来看:数组和结构体都没法通过编译,因为这两种类型没有实现 Display 特性。解决的方法有以下三种:
使用 {:?}
和 {:#?}
。 为自定义类型实现 Display 特性。 使用 newtype 为外部类型实现 Display 特性。 {:#?}
和 {:?}
几乎一样,区别是 {:#?}
能够更优美地输出内容。示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #[derive(Debug)] struct Person { name: String , age: u8 } fn main () { let v = vec! [1 , 2 , 3 ]; let p = Person { name: "sunface" .to_string (), age: 18 , }; println! ("{:?}, {:?}" , v, p); println! ("{:#?}, {:#?}" , v, p); }
输出结果:
1 2 3 4 5 6 7 8 9 [1, 2, 3], Person { name: "sunface" , age: 18 } [ 1, 2, 3, ], Person { name: "sunface" , age: 18, }
位置参数 每个格式化参数既可以按照顺序进行格式化,也可以按照指定的顺序进行格式化。
{}
:没有指定索引位置,会按照参数迭代逐个使用。{n}
:指定了索引位置,则只用指定索引的参数,n 从 0 开始计数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn main () { println! ("{} is a number, '{}' is a string" , 10 , "hello world" ); println! ("{1} is a number, '{0}' is a string" , "hello world" , 10 ); println! ("{1} {} {0} {}" , 1 , 2 ) }
具名参数 可以通过为参数指定名称来指定参数输出的顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn main () { let name = "Bob" ; println! ("My name is {name}." ); println! ( "My name is {name}, his name is {name1}." , name = "Alice" , name1 = "Jeff" ); println! ("name: {name}" ); }
注意: 带名称的参数必须放在不带名称参数的后面。
1 println! ("{abc} {1}" , abc = "def" , 2 );
报错:
1 2 3 4 | println!("{abc} {1}" , abc = "def" , 2); | ----- ^ positional arguments must be before named arguments | | | named argument
格式化参数 宽度 宽度用来指示输出目标的最小长度,如果长度不够则进行填充和对齐;如果长度超出并不会进行截断。
{:n}
:通过数字直接指定宽度,如 {:5} 。{:n$}
:通过参数索引指定宽度,如 {:1$} 。{:name$}
:通过具名参数指定宽度,如 {:width$} 。示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 fn main () { println! ("Hello {:5}!" , "a" ); println! ("Hello {:2}!" , "World" ); println! ("Hello {:1$}!" , "abc" , 5 ); println! ("Hello {1:0$}!" , 5 , "abc" ); println! ("Hello {:width$}!" , "abc" , width = 5 ); }
填充和对齐 在格式化字符串中,一般按照 :
+ 填充字符
+ 对齐方式
来排列。
[fillchar]<
:左对齐[fillchar]^
:居中对齐[fillchar]>
:右对齐[]
表示可选。
关于填充:
非数字时,默认填充使用 空格
+ 左对齐
。 数字时,默认填充使用 空格
+ 右对齐
。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 fn main () { assert_eq! (format! ("Hello {:7}!" , "abc" ), "Hello abc !" ); assert_eq! (format! ("Hello {:<7}!" , "abc" ), "Hello abc !" ); assert_eq! (format! ("Hello {:-<7}!" , "abc" ), "Hello abc----!" ); assert_eq! (format! ("Hello {:->7}!" , "abc" ), "Hello ----abc!" ); assert_eq! (format! ("Hello {:-^7}!" , "abc" ), "Hello --abc--!" ); assert_eq! (format! ("Hello {:7}!" , 7 ), "Hello 7!" ); assert_eq! (format! ("Hello {:<7}!" , 7 ), "Hello 7 !" ); assert_eq! (format! ("Hello {:^7}!" , 7 ), "Hello 7 !" ); assert_eq! (format! ("Hello {:07}!" , 7 ), "Hello 0000007!" ); assert_eq! (format! ("Hello {:07}!" , -7 ), "Hello -000007!" ); }
注意: 某些类型可能不会实现对齐,特别是对于 Debug trait 。确保应用填充的一种比较好的方法是格式化输入,再填充此结果字符串进行输出。
进制 Rust 中使用 #
来控制数字的进制输出。
#b
:二进制#o
:八进制#x
:小写十六进制#X
:大写十六进制x
:不带前缀的小写十六进制1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn main () { assert_eq! (format! ("Hello {:#b}!" , 10251 ), "Hello 0b10100000001011!" ); assert_eq! (format! ("Hello {:#o}!" , 10251 ), "Hello 0o24013!" ); assert_eq! (format! ("Hello {}!" , 10251 ), "Hello 10251!" ); assert_eq! (format! ("Hello {:#x}!" , 10251 ), "Hello 0x280b!" ); assert_eq! (format! ("Hello {:#X}!" , 10251 ), "Hello 0x280B!" ); assert_eq! (format! ("Hello {:x}!" , 10251 ), "Hello 280b!" ); assert_eq! (format! ("{:#010b}" , 27 ), "0b00011011" ); }
{:#010b}
:
#
是一个选项,表示在输出的二进制数前添加 0b
前缀。0
是填充字符,表示用 0
填充空白部分。10
是宽度,表示最少要输出 10 位。b
是类型,表示以二进制形式输出。精度控制 精度控制是指最大输出宽度,当超出精度控制时就会进行截止,一般针对浮点类型和字符串类型。
.n
:整数 n 就是精度。.n$
:使用参数 n 作为精度,可以使用参数索引或者具名参数。.*
:与两个输入格式关联,第一个输入是要保存的精度,第二个是要打印的值。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 fn main () { assert_eq! ( format! ("Hello {} is {:.5}" , "abc" , 0.01 ), "Hello abc is 0.01000" ); assert_eq! ( format! ("Hello {0} is {1:.5}" , "abc" , 0.01 ), "Hello abc is 0.01000" ); assert_eq! ( format! ("Hello {0} is {1:.2$}" , "abc" , 0.01 , 5 ), "Hello abc is 0.01000" ); assert_eq! ( format! ("Hello {0} is {1:.precision$}" , "abc" , 0.01 , precision = 5 ), "Hello abc is 0.01000" ); assert_eq! ( format! ( "Hello {} is {number:.precision$}" , "abc" , number = 0.01 , precision = 5 ), "Hello abc is 0.01000" ); assert_eq! ( format! ("Hello {} is {:.*}" , "abc" , 5 , 0.01 ), "Hello abc is 0.01000" ); assert_eq! ( format! ("Hello {} is {2:.*}" , "abc" , 5 , 0.01 ), "Hello abc is 0.01000" ); assert_eq! ( format! ("Hello, {number:.*}" , 3 , number = 123.45 ), "Hello, 123.450" ); assert_eq! ( format! ("Hello, {number:.*}" , 3 , number = "123.45" ), "Hello, 123" ); assert_eq! ( format! ("Hello, {number:>8.*}" , 3 , number = 123.45 ), "Hello, 123.450" ); }
指数 :e
:LowerExp,小写 e 的科学计数法。:E
:UpperExp,大写 E 的科学计数法。1 2 3 4 fn main () { println! ("{:e}" , 1000000000 ); println! ("{:E}" , 1000000000 ); }
指针地址 1 2 3 4 fn main () { let v = vec! [1 , 2 , 3 ]; println! ("{:p}" , v.as_ptr ()) }
转义 字符 {
使用 {{` 进行转义,字符 `}` 使用 `}}
进行转义。
1 2 assert_eq! (format! ("Hello {{}}" ), "Hello {}" );assert_eq! (format! ("{{ Hello" ), "{ Hello" );
参考引用