Rust中字符串与格式化
Rust 提供了多种字符串类型和强大的格式化工具。
字符串
Rust 中主要有两种字符串类型:&str
(字符串切片)和String
(动态字符串),二者均基于UTF-8 编码
。
String-动态字符串
String
是标准库提供的拥有所有权的动态字符串类型,存储在堆上:
- 可变:可通过方法修改内容(如添加、删除字符)。
- 动态分配:内存会根据内容自动扩容。
- 所有权:
String
拥有其数据,赋值或传递时会触发所有权转移。
创建:
let mut s = String::new(); // 空字符串
let s = "initial".to_string(); // 从字面量转换
let s = String::from("initial"); // 同上
let s = format!("Hello {}", "world"); // 通过格式化宏创建
&str-字符串切片
&str
是对一段 UTF-8 编码字符串的不可变引用,它不拥有数据所有权,仅作为 “视图” 存在:
- 不可变:无法直接修改
&str
指向的内容。 - 轻量:由两部分组成(指针 + 长度),不分配堆内存,传递时无需复制数据。
- 字面量默认类型:代码中的字符串字面量(如
"hello"
)本质上是&'static str
,生命周期为整个程序运行期。
let s: &str = "hello world"; // 字符串字面量,类型为&'static str
let slice: &str = &s[0..5]; // 从s中截取切片,结果为"hello"
&str与String间转换
String
→&str
:通过as_str()
方法或直接引用(&s
),因为String
实现了Deref<Target=str>
,会自动解引用为&str
。
let s = String::from("test");
let s_slice: &str = s.as_str(); // 显式转换
let s_slice2: &str = &s; // 自动解引用,更常用
&str
→String
:通过String::from
或to_string()
。
let slice = "hello";
let s = String::from(slice); // 转换为String
let s2 = slice.to_string(); // 等价方式
常用操作
分类 | 方法 / 操作 | 适用类型 | 说明 |
---|---|---|---|
创建 | String::new() | String | 创建空字符串 |
String::from("abc") | String | 从字面量创建 | |
"abc".to_string() | String | 从 &str 创建 String | |
String::with_capacity(n) | String | 预分配容量,减少重分配 | |
转换 | s.as_str() | String | 转换为 &str |
s.into_bytes() | String | 转换为 Vec<u8> | |
String::from_utf8(v) | Vec<u8> | 从字节向量还原字符串 | |
s.to_string() | &str | 创建新 String | |
长度 | s.len() | String / &str | 返回字节长度(不是字符数) |
s.chars().count() | String / &str | 返回字符数(Unicode scalar) | |
判断 | s.is_empty() | String / &str | 是否为空字符串 |
s.contains("ab") | String / &str | 是否包含子串 | |
s.starts_with("ab") | String / &str | 是否以指定子串开头 | |
s.ends_with("ab") | String / &str | 是否以指定子串结尾 | |
拼接 | s.push('a') | String | 追加单个字符 |
s.push_str("abc") | String | 追加字符串切片 | |
s1 + &s2 | String + &str | 拼接(移动左侧) | |
format!("{}{}", a, b) | 任意 | 格式化拼接,不移动 | |
截取/分割 | &s[a..b] | &str | 按字节切片(需在字符边界) |
s.split(',') | String / &str | 按分隔符分割为迭代器 | |
s.split_whitespace() | String / &str | 按空白分割 | |
s.lines() | String / &str | 按行分割 | |
修剪 | s.trim() | String / &str | 去除首尾空白 |
s.trim_start() / trim_end() | String / &str | 去除首或尾空白 | |
替换 | s.replace("a","b") | String / &str | 全部替换 |
s.replacen("a","b",n) | String / &str | 替换前 n 次 | |
大小写 | s.to_lowercase() | String / &str | 转小写 |
s.to_uppercase() | String / &str | 转大写 | |
查找 | s.find("ab") | String / &str | 返回首个匹配位置(Option) |
s.rfind("ab") | String / &str | 从后往前找 | |
迭代 | s.chars() | String / &str | 按字符迭代 |
s.bytes() | String / &str | 按字节迭代 | |
比较 | s1 == s2 | String / &str | 内容比较(UTF-8 安全) |
拷贝 / 克隆 | s.clone() | String | 深拷贝(堆上内容复制) |
清空 / 容量 | s.clear() | String | 清空内容 |
s.capacity() | String | 当前容量 | |
s.reserve(n) | String | 预留额外容量 | |
字节访问 | s.as_bytes() | String / &str | 获取字节切片 |
连接 Vec | vec.join(",") | Vec<&str> | 用分隔符连接 |
格式化输出 | format!() | 任意 | 返回格式化字符串 |
格式化
Rust 提供了一套强大的格式化宏,用于字符串拼接、输出等场景,核心是format!
(返回String
),以及衍生的print!
(打印到标准输出)、println!
(带换行的打印)、eprint!
(打印到标准错误)等。
格式化语法说明:
{[argument][:[fill][align][width][.precision][type]]}
// 动态宽度+精度(后面需要$)
println!("{value:>width$.prec$}", value=3.14159, width=10, prec=3); // " 3.142"// 带前缀的十六进制
println!("{:#08x}", 42); // "0x00002a"// 命名参数+格式化
println!("{name:*^10}", name="ALICE"); // "**ALICE***"
参数指定
{argument}
用于指定要格式化的参数,默认省略(按顺序)
形式 | 说明 | 示例 | 输出结果 |
---|---|---|---|
省略 | 按参数顺序匹配(默认) | println!("{}, {}", "a", "b") | a, b |
位置索引{n} | 按索引n (从 0 开始)匹配 | println!("{1}, {0}", "a", "b") | b, a |
命名{name} | 按参数名匹配 | println!("{x}, {y}", x=1, y=2) | 1, 2 |
填充与对齐
语法片段:[fill][align][width]
fill
:任意单字符(默认空格);使用fill时需要同时指定align+width
。align
:<
(左对齐)、>
(右对齐,默认)、^
(居中)。width
:最小字段宽度(整数),不足时使用fill
填充。
形式 | 说明 | 示例 | 输出结果 |
---|---|---|---|
固定宽度{:w} | 指定最小宽度 | println!("width: [{:*^5}]", 123); | [*123*] |
动态宽度{:width$} | 宽度由参数指定($ 是必须的) | println!("[{:w$}]", 123, w=5); | [ 123] |
format!("{:>8}", "hi") // 右对齐 -> " hi"
format!("{:<8}", "hi") // 左对齐 -> "hi "
format!("{:^8}", "hi") // 居中 -> " hi "
format!("{:0>5}", 42) // 填充字符 '0', 右对齐 -> "00042"
精度
语法片段:.precision
(写作 .N
或 动态 .*
)
适用类型 | 含义 | 示例 | 输出结果 |
---|---|---|---|
字符串 | 最大长度(超过则截断) | println!("{:.3}", "hello") | hel |
浮点数 | 小数位数(四舍五入) | println!("{:.2}", 3.1415) | 3.14 |
整数 | 最小位数(不足补 0,超过则原样输出) | println!("{:.4}", 12) | 0012 |
动态精度{:.*} | 精度由参数指定(前一个参数为精度值) | println!("{}-{:.*}","ab", 2, 3.1415) | ab-3.14 |
类型格式
指定数据的格式化类型,将数据转换为特定格式的字符串。
整数类型
整数类型(i8/i16/i32/i64/u8/u16/…/usize/isize):
说明符 | 作用 | 示例 | 输出结果 |
---|---|---|---|
b | 二进制 | println!("{:b}", 10) | 1010 |
o | 八进制 | println!("{:o}", 10) | 12 |
d | 十进制(默认) | println!("{:d}", 10) | 10 |
x | 小写十六进制 | println!("{:x}", 255) | ff |
X | 大写十六进制 | println!("{:X}", 255) | FF |
浮点数类型
浮点数类型(f32/f64):
说明符 | 作用 | 示例 | 输出结果 |
---|---|---|---|
f | 小数形式(默认) | println!("{:f}", 123.456) | 123.456000 (默认 6 位小数) |
e | 科学计数法(小写 e) | println!("{:e}", 123.456) | 1.234560e+02 |
E | 科学计数法(大写 E) | println!("{:E}", 123.456) | 1.234560E+02 |
g | 自动选择f 或e (取较短形式) | println!("{:g}", 123456789.0) | 1.23457e+08 |
G | 自动选择f 或E | println!("{:G}", 123456789.0) | 1.23457E+08 |
通用与特殊类型
说明符 | 作用 | 适用场景 | 示例 | 输出结果 |
---|---|---|---|---|
? | 按Debug trait 格式化(调试用) | 所有实现Debug 的类型 | println!("{:?}", vec![1,2]) | [1, 2] |
#? | 美化的Debug 格式(换行 + 缩进) | 复杂结构(如结构体、嵌套集合) | println!("{:#?}", vec![1,2]) | [\n 1,\n 2\n] |
p | 指针地址 | 仅用于引用类型 | println!("{:p}", &10) | 0x7ff72194e7b8 |
标志(Flags)
特殊修饰符,用于调整输出格式,可与其他说明符组合使用。
标志 | 作用 | 示例 | 输出结果 |
---|---|---|---|
# | 为数值添加前缀(二进制0b 、八进制0o 、十六进制0x ) | println!("{:#x}", 255) | 0xff |
+ | 强制显示正负号(正数显+ ,负数显- ) | println!("{:+}", 10); println!("{:+}", -10) | +10 -10 |
0 | 用0 填充(代替空格) | println!("{:05}", 123) | 00123 |
_ | 为大数值添加千位分隔符(仅整数和浮点数) | println!("{:_}", 1000000) | 1_000_000 |
特殊格式
语法 | 示例 | 说明 |
---|---|---|
{{ | {{ | 输出左花括号 { |
}} | }} | 输出右花括号 } |
自定义 Display
获取参数
当在类型上实现 fmt::Display
时,可以在 fmt(&self, f: &mut fmt::Formatter<'_>)
中读取用户给的 width
、precision
、alternate
等:
use std::fmt;struct Point { x: f64, y: f64 }impl fmt::Display for Point {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {// 获取 width / precision(Option<usize>)if let Some(prec) = f.precision() {write!(f, "({:.prec$}, {:.prec$})", self.x, self.y)} else if f.alternate() {// 如果用户写了 "{:#}",可切换格式write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)} else {write!(f, "({}, {})", self.x, self.y)}}
}