Rust 字符串

介绍

Rust 中有多种表示字符串的数据类型,其中最常用的是 str 和 String 两种类型。

str 类型

Rust 中有一个表示字符串的原始(primitive)类型 str。str 是字符串切片(slice),每个字符串切片具有固定的大小并且是不可变的。通常不能直接访问 str ,因为切片属于动态大小类型(DST)。所以,只能通过引用(&str)间接访问字符串切片。关于这一点,会在以后的文章中介绍。在下面的内容将不加区分的使用 str 和 &str。

可以通过字符串字面量构造 &str 类型的对象:

let s = "Hello, world!";

在 Rust 中,字符串字面量的类型是 & 'static str,因为它是被直接储存在编译后的二进制文件中的。

还可以使用切片的语法,从一个&str 对象构造出另一个 &str 对象:

let ss = &s[..3];

也可以将切片转换成相应的指针类型:

let p = s as \*const str;

String 类型

像大部分常见的编程语言一样,String 是一个分配在堆上的可增长的字符串类型,它的定义如下:

struct String {
vec: Vec<u8>
}

从源码可以看出,String 是对 Vec 的简单包装。

String 保存的总是有效的 UTF-8 编码的字节序列。

构造一个空字符串:

let s = String::new();

还可以通过字符串字面量构造 String 类型的对象:

let hello = String::from("Hello, world!"); String 和 &str 之间有着非常紧密的关系,后者可以用来表示前者的被借用(Borrowed)的副本。

str 和 String 类型的转换

前面已经看到,字符串字面量可以转换成 String。反过来,String 也可以转换成 str。这是通过解引用操作实现的:

impl Deref for String {
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

利用解引用操作就可以将 String 转换成 str:

let s: String = String::from("Hello");
let ss: &str = &s;

String 还可以连接一个 str 字符串:

let s = String::from("Hello");
let b = ", world!";
let f = s + b; // f == "Hello, world!"

如果要连接两个 String 对象,不能简单地直接相加。必须先通过解引用将后一个对象转换为 &str 才能进行连接:

let s = String::from("Hello");
let b = String::from(", world!");
let f = s + &b; // f == "Hello, world!"

注意这里字符串连接之后,s 的所有权发生了转移,而 b 的内容复制到了新的字符串中。

从 String 到 str 的转换是廉价的,反之,从 str 转为 String 需要分配新的内存。

一般来说,当定义函数的参数时, &str 会比 String 更加通用:因为此时既可以传递 &str 对象也可以传递 String 对象。

各种编码转化

&str    -> String--| String::from(s) or s.to_string() or s.to_owned()
&str    -> &[u8]---| s.as_bytes()
&str    -> Vec<u8>-| s.as_bytes().to_vec() or s.as_bytes().to_owned()
String  -> &str----| &s if possible* else s.as_str()
String  -> &[u8]---| s.as_bytes()
String  -> Vec<u8>-| s.into_bytes()
&[u8]   -> &str----| s.to_vec() or s.to_owned()
&[u8]   -> String--| std::str::from_utf8(s).unwrap(), but don't**
&[u8]   -> Vec<u8>-| String::from_utf8(s).unwrap(), but don't**
Vec<u8> -> &str----| &s if possible* else s.as_slice()
Vec<u8> -> String--| std::str::from_utf8(&s).unwrap(), but don't**
Vec<u8> -> &[u8]---| String::from_utf8(s).unwrap(), but don't**

字符串转整数

 fn main() {
    let my_string = "27".to_string();  // `parse()` works with `&str` and `String`!
    let my_int = my_string.parse::<i32>().unwrap();
    println!("{:?}", my_string);
    println!("{:?}", my_int);
}

包含字符串

 fn main() {
     let a="abcd";
    println!("{:?}", a.contains("bc"));
 }

计算字节长度

 fn main() {
     let s1 = "中国-China";
     println!("{:?}", s1.len());   // -> 12
     let s2 = String::from("中国-China");
     println!("{:?}", s2.len());   // -> 12
 }

计算字符个数

 fn main() {
     let s1 = "中国-China";
     println!("{:?}", s1.chars().count());  // -> 8
     let s2 = String::from("中国-China");
     println!("{:?}", s2.chars().count());   // -> 8
 }

截取指定开始的 n 个的字符

fn substr(s: &str, start: usize, length: usize) -> String {
     s.chars().skip(start).take(length).collect()
 }
 fn main() {
     let s1 = "中国-China";
     println!("{:?}", substr(s1, 1, 100));
     println!("{:?}", substr(s1, 0, 2));

     let s2 = String::from("中国-China");
     println!("{:?}", substr(&s2, 1, 100));
     println!("{:?}", substr(&s2, 0, 2));
 }

获取指定位置开始的 n 个字节(如果存在非法的字符边界,则返回 None)

 fn main() {
     let mut s = String::from("中国-China");
     println!("{:?}", s.get(0..=5)); // -> Some("中国")
     println!("{:?}", s.get(0..=4)); // -> None
 }

判断是不是包含某个子串

fn main() {
     let s1 = "中国-China";
     let s2 = String::from("中国-China");
     assert_eq!(true, s1.contains("中国"));
     assert_eq!(true, s2.contains("中国"));
 }

判断是不是以某个字符串开头

 fn main() {
     let s1 = "中国-China";
     let s2 = String::from("中国-China");
     assert_eq!(true, s1.starts_with("中国"));
     assert_eq!(true, s2.starts_with("中国"));
 }

判断是不是以某个字符串结尾

fn main() {
     let s1 = "中国-China";
     let s2 = String::from("中国-China");
     assert_eq!(true, s1.ends_with("China"));
     assert_eq!(true, s2.ends_with("China"));
 }

全部转为大写

fn main() {
     let s1 = "中国-China";
     let s2 = String::from("中国-China");
     println!("{:?}", s1.to_uppercase()); // -> 中国-CHINA
     println!("{:?}", s2.to_uppercase()); // -> 中国-CHINA

     //请注意与  to_uppercase() 的不同
     let mut s3 = String::from("中国-China");
     s3.make_ascii_uppercase();
     println!("{:?}", s3);   // -> 中国-CHINA
 }

全部转为小写

fn main() {
     let s1 = "中国-China";
     let s2 = String::from("中国-China");
     println!("{:?}", s1.to_lowercase()); // -> 中国-china
     println!("{:?}", s2.to_lowercase()); // -> 中国-china

     //请注意与  to_lowercase() 的不同
     let mut s3 = String::from("中国-China");
     s3.make_ascii_lowercase();
     println!("{:?}", s3);   // -> 中国-china
 }

判断是不是 ASCII 字符串

fn main() {
     let s1 = "中国-China";
     let s2 = "China";
     assert_eq!(false, s1.is_ascii());
     assert_eq!(true, s2.is_ascii());
 }

判断指定位置是不是一个合法的 UTF-8 边界,比如一个汉字的 UTF-8 包含三个字节,那么第一个字节的结束位置必然不是一个合法的 UTF-8 边界(更准确的说,应该是这个位置是不是一个合法字符的开始)

fn main() {
     let mut s = String::from("中国-China");
     assert_eq!(true, s.is_char_boundary(0));
     assert_eq!(true, s.is_char_boundary(12));
     assert_eq!(false, s.is_char_boundary(2));
     assert_eq!(true, s.is_char_boundary(3));
 }

字符串替换

 fn main() {
     let mut s = String::from("中国-China");
     println!("{:?}", s.replace("中国", "China"));
 }

字符串切割

fn main() {
     let mut s = String::from("中国-China");
     let result: Vec<&str> = s.split("-").collect();
     println!("{:?}", result);  // -> ["中国", "China"]
 }

通过分离: s.split("separator") 通过空白: s.split_whitespace() 通过换行符: s.lines()

trim

fn main() {
    let s = " Hello\tworld\t";
    assert_eq!("Hello\tworld", s.trim());
}

trim_left

fn main() {
    let s = " Hello\tworld\t";
    assert_eq!("Hello\tworld\t", s.trim_left());
}

trim_right

fn main() {
    let s = " Hello\tworld\t";
    assert_eq!(" Hello\tworld", s.trim_right());
}

trim_matches

fn main() {
    assert_eq!("11foo1bar11".trim_matches('1'), "foo1bar");
    assert_eq!("123foo1bar123".trim_matches(char::is_numeric), "foo1bar");

    let x: &[_] = &['1', '2'];
    assert_eq!("12foo1bar12".trim_matches(x), "foo1bar");
}

trim_left_matches

fn main() {
    assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11");
    assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123");

    let x: &[_] = &['1', '2'];
    assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12");
}

trim_right_matches

fn main() {
    assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar");
    assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar");

    let x: &[_] = &['1', '2'];
    assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar");

    assert_eq!("1fooX".trim_left_matches(|c| c == '1' || c == 'X'), "fooX");
}

其他

fn main() {
    let a_string = "123foo1bar123".to_string();
    let a_str = "123foo1bar123";
    let b_string = a_string.replace("123", "");
    let b_str = a_str.replace("123", "");
    println!("b_string:{:?} b_str:{:?}", b_string, b_str);
}