本文简介
第8章 字符串与集合
程序中最常用的三大数据结构是字符串、数组和射。
字符串和数组、映射的区别在于,字符串是被作为一个整体来关注和使用的; 数组和映射关注最多的是其中的元素及它们之间的关系。所以,数组和映射也被称为集合类型。
8.1 字符串
- 字符和计算机底层一串
0
、1
的一一映射关系就叫作字符编码(Character Encoding) 。 Unicode
字符集是通用的多字节编码字符集集。Unicode 字符集相当于一张表,其中包含了世界上所有语言中可能出现的字符,每个字符对应一个非负整数,该数字称为码点(Code Point) 。这个码点也分为不同的类型,包括标量值(Scala Value) 、代理对码点、非字符码点、保留码点和私有码点。- 标量值最常用,它是指实际存在对应字符的
Unicode
码位 Unicode
只规定字符对应的码点,没有指定如何存储Unicode
字符集解决了字符通用的问题,但是必须寻求另外一种存储方式以更加节约流量和硬盘空间。这种存储方式就是码元(Code Unit)组成的序列- 码元是指用于处理和交换编码文本的最小比特组合。比如计算机处理字符的最小单位1字节就是一个码元。通过将Unicode标量值和码元序列建立一一映射关系,就构成了编码表。
- 在
Unicode
中一共有三种这样的字符编码表:UTF-8、UTF-16 UTF-32,它们正好对应了1字节、2字节和4字节的码元。 - 但是多字节码元有字节序的问题,而UTF-8一个码元只有一个字节,不存在字节序可以直接储存
- UTF-8编码规则大致如下:
- 当一个字符在ASCII码的范围(兼容ASCII码)时,就用1字节表示
- 当一个字符占用了n个字节时,第一字节的前n个设置为1,第
n+1
位设置为0,后面字节的前两位设置为10
- 将
Unicode
字符转换为字节序列的过程称为编码;把编码字节序转为字符的过程为解码
8.2 字符
- Rust使用char类型表示单个字符。char类型使用整数值与Unicode标量值一一对应
- Rust用单引号来定义字符,使用双引号定义的是字符串字面量。
- 在Rust中每个char类型的字符都代表一个有效的u32类型的整数
- 为了能存储任何Unicode标量值,Rust规定每个字符都占4个字节
方法 | 说明 |
---|---|
is_digit(16) | 用于判断给定字符是否属于十六进制形式。如果参数为10,则判断是否为十进制形式 |
to_digit(16) | 用于将给定字符转换为十六进制形式。如果参数为10,则将给定字符转换为十进制形式 |
is_lowercase | 用于判断给定字符是否为小写的。作用于 Unicode 字符集中具有Lowercase属性的字符 |
is_uppercase | 用于判断给定字符是否为大写的。作用于 Unicode 字符集中具有Uppercase属性的字符 |
to_lowercase | 用于将给定字符转换为小写的。作用于Unicode字符集中具有Lowercase属性的字符 |
to_uppercase | 用于将给定字符转换为大写的。作用于Unicode字符集中具有Uppercase属性的字符 |
is_whitespace | 用于判断给定字符(或十六进制形式的码点)是否为空格字符 |
is_alphabetic | 用于判断给定字符是否为字母。汉字也算是字母 |
is_alphanumeric | 用于判断给定字符是否为字母、数字 |
is_control | 用于判断给定字符是否为控制符 |
is_numeric | 用于判断给定字符是否为数字 |
escape_default | 用于转义\t 、\r 、\n 、单引号、双引号、反斜杠等特殊符号 |
8.3 字符串分类
类别 | 说明 |
---|---|
str | 表示固定长度的字符串 |
String | 表示可增长的字符串 |
CStr | 表示由C分配而被Rust借用的字符串,一般用于和C语言交互 |
CString | 表示由Rust分配且可以传递给C函数使用的C字符串,同样用于和C语言交互 |
OsStr | 表示和操作系统相关的字符串。这是为了兼容Windows系统 |
OsString | 表示OsStr的可变版本。与Rust字符串可以相互转换 |
Path | 表示路径,定义于std::path模块中。Path包装了OsStr |
PathBuf | 跟Path配对,是Path的可变版本。PathBuf包装了OsString |
创建字符串的各种方法:
let string = String::new();
let string = String::from("hello rust");
let string = String::with_capacity(20);
let str = "hello";
let string = str.chars().filter(|c| !c.is_whitespace()).collect();
let string = str.to_owned();
let string = str.to_string();
8.1.4 字符串的两种处理方式
在Rust中对字符串的操作大致分为两种方式:按字节处理和按字符处理 。
let str = "boo虎foo"
- 如何得到字符串的长度?长度是多少?长度的单位是___数
- 如何将其转换为字符
char
向量,它的长度是多少? - 如何将其转换为字符
bytes
迭代器?
- 字符串为什么不能按索引访问其中字符?
let mut v = String::from("boo虎foo");
- 如何访问第0个字符?
- 如何访问”虎”这个字符?
v.get_mut(5..)
的结果是什么?- 如何判断字符”虎”的边界是3?
let s = "hello,我的世界";
- 如何分割成中英文两个子串?
- 在索引7处分割可以吗?
8.1.5 字符串的修改
修改字符串,则使用String类型。修改字符串大致分为追加、插入、连接、更新和删除5种情形
- 追加
let mut hello = String::from("Hello, ");
- 如何在
hello
后追加一个字符R
- 如何追加字符串
ust!
- 如何通过
Extend
迭代器追加字符串
- 为什么
hello
可以通过extend
追加字符串 - 如何通过迭代器追加
[',', 'r', 'u']
数组 - 如何通过迭代器追加
"st "
字符串 - 如何通过迭代器追加
"w o r l d"
去除空白字符的字符串
- 如何在
- 插入字符串
let mut s = String::with_capacity(3);
- 如何在索引0处插入
'f'
字符 - 如何在索引2处插入
'o'
字符(对吗?) - 如何在索引0处插入
"bar"
字符串 - 索引的单位是_____
insert
内部是基于is_char_boundary
来判断插入位置的合法性的
- 如何在索引0处插入
- 连接字符串
let left = "abc".to_string(); let right = "def".to_string();
- 如何把
left
和right
连接起来
- 如何把
- 更新字符串
let s = String::from("foobarr");
- 如何把偶数位改成大写?
- 删除字符串
let mut s = String::from("He虎llo");
- 如何删除”虎”字?
- 如何删除最后一个字符?
- 如何删除虎后面的所有字符?
- 如何清除整个字符串?
let mut s = String::from("老虎 is tiger, 山羊 is sheep");
drain
的作用:移除指定范围内的字符串?- 如何用
drain
把字符串分成两个部分?
8.1.6 字符串的查找
rust 可以用字符匹配的方法来查找
分类 | 方法 |
---|---|
存在性判断 | contains、starts_with、ends_with |
位置匹配 | find、rfind |
分割字符串 | split、rsplit、split_terminator、rsplit_terminator、splitn、rsplitn |
捕获匹配 | matches、rmatches、match_indices、rmatch_indices |
删除匹配 | trim_matches、trim_left_matches、trim_right_matches |
替代匹配 | replace, replacen |
字符串匹配模式原理
8.1.7 与其他类型的互换
- 如何把
&str
类型转换为u32
类型?parse
方法内部是用__trait的___方法来实现的?- 如何实现下面的代码:
struct Point {x: i32, y: i32}; let p = Point::from_str("{1, 2}"); assert_eq!(p.unwrap(), "Point{x: 1, y: 2}");
- 把其他类型转为字符串用____宏?
format!
宏使用的是__trait中的__函数
- 用什么关键字保留字符串中的特殊符号?
8.2 集合类型
类型 | 说明 |
---|---|
Vec<T> |
变长数组 |
VecDeque<T> |
双端队列 |
LinkedList<T> |
双向链表 |
BinaryHeap<T> |
二叉堆(最大堆) |
HashMap<T> |
哈希表 |
BTreeMap<T> |
基于B树的有序映射集 |
HashSet<T> |
无序集合 |
BTreeSet<T> |
有序集合 |
8.2.1 变长数组
array
元素可以保存在__上,而Vec
元素只能在__上String
类型是____的包装- Rust用____值代表零大小的容量
- Rust的偏序、全序与等价
- 偏序与全序相差的是_____
- 完全性是指的这个类型的每个元素都可以____
PartialEq
定义了__和__两个方法Eq
表示标记了Eq
的类型是___关系
Vec
的基本操作
操作 | 方法 |
---|---|
新建 | Vec::new() |
压栈 | vec.push(1) |
长度 | vec.len() |
索引 | vec[0] |
弹出 | vec.pop() |
改写 | vec[0] = 7 |
获取 | vec.get(10) |
扩展 | vec.extend([1, 2, 3].iter().cloned()) |
获取 | vec.get(1..2) |
转移 | vec.append(&mut vec1) |
交换 | vec.swap(1, 3) |
拷贝 | vec.copy_from_slice(&[1, 2, 3]) |
拷贝 | vec.clone_from_slice(&[1, 2, 3]) |
截断 | vec.truncate(0) |
清零 | vec.clear() |
释放 | vec.shrink_to_fit() |
包含 | vec.contains(&10) |
起始 | vec.starts_with(&[10]) |
起始 | vec.starts_with(&[10, 20]) |
结束 | vec.ends_with(&[30]) |
结束 | vec.ends_with(&[20, 30]) |
结束 | vec.ends_with(&[]) |
搜索 | vec.binary_search(&10) |
搜索 | vec.binary_search_by(|p| p.com(&13)) |
排序 | vec.sort(), vec.sort_by(), vec.sort_by_key() |
8.2.2 映射集
Map是依照__形式存储的数据结构,Rust有两种类型的Map:基于的Map、基于的__Map
HashMap的增、删、改、查
操作 | 说明 |
---|---|
创建 | books = HashMap::with_capacity(10) |
插入 | books.insert("Rust Book", "good") |
循环 | for key in books.keys() |
for val in books.values() |
|
包含 | books.contains_key("Rust Book") |
删除 | books.remove("Rust Book") |
获取 | books.get("Rust Book") |
迭代 | for (book, review) in &books |
索引 | books["Rust Book"] |
Entry的方法
Entry
体有三个方法or_insert
、or_insert_with
和key
,需要先把HashMap
转为___
对于
let mut map: HashMap<&str, u32> = HashMap::new();
- 找到
current_year
项,没有就增加并插入2017
- 找到
current_year
项的可变引用,并给它的值加10 - 找到
current_year
项,没有就用环境变量next_year+10
给它赋值
操作 | 说明 |
---|---|
创建 | let mut map: HashMap<&str, i32> = HashMap::new() |
查询或创建 | map.entry("current_year").or_insert(2021) |
查询或修改 | *map.entry("current_year").or_insert(2021) += 10 |
map.entry("current_year").or_insert_with(|| last_year + 10) |
Map合并的方法
extend
合并map1.extend(map2)
merge_chain
合并map1.into_iter().chain(map2).collect()
merge_by_ref
合并map1.extend(map_ref.into_iter().map(|(k, v)| (k.clone(), v.clone())))
Hash碰撞的两个元素称为____
8.3 容量
容量是为集合容量分配的__空间,其大小是指集合包含的__数量