首页 > 其他分享 >【Rust自学】8.4. String类型 Pt.2:字节、标量值、字形簇以及字符串的各类操作

【Rust自学】8.4. String类型 Pt.2:字节、标量值、字形簇以及字符串的各类操作

时间:2024-12-30 10:29:28浏览次数:8  
标签:字节 字符串 字母 Pt.2 String let 标量 Rust 8.4

8.4.0. 本章内容

第八章主要讲的是Rust中常见的集合。Rust中提供了很多集合类型的数据结构,这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。

第八章中的集合是存储在堆内存上而非栈内存上的,这也意味着这些集合的数据大小无需在编译时就确定,在运行时它们可以动态地变大或变小。

本章主要会讲三种集合:Vector、String(本文) 和HashMap

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

8.4.1. 不能使用索引来访问String

Rust中的String不同于其他语言,不能用索引访问。如下例:

fn main() {  
    let s = String::from("6657 up up");  
    let a = s[0];  
}

输出:

error[E0277]: the type `str` cannot be indexed by `{integer}`
 --> src/main.rs:3:15
  |
3 |     let a = s[0];
  |               ^ string indices are ranges of `usize`
  |
  = help: the trait `SliceIndex<str>` is not implemented for `{integer}`, which is required by `String: Index<_>`
  = note: you can use `.chars().nth()` or `.bytes().nth()`
          for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
  = help: the trait `SliceIndex<[_]>` is implemented for `usize`
  = help: for that trait implementation, expected `[_]`, found `str`
  = note: required for `String` to implement `Index<{integer}>`

报的错是类型String无法使用整数来进行索引,继续往下看到=help这一行,这里提示了String这个类型没有实现index<{integer}>(index是索引的意思,integer是整数的意思)这个trait。

8.4.2. String类型的内部表示

String类型是对Vec<u8>的包装,u8也就是byte字节。我们可以通过String上的len()方法来返回字符串的长度。如下例:

fn main() {  
    let len = String::from("Niko").len();  
    println!("{}", len);  
}

输出:

4

这个字符串采用的是utf-8编码,len的值为4也就是这个字符串占了4个字节,所以在这个例子里面每个字母就占用了一个字节。

但情况并不总是这样,比如说我们把字符串换成其他语言(这里是西里尔字母写的俄语):

fn main() {  
    let hello = String::from("Здравствуйте");  
    println!("{}", hello.len());  
}

如果你数一下这个字符串有12个字母,但是输出却是:

24

也就是说在这个语言里面一个字母会占用两个字节(中文是一个汉字占三个字节),而所谓的字母用一个专业术语来表示就是Unicode标量值,而西里尔字母每个Unicode标量值都对应两个字节。

通过这个例子你可以发现,String的数字索引并不能总是对应道一个完整的Unicode标量值,因为有的Unicode标量值会占不止一个字节,而数字索引注定只能读取到一个字节的值。

再举个例子,西里尔语里的З(不是数字)这个字母对应的是两个字节,而这两个字节的值分别是是208和151。假如说数字索引是允许的,那么我取Здравствуйте的索引0的值就会是208,而208本身又是无意义的字符(因为缺少第二个字节组不成一个Unnicode标量值)。所以为了避免这种无法立即发现的bug,Rust封杀了数字索引String,也就是在开发的早期阶段杜绝可能的误解。

8.4.3. 字节、标量值、字形簇

Rust中有三种看待字符串的方式:字节(Bytes)、标量值(Scalar Values)和字形簇(Grapheme Clusters)。其中字形簇是最接近我们说说的字母的概念的。

1. 字节

看个例子:

fn main() {  
    let s = String::from("नमस्ते");  //梵文书写的印度语
    for b in s.bytes() {  
        print!("{} ", b);  
    }  
}

这个梵文看起来好像有4个字母组成,我们使.bytes()这个方法来获得它所对应的字节,输出如下:

224 164 168 224 164 174 224 164 184 224 165 141 224 164 164 224 165 135

这里的18个字节就是计算机存储字符串的样子

2. 标量值

我们再来以Unicode标量值的形式来看待它:

fn main() {  
    let s = String::from("नमस्ते");  
    for b in s.chars() {  
        print!("{} ", b);  
    }  
}

使用.chars()方法能够获得这段字符串所对应的标量值,输出如下:

न म स ् त े 

它有4个实际的字母,而第四个和第六个标量值代表的是音标,单独存在没有任何意义,得于前面的东西放在一起算是一个字母。

这里也解释了为什么这个梵文实际上有18个字节,因为一个梵文占3个字节,这段字符串加上隐藏着的音标一共6个字符,把这两个数字相乘可以得到18这个数字,也就是18个字节。

3. 字形簇

因为从String里获得字形簇很复杂,所以Rust标准库没有提供这个功能,这里也就不做演示,但是可以去crate.io找第三方的库来实现这个功能。

总之,这串梵文如果以字形簇的格式打印出来会是:
请添加图片描述
这个样子。

8.4.4. 不能使用索引来访问String的原因

  • 数字索引取出来值的可能并不完整,无法组成一个Unicode标量值,导致无法第一时间察觉的错误
  • 索引操作会消耗一个常量时间,也就是O(1),而String无法保证这个时间,因为它需要从头到尾遍历所有内容从而确定有多少个合法的字符。

8.4.5. 切割String

可以使用[],在里面填上范围来创建字符串切片(关于字符串切片的详细内容在4.5. 切片(Slice),这里不再赘述)。如下例:

fn main() {  
    let hello = String::from("Здравствуйте");  
    let s = &hello[0..4];  
    println!("{}", s);  
}

刚才也说了一个西里尔字母占两个字节,这里的字符串切片切的是字符串的前4个字节,也就是前两个字母,看一下输出:

Зд

那如果字符串切片切的是2前三个字节呢?也就意味着切片的内容会是第一个字母加上半个第二个字母,这种情况会怎么样呢?看下面的例子:

fn main() {  
    let hello = String::from("Здравствуйте");  
    let s = &hello[0..3];  
    println!("{}", s);  
}

输出:

byte index 3 is not a char boundary; it is inside 'д' (bytes 2..4) of `Здравствуйте`

程序触发了panic!,错误信息是:索引3不是一个char边界。也就是说在切割的时候必须沿着char的边界来切割,对于这个西里尔语言来说就是2个2个字节地切割。

8.4.6. 遍历String

  • 对于标量值,使用.chars()方法。如下例:
fn main() {  
    let s = String::from("नमस्ते");  
    for b in s.chars() {  
        print!("{} ", b);  
    }  
}
  • 对于字节,使用.bytes()方法。如下例:
fn main() {  
    let s = String::from("नमस्ते");
    for b in s.bytes() {  
        print!("{} ", b);  
    }  
}
  • 对于字形簇,标准库未提供方法,但是可以找第三方库。

标签:字节,字符串,字母,Pt.2,String,let,标量,Rust,8.4
From: https://blog.csdn.net/weixin_71793197/article/details/144814535

相关文章

  • 【Rust自学】5.2. struct使用例(加打印调试信息)
    对不起我都写到第8章了才发现我忘记发这一篇了,现在补上,不过这可能导致专栏的文章顺序有一点问题,但也只能将就着了。喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)5.2.1.例子需求创建一个函数,计算长方形的面积,长......
  • Rust中以下三个东西是什么?有什么用? #[automatically_derived] #[allow(clippy::ptr_ar
    Rust中以下三个东西是什么?有什么用?#[automatically_derived]#[allow(clippy::ptr_arg)]#[rustfmt::skip]gpttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt在Rust中,以下三个属性分别有不同的用途:1.#[automatically_derived]作用:这个属性表示该代码是由......
  • 你根据这个写一个完整的能运行的结合logos和lalrpop的Rust示例程序,并且要求有AST部分
    gpt好的,下面是一个结合logos和lalrpop的完整Rust示例,展示了如何使用logos编写词法分析器(lexer),然后用lalrpop来解析语法,并生成AST(抽象语法树)。这个示例将包括以下几个部分:logos词法分析器:用来从输入文本中生成tokens。lalrpop语法分析器:用来解析这些tokens,生成......
  • Rust和C/C++相关调用总结
    一.Windows下Rust与C/C++互相调用1.C/C++调用rust1.1动态库调用1.1.1以LoadLibrary方式显示调用add.rs#[no_mangle]//防止Rust修改函数名pubextern"C"fnhello_world(){println!("HellofromRust!");}#[no_mangle]pubextern"C"fnadd(a:i32,b:i3......
  • rust的几种闭包类型
    前提知识:rust里面有move,copy,clone。所有对象都有一个类型,具体所有权。比如#[derive(Debug)]structComplex{real:f64,imag:f64,}fnmain(){leta=Complex{real:1.,imag:2.};letb=a;println!("{:?}",a);println!("{:?}",b);}会......
  • rust学习十五.1、智能指针基本概念
    本文没有什么需要特别详细阐述的内容,基本都是一些基础性的概念和一些空洞的定义。一、基本概念指针-拥有一个指向一个堆数据的地址的变量。本身是变量,但其数据就是一个地址。智能指针-一种特别的指针(也是变量),除了指向数据的地址,通常还具有元数据和其它功能。智能指针通常使......
  • 详细讲解一下Rust中package、crate、module的概念
    在Rust中,package、crate和module是三个层次不同但又相互关联的概念,它们共同组成了Rust的代码组织和管理体系。以下是它们的详细介绍:1.Package(包)定义:一个package是一个由Cargo(Rust的构建工具和包管理器)管理的项目,包含一个或多个crate。核心文件:每个package至少......
  • rust-analyzer 引入含有openssl包报错(openssl未找到)问题解决方案
    1.下载openssl编译后的包https://slproweb.com/products/Win32OpenSSL.html选择完全包2.安装注意下面这一步把dll安装到/bin所在的同级目录一路回车,最后的捐款可以不选3.设置环境变量经过实验,主要的环境变量有3个OPENSSL_DIR="C:\ProgramFiles\OpenSSL-Win64"这......
  • 用rust写了一个桌面小游戏,希望大家体验
    使用rust的游戏框架Bevy写了一个桌面游戏《推十点半》。地址https://github.com/davelet/poker-of-ten-half/releases/tag/release-0.1。没有打包,因为还在初期,只是能简单玩。所以需要下载到本地通过cargorun来体验。之前用fltk-rs写的json处理工具也希望大家体验:https:/......
  • 看下面这个Rust程序,我想知道 other_error => panic!("Problem opening the file: {:?}
    看下面这个Rust程序,我想知道other_error=>panic!("Problemopeningthefile:{:?}",other_error)这一行代码,为什么是other_error=>panic...而不是_=>panic...?usestd::fs::File;usestd::io::ErrorKind;fnmain(){letf=File::open("hello.txt&qu......