16_rust的字符串

发布时间 2023-10-25 01:40:12作者: UFOFCZ

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]; // 编译报错

内部表示

String是对Vec<u8>的包装,.len()方法可返回string所占的字节数。

let len = Stirng::from("test").len(); // 这里len=4
let len = Stirng::from("你好").len(); // 这里也是len=4
// 因为是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 s = &s1[0...1];
let s = &s1[0...3]; // 编译报错

遍历String的方法

对于标量值:chars()方法遍历
对于字节:bytes()方法遍历
对于字形簇:很复杂,标准库未提供方法,需要借助第三方库。

UTF-8编码的好处是可防止开发后期处理非ASCII字符的错误。