0x001
字符串在rust中是一个对象特殊类型,可以理解为char的集合。
rust在核心语言中只有一种String类型,通过borrow的方式&str查看str。
string类型由rust的标准库提供,而不是编码为核心语言,是一种可增长、可变、拥有的、UTF-8编码的string类型。当rustaceans指的是rust中的“string”时,通常指的是string和string slice&str类型,而不仅仅是这些类型中的一种。尽管这一部分主要是关于string的,但这两种类型在rust的标准库中都被大量使用,并且string和string slice都是UTF-8编码的。
rust的标准库还包括许多其他string类型,例如osstring、osstr、cstring和cstr。可以为存储string提供更多的选项。这些类型指的是拥有和借用的变体,和string和str类型一样。例如,这些字符串可以以不同的编码存储文本,或者以不同的方式在内存中表示。
0x002 构造
字符串创建一般有三个方法
fn main(){
let s = String::from("hello 世界”);
let mut s1 = String::new();
s1.push_str("hello 世界);
//s1+="hello 世界"; //与上面的语句等价,即:追加字符串在后面
let s2 = "hello 世界".to_string();
println!("{}", s);
println!("{}", s1);
println!("{}", s2);
}
输出的结果都是一样的:
hello 世界
hello 世界
hello 世界
以上三种方法分别调用from函数,new函数以及to_string函数。
其中,from函数和to_string函数函数功能是等价的,只是调用的对象不同而已,作用都是从一个字符串字面量直接构造出一个String来。而new函数则是凭空生产一个String,而且是空的,如果想要让它存值,就得将它声明为可变的(mut),之后可以用push_str函数或者+=操作符来追加字符串。
除此之外还有一个函数with_capacity:
let s = String::with_capacity(100);
它的作用与new基本相同,唯一不同的是,这个函数需要一个参数来初始化其内部的内存大小,如果你事先知道自己需要多大的内存,那么建议你使用这个函数来构造一个String而不是new。
0x003 遍历
对字符串的遍历,用到两个函数:as_bytes与chars
1.遍历字符串的所有字节
fn main(){
let s = String::from("hello 世界");
for i in s.as_bytes(){
println!("{}",i);
}
}
输出结果:
104 101 108 108 111 32 228 184 150 231 149 140
hello分别对应前面的104 101 108 111 ,中间空格对应的是32,”世界“对应的是228 184 150 和231 149 140
2.使用chars函数遍历字符串
fn main(){
let s =Strig::from("hello 世界");
for i in s.chars(){
println!("{}",i);
}
}
输出结果
h e l l o 世 界
如果想要取出第几个字符,使用函数nth:
fn main(){
let s = String::from("hello 世界");
let mut m = s.chars();
let c = m.nth(7);
if let Some(t) = c { //取出返回值中携带的字符
println!("{}",t);
}
}
输出结果: 界
//更简洁的写法
fn main(){
let s = String::from("hello 世界");
let c = s.chars().nth(7);
if let Some(t) = c {
println!("{}", t);
}
}
还能更简洁:
fn main(){
let s = String::from("hello 世界");
let c = s.chars().nth(7).unwrap();
println!("{}", c);
}
unwrap函数是option这个trait的一个函数,等价于:
fn main(){
let s = String::from("hello 世界");
let c = s.chars().nth(7);
let r = match c {
Some(t) => t,
None => panic!("")
};
println!("{}", r);
}
0x004 容量操作
字符串为实现内容可变,必须要预留一定的空间,为此rust提供了一些函数。
- reserve():为字符串新增一些空间
- shrink_to_fit():将字符串的容量缩小到它所包含的内容所需要的最小值。
- shrink_to():将字符串的容量缩小到指定下限。如果当下容量小于下线,或者大于当前值,则什么也不做。
fn main(){
let mut s = "micro cold".to_string();
println!("original capacity = {}", s.capacity());
s.reserve(10);
println!("after reserve = {}", s.capacity());
s.shrink_to_fit();
println!("fit capacity = {}", s.capacity());
s.shrink_to(25);
println!("fit to 25 = {}", s.capacity());
}
//输出结果
original capacity = 10
after reserve = 20
fit capacity = 10
fit to 25 = 10
0x005 增删改查
String是可变字符串,所有提供了增加,删除,插入等操作
类别 | 方法 |
弹出与追加 | 追加字符push;追加字符串push_str;弹出最后一个字符pop |
删除 | 清空clear;截短truncate;删除某位置remove;匹配删除remove_matches |
插入 | 插入字符insert;插入字符串insert_str |
替换 | 匹配替换replace;位置替换replace_range; |
匹配 | 从左查找find;从右查找rfind;均返回第一个匹配的位置 |
上述方法进行测试:
fn main(){
let mut s = "micro cold".to_string();
s.push("a");
println!("push("a") -> {}", s);
s.push_str("bcd");
println!("push_str("\bcd\") -> {}", s);
s.truncate(10);
println!("truncate(3) -> {}", s);
s.remove(1);
println!("remove(1) -> {}", s);
s.insert(1, 'i');
println!("insert(1, 'i') -> {}"), s);
s.insert_str(0, "hello");
println!("insert_str(0, \"hello\" -> {}", s);
s.replace_range(0..5,"Hola");
println!("replace_range(0..5, \"Hola\" -> {}", s);
println!("s.replace(\"Hola\",\"你好\") -> {}", s.replace("Hola", "你好"));
}
//测试结果
push('a') -> micro colda
push_str("bcd") -> micro coldabcd
truncate(3) -> micro cold
remove(1) -> mcro cold
insert(1, 'i') -> micro cold
insert_str(0, "hello ") -> hello micro cold
replace_range(0..5, "Hola") -> Hola micro cold
s.replace("Hola", "你好") -> 你好 micro cold
0x006 字符串切片
字符串切片:指向String对象某个连续部分的引用。
切片的类型为:&str
使用形式:[starting_index..ending_index],其中,starting_index是切片起始位置的索引值,ending_index是切片终止位置的下一个位置的索引值。
注意:字符串字符串切片的范围索引必须发生在有效的UTF-8字符边界内,如果尝试从一个多字节中创建字符串切片,程序会报错并退出。
字符串字面量就是切片
let s = "Hello world";
变量s 的类型就是&str:它是一个指向二进制程序特定位置的切片。因为&str是一个不可变的引用,所有字符串字面量是不可变的。
fn main(){
let s = String::from("hello world");
let hello = &s[0..5];//如果要从索引0开始,可以不写两个点号之前的值 let hello =&s[..5];
let world = &s[6..11];//如果slice包含String的最后一个字节,也可以舍弃尾部的数字 let world =&s[6..];
println!("{},{}",hello, world)
//也可以同时舍弃这两个值来获取整个字符串的slice
let s1 = &s[..];
//等价于
let len = s.len()
let s1 = &s[0..len];
}
0x007 String和str类型的转换
字符串字面量可以转换为String。相反,String也可以转换成str。
通过解引用操作实现。
impl Deref for String{
fn deref(&self) -> &str{
unsafe{ str::from_utf_unchecked(&self.vec) }
}
}
利用解引用操作就可以将String转换成str:
let s: String = String::from("Hello");
let ss: &str = &s;
连接一个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对象。
字符串转整数
fn main(){
let my_string = "27".to_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";//let s1 = String::from("中国-China");
println!(":?}",substr(s1,1,100));
println!({:?}",substr(s1,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 s1 = String::from("中国-China");
assert_eq!(true, s1.ends_with("China"));
}
全部转为大写或小写
fn main(){
let s1 = "中国-China"; // let s1 = String::from("中国-China");
println!("{:?}",s1.to_uppercase()); //中国-CHINA
//与to_uppercase()的不同
let mut s2 = String::from("中国-China");
s2.make_ascii_uppercase();
println!("{:?}", s2); //中国-CHINA
}
//全部转换为小写
fn main(){
let s1 = "中国-China"; // let s1 = String::from("中国-China");
println!("{:?}",s1.to_lowercase()); //中国-china
//与to_lowercase()的不同
let mut s2 = String::from("中国-China");
s2.make_ascii_lowercase();
println!("{:?}", s2); //中国-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 s1 = String::from("中国-China");
assert_eq!(true, s1.is_char_boundary(0));
assert_eq!(true, s1.is_char_boundary(12));
assert_eq!(false, s1.is_char_boundary(2));
assert_eq!(true, s1.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类有六个函数,trim、trim_left、trim_right、trim_matches、trim_left_matches、trim_right_matches
//trim
let s = "Hello\tword\t";
assert_eq!("Hello\tword\", s.trim());
//trim_left
let s = "Hello\tword\t";
assert_eq!("Hello\tword\", s.trim_left());
//trim_right
let s = "Hello\tword\t";
assert_eq!("Hello\tword\", s.trim_right());
//trim_matches
assert_eq!("11foolbar11".trim_matches('1'),"foolbar");
assert_eq!("123foolbar123".trim_matches(char::is_numeric), "foolbar");
let x: &[_] = &['1', '2'];
assert_eq!("12foolbar12".trim_matches(x), "foolbar");
//trim_left_matches
assert_eq!("11foolbar11".trim_letf_matches('1'),"foolbar11");
assert_eq!("123foolbar123".trim_left_matches(char::is_numeric), "foolbar123");
let x: &[_] = &['1', '2'];
assert_eq!("12foolbar12".trim_matches(x), "foolbar12");
assert_eq!("1fooX".trim_left_matches(|c| == '1' || c == 'X'), "fooX");
//trim_right_matches
assert_eq!("11foolbar11".trim_right_matches('1'),"11foolbar");
assert_eq!("123foolbar123".trim_right_matches(char::is_numeric), "123foolbar");
let x: &[_] = &['1', '2'];
assert_eq!("12foolbar12".trim_right_matches(x), "12foolbar");