RUST 学习日记 第16课 ——字符串的常用方法(二)
0x00 回顾与开篇
上节课介绍了Rust的字符串常见的修改方法这节课开始介绍Rust字符串的访问方法。介绍Rust字符串的文章,这已经是第4篇了。后续如果还有时间,我会更加详细的在介绍下字符串的一些其它知识。
0x01 Unicode和UTF-8
在计算机中最常见的编码应该就是ASCII编码了,但是ASCII编码的范围只有0x000x7F,无法存储汉字,少数民族文字等等。从而出现了GB2312,GB18030等各种编码。为了统一字符编码,国际标准化组织制定了通用的多字节编码字符集,也就是**Unicode**字符集。其包含了所有世界上所有的语言和各种字符。其中最常用的范围是0x00000xD7FF和0xE000~0x10FFFF。
但是Unicode字符集每个字符都占4个字节,为了节省空间,UTF-8是最简单且高效的一种编码格式。在Rust中String和&str类型都是使用UTF-8编码格式表示文本。UTF-8是以1字节为编码单位的可变长编码,它根据一定规则将码位编码为1~4字节。如下表所示:
UTF-8编码(1~4字节) | 码点表示 | uNICODE范围 |
---|---|---|
0xxxxxxx | 0bxxxxxxx | 0x00~0x7f |
110xxxxx 10aaaaaa | 0bxxxxxaaaaaa | 0x80~0x7ff |
1110xxxx 10aaaaaa 10bbbbbb | 0bxxxaaaaaabbbbbb | 0x800~0xffff |
11110xxx 10aaaaaa 10bbbbbb 10cccccc | 0bxxxaaaaaabbbbbbcccccc | 0x10000~0x10ffff |
这就很好理解上节所说的汉字占3个字节,而英文字母,阿拉伯数字占1个字节的原因了。
UTF-8编码举例如下:
UTF-8编码(1~4字节) | 字符 | 码点 |
---|---|---|
01100001 | a | 0b1100001 == 0x61 |
11000010_10101001 | © | 0b00010_101001 == 0xa9 |
11100110_10110001_10001001 | 汉 | 0b0110_110001_001001 == 0x6c49 |
11110000_10011111_10011000_10000011 | 0b000_011111_011000_000011== 0x1f603 |
示例代码如下:
println!("***************1、编码*****************");
let a = "a";
let b = "©";
let c = "汉";
let d = "";
println!("a 占 {} 个字节", std::mem::size_of_val(a));
println!("b 占 {} 个字节", std::mem::size_of_val(b));
println!("c 占 {} 个字节", std::mem::size_of_val(c));
println!("d 占 {} 个字节", std::mem::size_of_val(d));
println!("\n***************1、编码(打印二进制)*****************");
for x in a.bytes() {
print!("{:08b}_", x);
}
println!();
for x in b.bytes() {
print!("{:08b}_", x);
}
println!();
for x in c.bytes() {
print!("{:08b}_", x);
}
println!();
for x in d.bytes() {
print!("{:08b}_", x);
}
println!("\n***************1、编码(打印Unicode)*****************");
println!("{:X}", 'a' as i32);
println!("{:X}", '©' as i32);
println!("{:X}", '汉' as i32);
println!("{:X}", '' as i32);
代码运行结果:
***************1、编码*****************
a 占 1 个字节
b 占 2 个字节
c 占 3 个字节
d 占 4 个字节
***************1、编码(打印二进制)*****************
01100001_
11000010_10101001_
11100110_10110001_10001001_
11110000_10011111_10011000_10000011_
***************1、编码(打印Unicode)*****************
61
A9
6C49
1F603
编码和解码规则这里就不赘述了,感兴趣的可以搜索下资料。如果留言比较多,我回额外一章讲解下编码解码。
0x02 字符串的访问
在Rust中,字符串的访问要注意以下两个点:
1、由于字符串是UTF-8编码的字节序列,是可变长度编码,所以不能直接使用索引来访问字符。
2、字符串操作分为按字节处理和按字符处理两种方式。使用bytes()
方法是按字节处理,返回字节迭代的迭代器。使用chars()
方法是按字符处理,返回字符迭代的迭代器。
字符串的长度
如果通过len()
方法获取字符串的长度,则返回的是以字节为单位的长度,即字符串中所有字符的总字节数。如果通过chars().count()
方法获取的长度则表示字符的长度,通过此方法获取的长度就是咱们嘴上常说的字符串长度啦。
示例代码如下:
let string_length = "我正在学习Rust~";
println!("\"{}\"的字节长度 : {}", string_length, string_length.len());
println!("\"{}\"的字符长度 : {}", string_length, string_length.chars().count());
代码运行结果:
"我正在学习Rust~"的字节长度 : 20
"我正在学习Rust~"的字符长度 : 10
访问字符串元素
由于Rust的字符串是UTF-8编码,所以不允许直接使用索引来访问单个字符元素。那么咱们只能借助迭代器来访问。(有关迭代器的相关知识将在后续章节介绍)bytes()
和char()
方法分别返回字节和字符迭代器,他们中存在nth
方法可以通过索引的形式去访问元素。该方法返回的是Option
类型。
示例代码如下:
let string_nth = "Rust编程基础";
// 访问第5个字符
dbg!(string_nth.chars().nth(5));
// 访问第5个字节
dbg!(string_nth.bytes().nth(5));
代码运行结果:
[src\main.rs:45] string_nth.chars().nth(5) = Some(
'程',
)
[src\main.rs:47] string_nth.bytes().nth(5) = Some(
188,
)
0x03 小结
花了4篇文章也只是简单介绍了Rust的字符串,要想真正的了解字符串的方法,还需要多练习。其实关于Rust字符串的知识和相关方法还是挺多的,由于篇幅有限,字符串的讲解就暂告一段落了。
0x05 参考资料
Unicode所有区段 统一码编码范围 | Unicode符号库 ✏️ (fuhaoku.net)
0x04 本节源码
016 · StudyRust - 码云 - 开源中国 (gitee.com)
下节预告——流程控制。