rust-str-string

新建字符串

String::new()

返回一个新的空字符串。这时还没有在堆上分配缓冲区,但将来会按需分配

String::with_capacity(n)

返回一个新的空字符串,其中预先分配了一个足以容纳至少 n 字节的缓冲区。

str_slice.to_string()

fn main() {
    let s = "hello world";

    let s1:String = s.to_string();

    println!("{}", s1);
}

iter.collect()

fn main() {
    let s = "hello world";

    let s1:String = s.chars().filter(|x| !x.is_ascii_whitespace()).collect();

    println!("{}", s1);
}

slice.to_owned()

效果和to_string()一样,to_owned会从借用转为拥有(重新clone一份)

fn main() {
    let s = "hello world";

    let s1:String = s.to_owned();

    println!("{}", s1);
    println!("{}", s);
}

判断&str内容

slice.len()

返回字符串的长度

fn main() {
    let s = "hello world";
    println!("{}", s.len());
}

slice.is_empty()

判断字符串是否为空,返回bool值

fn main() {
    let s = "hello world";
    println!("{}", s.is_empty());
}

slice.split_at(i)

返回元组,这会返回 (slice[..i], slice[i..])

fn main() {
    let s = "hello world";
    println!("{:?}", s.split_at(3));
}

slice.is_char_boundary(i)

判断索引i处是否是字符边界
字符边界 = 字符开始的位置

fn main() {
    // 对于ascii来说,每一个就是一个字符
    let s = "hello";
    println!("{}", s.is_char_boundary(0)); // true
    println!("{}", s.is_char_boundary(4)); // true
    println!("{}", s.is_char_boundary(5)); // true(结尾也是边界)
    println!("{}", s.is_char_boundary(11)); // false(越界)
}
fn main() {
    // 对于utf-8来说,一个字符可能占一个字节,也可能占多个字节
    // 中文占用3个字节,你->0-2,好->3-5
    let s = "你好";
    println!("{}", s.is_char_boundary(0)); // true
    println!("{}", s.is_char_boundary(1)); // false
    println!("{}", s.is_char_boundary(2)); // false
    println!("{}", s.is_char_boundary(3)); // true
    println!("{}", s.is_char_boundary(4)); // false
    println!("{}", s.is_char_boundary(5)); // false
    println!("{}", s.is_char_boundary(6)); // true
}

追加文本与插入文本

string.push(char)

将字符char追加到 string 的末尾。

fn main() {
    let mut s = String::from("你好");
    s.push('😁');

    println!("{}", s); // 你好😁
}

string.push_str(slice)

追加 slice 的全部内容

fn main() {
    let mut s = String::from("你好");
    s.push_str("!!!");

    println!("{}", s); // 你好!!!
}

string.extend(iter)

追加 iter 的全部内容

fn main() {
    let mut s = String::from("你好");

    let a = vec!['a', ' ', 'b'];
    s.extend(a);

    println!("{}", s); // 你好a b
}

string.insert(i, ch)

将char插入到指定的索引i处
需要插入在字符边界,否者会报错

fn main() {
    let mut s = String::from("你好");


    s.insert(3, 'A');

    println!("{}", s);
}

string.insert_str(i, slice)

将&str插入到指定的索引i处
需要插入在字符边界,否者会报错

fn main() {
    let mut s = String::from("你好");


    s.insert_str(3, "哎哎哎");

    println!("{}", s); // 你哎哎哎好
}

String + &str

Add<&str> 和 AddAssign<&str>
Sting + &str, 不能&str + String

fn main() {
    let mut s = String::from("你好");

    s = s + "!";

    println!("{}", s);
}

移除文本与替换文本

String.clear()

清空字符串内容

fn main() {
    let mut s = String::from("你好");

    s.clear();

    println!("{}", s);
}

string.truncate(i)

截断字符串,截断到索引i处(i必须是字符边界)

fn main() {
    let mut s = String::from("abc你好");

    s.truncate(2);

    println!("{}", s);
}
fn main() {
    let mut s = String::from("abc你好");

    s.truncate(6);

    println!("{}", s);
}

string.pop()

移除字符串末尾的字符并返回该字符

fn main() {
    let mut s = String::from("abc你好");

    let a = s.pop().unwrap();

    println!("{}", s);
    println!("{}", a);
}

string.remove(i)

移除索引i处的字符并返回该字符(i需要时字符边界)

fn main() {
    let mut s = String::from("abc你好");

    let a = s.remove(3);
    // let a = s.remove(4); // 错误

    println!("{}", s); // abc好
    println!("{}", a); // 你
}

string.drain(range)

抽取文本内容并丢弃, range也是字符边界

fn main() {
    let mut s = String::from("中国你好呀!");

    s.drain(3..9); // 抽取 国你

    println!("{}", s); // 中好呀!
}

string.replace_range(range, slice)

范围替换文本内容, range也是字符边界

fn main() {
    let mut s = String::from("中国你好呀!");

    s.replace_range(3..9, "aacc");

    println!("{}", s); // 中aacc好呀!
}

搜索

为什么是&str,而不是String?

原因 说明
1. 零拷贝(Zero-copy) &str 是只读视图,不拥有数据,无需复制或分配内存
2. 通用性(Generality) &str 可以表示 String、字符串字面量、子串等
3. 性能(Performance) 避免不必要的堆分配和数据拷贝
4. 所有权语义(Ownership) 不需要转移所有权,函数可以接受任何字符串来源
5. 与 UTF-8 安全模型一致 &str 保证是合法 UTF-8,而操作基于字节索引需边界检查

查找

slice.find(slice)

左到右

fn main() {
    let s = "hello abab world nihao abab aacc";

    let a = s.find("abab");

    println!("{:?}", a); // Some(6)
}

slice.rfind(slice)

右到左

fn main() {
    let s = "hello abab world nihao abab aacc";

    let a = s.rfind("abab");

    println!("{:?}", a); // Some(23)
}

替换

slice.replace(pattern, replacement)

返回新的 String,它是通过用 replacement 急性替换 pattern 的所有匹配项而形成的

fn main() {
    let s = "hello abab world nihao abab aacc";

    let a = s.replace("abab", "你好");

    println!("s: {}", s); // hello 你好 world nihao 你好 aacc
    println!("a: {}", a); // hello abab world nihao abab aacc
}

slice.replacen(pattern, replacement, n)

与replace一样,多了替换次数n

判断开头和结尾

slice.starts_with(pattern)

fn main() {
    let s = "aaa.text";

    let a = s.starts_with("aaa");
    println!("a: {}", a); // a: true
}

slice.ends_with(pattern)

fn main() {
    let s = "aaa.text";

    let a = s.ends_with("text");
    println!("a: {}", a); // a: true
}

遍历文本

slice.bytes()

按字节遍历

fn main() {
    let s = "ab 你好   as12wde asd lk ";
    let s1:Vec<_> = s.bytes().collect();
    println!("s1: {:?}", s1); // s1: [97, 98, 32, 228, 189, 160, 229, 165, 189, 32, 32, 32, 97, 115, 49, 50, 119, 100, 101, 32, 97, 115, 100, 32, 108, 107, 32]
}

slice.chars()

按字符遍历

fn main() {
    let s = "ab 你好   as12wde asd lk ";
    let s1:Vec<_> = s.chars().collect();
    println!("s1: {:?}", s1); // ['a', 'b', ' ', '你', '好', ' ', ' ', ' ', 'a', 's', '1', '2', 'w', 'd', 'e', ' ', 'a', 's', 'd', ' ', 'l', 'k', ' ']
}

分割

slice.split(pattern)

按内容pattern分割
如果输入以分隔符结尾, 保留末尾的空字符串

  • slice.rsplit(pattern)
fn main() {
    let s = "ab 你好 as12wde asd lk ";
    let s1:Vec<&str> = s.split(' ').collect();
    println!("s1: {:?}", s1); // s1: ["ab", "你好", "as12wde", "asd", "lk", ""]
}

slice.split_terminator(pattern)

忽略末尾的分隔符

  • slice.rsplit_terminator(pattern)
fn main() {
    let s = "ab 你好 as12wde asd lk ";
    let s1:Vec<&str> = s.split(' ').collect();
    println!("s1: {:?}", s1); // s1: ["ab", "你好", "as12wde", "asd", "lk", ""]

    let s2:Vec<&str> = s.split_terminator(' ').collect();
    println!("s2: {:?}", s2); // s2: ["ab", "你好", "as12wde", "asd", "lk"]
}

slice.split_whitespace()

按空白字符拆分

  • slice.split_ascii_whitespace() 按ascii空白字符拆分
fn main() {
    let s = "ab 你好   as12wde asd lk ";
    let s1:Vec<&str> = s.split(' ').collect();
    println!("s1: {:?}", s1);

    let s2: Vec<&str> = s.split_whitespace().collect();
    println!("s2: {:?}", s2);
}

slice.splitn(n, pattern)

分割n个

  • slice.rsplitn(n, pattern)
fn main() {
    let s = "ab 你好   as12wde asd lk ";
    let s1:Vec<&str> = s.splitn(2, ' ').collect();
    println!("s1: {:?}", s1);
}

slice.lines()

按行分割, “\n"换行 或 "\r\n”回车

fn main() {
    let s = "ab 你好\nas12wde asd\nlk ";
    let s1:Vec<&str> = s.lines().collect();
    println!("s1: {:?}", s1);
}

slice.matches(pattern)

返回仅匹配的子字符串

  • slice.rmatches(pattern)
fn main() {
    let s = "asd coj sad salknd asd as";
    let n: Vec<_> = s.matches("asd").collect();
    println!("n: {:?}", n); // n: ["asd", "asd"]
}

slice.match_indices(pattern)

返回匹配项的(起始索引, 子字符串) 元组

  • slice.rmatch_indices(pattern)
fn main() {
    let s = "asd coj sad salknd asd as";
    let n: Vec<_> = s.match_indices("asd").collect();
    println!("n: {:?}", n); // n: [(0, "asd"), (19, "asd")]
}

修剪

slice.trim()

去掉首尾空白字符

  • slice.trim_start() 除去开头空白字符
  • slice.trim_end() 除去结尾空白字符
fn main() {
    let s = " asd coj sad salknd asd as ";
    let n = s.trim();
    println!("s: {}", s); //  asd coj sad salknd asd as 
    println!("n: {}", n); // asd coj sad salknd asd as
}

slice.trim_matches(pattern)

去掉首尾匹配项,重复移除(贪婪模式)
pattern支持char和字符数组/切片

  • slice.trim_start_matches(pattern)
  • slice.trim_end_matches(pattern)
    pattern是char

只移除指定开头和尾部匹配项的字符

fn main() {
    let s = "aaabbbhello worlfbaba";
    let n = s.trim_matches('a');
    println!("s: {}", s); // s: aaabbbhello worldbaba
    println!("n: {}", n); // n: bbbhello worldbab
}

pattern是字符数组/切片

只移除指定多个字符开头和尾部匹配项的字符(匹配不到就停止)

fn main() {
    let s = "aaabbbhello worldbaba";
    let n = s.trim_matches(['a', 'b']);
    println!("s: {}", s); // s: aaabbbhello worldbaba
    println!("n: {}", n); // n: hello world
}

slice.strip_prefix(pattern)

移除固定前缀
返回一个 Some,其中携带了移除匹配文本之后的切片。否则,它会返回 None。

fn main() {
    let s = "http://www.aaa.com";
    let n = s.strip_prefix("http://");
    println!("s: {}", s); // s: http://www.aaa.com
    println!("n: {}", n); // n: www.aaa.com
}

slice.strip_suffix(pattern)

移除固定后缀
返回一个 Some,其中携带了移除匹配文本之后的切片。否则,它会返回 None。

fn main() {
    let s = "aa.txt";
    let n = s.strip_suffix(".txt").unwrap();
    println!("s: {}", s); // s: aa.txt
    println!("n: {}", n); // n: aa
}

字符串的大小写转换

slice.to_uppercase() 方法和 slice.to_lowercase() 方法会返回一个新分配的字符串,其中包含已转为大写或小写的 slice 文本。

fn main() {
    let s = "aa.txt";
    let n1 = s.to_uppercase();
    let n2 = &n1.to_lowercase();
    println!("s: {}", s);  // s: aa.txt
    println!("n1: {}", n1); // n1: AA.TXT
    println!("n2: {}", n2); // n2: aa.txt
}

字符串转换类型

字符串中解析出其他类型

FromStr 是 Rust 标准库中的一个 trait(特征),它的核心作用就是:
将字符串(&str)安全地解析(parse)为其他类型的值。

pub trait FromStr {
    type Err;                 // 解析失败时返回的错误类型
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}
use std::str::FromStr;

//
assert_eq!(usize::from_str("3628800"), Ok(3628800));
// 
assert_eq!(f64::from_str("128.5625"), Ok(128.5625));
// 
assert_eq!(bool::from_str("true"), Ok(true));
// 
assert!(f64::from_str("not a float at all").is_err());
// 布尔值
assert!(bool::from_str("TRUE").is_err()); 

// char 类型也实现了 FromStr,用于解析只有一个字符的字符串
assert_eq!(char::from_str("é"), Ok('é'));

自定义FromStr

use std::str::FromStr;

#[derive(Debug)]
enum Color {
    Red, Green, Blue,
}

impl FromStr for Color {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "red"   => Ok(Color::Red),
            "green" => Ok(Color::Green),
            "blue"  => Ok(Color::Blue),
            _       => Err(format!("Unknown color: {}", s)),
        }
    }
}

fn main() {
    let c = Color::from_str("RED").unwrap();
    println!("{:?}", c); // Red
}

parse() 与 FromStr 的关系

str 类型上的 .parse::() 方法 要求 T: FromStr
它本质上是 FromStr::from_str(self) 的语法糖

"123".parse::<i32>() 
// 等价于
i32::from_str("123")

其它类型转为字符串

使用format!宏

实现std::fmt::Display特型

let s = format!("{}", 22);

Rust 对字符串处理的核心哲学:UTF-8 是唯一、默认且强制的文本编码。

Rust 的 str/String 底层是 UTF-8 字节,操作时需注意字节 vs 字符

let s = "你好";
println!("{}", s.len()); // 输出 6(字节长度!不是字符数)
// 因为 "你" = 3 字节,"好" = 3 字节,UTF-8 编码

只有合法 UTF-8 字节才能转为 String,否则会报错

let bytes = vec![0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd]; // "你好" 的 UTF-8 字节
let s = String::from_utf8(bytes).unwrap(); // ✅ 合法 UTF-8 → 文本
println!("{}", s); // 你好

str::from_utf8(byte_slice)

  • ✅ 不分配新内存:返回的 &str 直接指向 bytes 的内存。
  • ⚠️ 生命周期受限:&str 的生命周期不能超过 bytes。
  • 📌 适用于:只读、临时解析,比如处理网络包、文件映射等。
use std::str;

let bytes = b"Hello, 世界"; // &[u8] 字面量

match str::from_utf8(bytes) {
    Ok(s) => println!("Valid UTF-8: {}", s), // s: &str
    Err(e) => println!("Invalid UTF-8: {}", e),
}

String::from_utf8(vec)

拥有,转移所有权

  • 零拷贝转换:Rust 内部直接把 Vec 的堆缓冲区“重解释”为 String,不复制字节!
  • 获得所有权:返回的 String 可以长期使用、移动、修改。
  • 适用于:从 I/O 读取、加密解密、协议解析等场景,你已经拥有了字节数据,想把它变成字符串。
let bytes = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; // Vec<u8>,拥有数据

match String::from_utf8(bytes) {
    Ok(s) => println!("Got string: {}", s), // s: String(拥有数据)
    Err(e) => println!("Invalid UTF-8: {:?}", e.into_bytes()), // 可取回原始 Vec
}

String::from_utf8_lossy(byte_slice) -> Cow

有损转换:非法字节 → 替代
如果遇到非法 UTF-8 字节序列,不会 panic 或报错,而是用 Unicode 替换字符 (U+FFFD)代替。

let bytes = b"Hello \xFF\xFE World"; // 包含非法字节
let s = String::from_utf8_lossy(bytes);
println!("{}", s); // 输出: Hello �� World

String::from_utf8_unchecked(vec)

跳过检查!我保证它是 UTF-8

let s = String::from("Hello");
let bytes = s.into_bytes(); // 合法 UTF-8 字节
// 安全!因为 bytes 来自合法 String
let s2 = unsafe { String::from_utf8_unchecked(bytes) };

str::from_utf8_unchecked(vec)

跳过检查!我保证它是 UTF-8(只读版)

fn parse_known_utf8(data: &[u8]) -> &str {
    // 假设上层已验证 data 是 UTF-8
    unsafe { std::str::from_utf8_unchecked(data) }
}

区别

函数 安全性 输入 输出 是否验证 UTF-8 适用场景
String::from_utf8_lossy ✅ 安全 &[u8] Cow<str> ❌ 不验证,非法→ 处理不可信输入
String::from_utf8_unchecked ⚠️ unsafe Vec<u8> String ❌ 跳过验证 已知合法 + 高性能
str::from_utf8_unchecked ⚠️ unsafe &[u8] &str ❌ 跳过验证 已知合法 + 零拷贝

延迟分配Cow

  • 如果数据不需要修改 → 用 Cow::Borrowed(&T)(零拷贝)
  • 如果数据需要修改 → 用 Cow::Owned(T)(拥有所有权)
use std::borrow::Cow;

fn need_modify(name: &str) -> bool {
    if name.to_uppercase() == name {
        true
    } else {
        false
    }
}

fn process(name: &str) -> Cow<'_, str> {
    if need_modify(name) {
        Cow::Owned(name.to_uppercase()) // 需要时才分配
    } else {
        Cow::Borrowed(name) // 直接借用,零成本
    }
}
fn main() {
    // 使用示例
    let a = process("hello"); // Cow::Borrowed("hello") → 无分配
    let b = process("WORLD"); // Cow::Owned("world") → 有分配

    // 统一使用
    println!("{}", a); // 都能打印
    println!("{}", b);
}
posted @ 2026-08-10 10:13  lxd670  阅读(4)  评论(0)    收藏  举报