16_rust的字符串
rust的字符串
字符串简介
字符串:UTF-8格式,byte的集合,提供了一些解析byte为文本的方法。
rust的核心语言层面,只有一个字符串类型:字符串切片str(或&str)。
字符串切片:对存储在其他地方、UTF-8编码的字符串的引用
其中字符串字面值,存储在二进制文件中,也是字符串切片。
String类型:
- 来自标准库,而不是核心语言。
- 可增长、可修改、可拥有。
- 也是UTF-8编码。
通常说的字符串是指:String和&str两种类型。他们都在标准库里非常常用,都是UTF-8编码。
其它类型的字符串
- rust的标准库还包括很多其它字符串类型,如OsString、OsStr、CString、Cstr。
- String后缀的通常拥有所有权,以Str后缀通常表示借用的变体。
- 也提供了可存储不同编码的文本或在内存中以不同形式展现的库。
Library crate针对存储字符串可提供更多的选项。
创建字符串(String)
String是byte的集合,所以很多Vec<T>
的操作都可用于String。
- String::new()函数创建空的String类型。
let mut s = String::new();
- 使用初始值创建String,
- to_string方法可用于Display trait的类型,包括字符串字面值。
- String::from()函数,从字面值创建String。
let data = "test";
let s = data.to_string();
let s1 = "test".to_string();
let s2 = String::from("test");
因为是UTF-8编码,可以存储多种语言。
更新String
push_str
方法:把一个字符串切片附加到String。
let mut s = String::from("test ");
s.push_str("func"); // 参数是&Str类型,获得字符串切片,无需获得所有权
let s1 = String::from("func");
s.push_str(&s1);
- push()方法:把单个字符附加到String。
let mut s = String::from("test ");
s.push("t");
+
号连接字符串(拼接),使用了类似签名方法fn add(self, s:&Str)->String{...};标准库中的add方法使用了泛型,只能把&str添加到String,解引用强制转换(deref coercion)。
let s1 = String::from("s1 +");
let s2 = String::from(" s2");
let s3 = s1 + &s2; // 第一个s1转移所有权,第二个得是引用(字符串切片),不获得所有权
// 相加的过程,相当于调用add函数,首先获得第一个参数s1的所有权,然后添加s2的切片内容,再把所有权转移给s3
println!("{}", s3);
println!("{}", s1);// 因为在相加的时候失去了所有权,所以编译报错
println!("{}", s2);
format!
宏连接多个字符串,样式与println!类似,输出目的地不同。不会获取参数的所有权。
let s1 = String::from("s1");
let s2 = String::from("s2");
let s3 = String::from("s3");
let s4 = s1 + "-" + &s2 + "-" + &s3;
let s5 = format!("{}-{}-{}", s1, s2, s3); // 不会获取参数的所有权,这些参数在后续依然可使用
访问String
对String按索引的形式进行访问:按索引语法访问String某部分会报错。rust的字符串不支持索引语法访问。
let s1 = String::from("s1test");
let c = s1[2]; // 编译报错 the type `str` cannot be indexed by `{integer}`
内部表示
String是对Vec<u8>
的包装,.len()
方法可返回string所占的字节数。
let len = Stirng::from("test").len(); // 这里len=4
let len = Stirng::from("你好").len(); // 这里也是len=6
// 因为是UTF-8编码,每个字符单元是一个Unicode 标量值(通常占用两个字节,中文三个字节)
// 如果使用索引去访问,比如
let s1 = "你好";
let c = s1[1];
// 因为一个字占两个字节,如果按照索引访问s[1],显然获得值是没有意义的,并不代表一个字。
为了防止上述索引访问引起的歧义和错误,rust语言直接禁止这种访问方式,防止运行未知错误。
字节、标量值、字形簇
Bytes、Scalar Values、Grapheme Clusters
rust有三种看待字符串的方式:字节bytes、标量值、字形簇(最接近所谓的“字母”)。
let s1 = "test";
for b in s1.bytes() {
println!("{}", b);
}
for b in s1.chars() { //chars对应unicode标量值
println!("{}", b);
}
rust不允许对String进行索引的最后一个原因:索引操作消耗一个常量时间O(1),但String无法保证,需要遍历所有内容来确定有多少个合法的字符。
切割String
可用[]
和一个范围
来创建字符串的切片。必须谨慎使用,因切割是必须按照Unicode标量值的整数倍切割(2*n),如果切割时跨越了字符边界,程序会panic。
let s1 = "你好";
let s2 = &s1[0..3]; // 正确
let s = &s1[0...2]; // 编译报错panicked at 'byte index 2 is not a char boundary
遍历String的方法
对于标量值:chars()方法遍历
对于字节:bytes()方法遍历
对于字形簇:很复杂,标准库未提供方法,需要借助第三方库。
UTF-8编码的好处是可防止开发后期处理非ASCII字符的错误。