Rust 笔记Rust 语言中的字符串
1. 概述
本文介绍 Rust 语言中的 字符 和 字符串。在 Rust 语言中,不仅 字符 和 字符串 是不同的类型,并且还存在着 str 和 String 两种字符串。
Rust 中的字符类型是 char,它表示 Unicode 标量值,占用 4 个字节,而字符串类型是 str 和 String。str 类型是一种不可变的字符串类型,通常使用 &str 来表示,而 String 类型是一种可变的字符串类型,通常使用 String 来表示。
2. Rust 的 字符 类型
2.1 Rust 中的 字符(char)类型
2.1.1 char 类型的概念
Rust 的 char
类型是该语言最原始的字母类型。该类型 在 Rust 表示 Unicode 标量值,它占用 4 个字节,可以使用 单引号 来表示一个 char 类型的值,例如:
let a = 'a'; // 常规的 ASCII 字符 let b = '😎'; // Unicode 表情符号 let c = '\u{1F600}'; // 同样是 Unicode 表情符号,使用 Unicode 标量值表示
2.1.2 在 char 类型 里 使用 转义字符
现代高级语言中,如 C、JavaScript、Python,都使用 转义符 来表示一些特殊的字符, Rust 也不例外,其中的转义字符使用 反斜杠 (\
)来表示,例如:
let a = '\n'; // 换行符 let b = '\t'; // 制表符 let c = '\''; // 单引号
由于反斜杠被用作 转义符,这意味着只有一个反斜杠表示的不是反斜杠自身,因此如果你要使用反斜杠,则加一个反斜杠对其进行转义,也就是:
let d = '\\'; // 反斜杠
在这一小节的例子中,我们使用的都是 单引号,可知这里的转义符针对的是 字符 类型。实际上 Rust 中,也可以将其用于 字符串 类型(在后面介绍)。
2.1.3 char 类型 与其它类型的转换
Rust 语言中提供了 as 关键字,与 TypeScript 中的 as 关键字仅仅是用于 类型断言 不同,在 Rust 语言中,as 的直接将 一个类型 转换为 另一个类型,而不仅仅是视作另一个类型。
因此我们可以通过 as 将 char 类型转换为 u8 类型,或者将 u8 类型转换为 char 类型:
let a = 'a'; let b = a as u8; // char 类型转换为 u8 类型 let c = b as char; // u8 类型转换为 char 类型
或者
let d = '😀'; let e = d as u32; // char 类型转换为 u32 类型 let f = e as char; // u32 类型转换为 char 类型
2.1.4 在字符(char
)实例上提供的一些方法
我们可以调用 char
原生支持的一些方法,实现某些功能。
char 类型上用于判断的相关方法
方法 | 描述 |
is_ascii |
判断是否是 ASCII 字符 |
is_alphabetic |
判断是否是字母 |
is_digit |
判断是否是十进制数字 |
is_lowercase |
判断是否是小写字母 |
is_uppercase |
判断是否是大写字母 |
例如:
let a = 'A'; let m = a.is_ascii(); // 判断是否是 ASCII 字符 let n = a.is_alphabetic(); // 判断是否是字母 let o = a.is_digit(10); // 判断是否是十进制数字 let p = a.is_lowercase(); // 判断是否是小写字母 let q = a.is_uppercase(); // 判断是否是大写字母
char 类型上用于转换的相关方法
方法 | 描述 |
to_ascii_lowercase | 转换为小写字母 |
to_ascii_uppercase | 转换为大写字母 |
例如:
let c = 'C'; let l = c.to_ascii_lowercase(); // 转换为对应的小写字母 => c
let s = 's' let u = s.to_ascii_uppercase(); // 转换为对应的大写字母 => S
2.2 从字符到字符串
2.2.1 什么是字符串
顾名思义,将一串字符穿在一起就形成了 字符串。字符串是非常重要的数据类型,因为它们可以用于表示文本数据,例如文件内容、用户输入、网络数据等等。现实生活中的文本、句子都需要使用字符串而不是单个字符进行表示。
字符串还可以用于格式化输出,例如将数字转换为字符串,或将字符串转换为数字。
在Rust语言中,str类型的 字符串是一种不可变的数据类型,用于表示文本数据。字符串是 由字符组成的序列。str类型是一种不可变的字符串类型,通常使用&str来表示。String类型是一种可变的字符串类型,通常使用String来表示。
2.2.2 字符 和 字符串的异同
在 Rust 语言中,字符和字符串相比:
- 字符类型(char)是单个字符,而 字符串 (str、String)是多个字符 组成的序列;
- 字符类型(char)是不可变的,String 类型的字符串类型可以是可变的;
- 字符类型通常使用 单引号表示,字符串类型通常使用 双引号 或者S tring::from方法 创建;
- 字符 和 字符串 类型 都可以 使用一些特殊的 转义字符,如换行符、制表符等,字符串类型也支持这些转义字符。
- 字符 类型 支持的一些方法,如 is_ascii、is_alphabetic、is_digit,同样在字符串中也支持。
3. 不可变字符串类型 str
3.1 str
类型概述
在 Rust 语言中,str 类型 是一种不可变的字符串类型,通常使用 &str 来表示。创建 str 类型 的方法有很多,比如最简单的就是使用 双引号语法,该方法可以非常方便地创建 str 字面量:
let s1 = "hello world!"; // 直接使用字符串字面量创建 let s2: &str = "hello"; // 使用类型注解创建
str 也可以是通过可变字符串 String转换来的:
let s3 = String::from("world").as_str(); // 使用 String 类型的 as_str 方法创建
或者使用可变字符串 String 类型的引用创建不可变字符串 str 类型地字面量:
let s4 = &String::from("world"); // 使用 String 类型的引用创建
3.2 不可变字符串str
的特点
3.2.1 不可变字符串str
的优点
不可变字符串具有以下有点
- 安全性高:不可变字符串不会被意外修改,避免了一些潜在的错误。
- 性能高:不可变字符串的内存布局更加紧凑,访问速度更快。
3.2.2 不可变字符串str
的不足
不可变字符串也有一些不足,比如:
- 不可变字符串无法修改,需要重新创建一个新的字符串来实现修改操作,会占用更多的内存空间。
- 不可变字符串无法直接拼接,需要使用 format! 宏 或者 String::from 方法来实现。
3.2.3 如何选用 字符串str
一般以下,可以参考以下建议选择字符串:
- 字符串不需要修改时,选用不可变字符串 str ,这样更加 安全 和 高效;
- 反之,字符串需要修改时,选用可变字符串 String。
3.3 如何判断一个值的类型为str
判断一个值是否是 str 类型可以使用类型判断语法:
fn is_str(s: &dyn std::any::Any) -> bool { s.is::<&str>() }
例如:
let s1 = "hello"; let s2 = String::from("world"); let s3 = 123; let s4 = vec![1, 2, 3]; println!("{}", is_str(&s1)); // true println!("{}", is_str(&s2)); // false println!("{}", is_str(&s3)); // false println!("{}", is_str(&s4)); // false
4. 可变字符串类型 String
4.1 快速入门可变字符串 String
4.1.1 什么是可变字符串
可变字符串的 可变 是相对于不可变字符串而言的,我们上一节使用字面量语法创建的字符串已经创建就不可以修改了,因而称之为不可变字符串。
换而言之,可变字符串的可变指的是字符串的内容可以被修改。例如,使用 push_str 方法可以在字符串末尾添加字符串字面量:
let mut s1 = String::from("hello"); s1.push_str(", world!"); // 在字符串末尾添加字符串字面量 println!("{}", s1); // 输出:hello, world!
可以看到,通过这样的方法,使得字符串的内容发生了变化。类似的方法还有很多,请参考 4.2 String
方法解析 部分。
4.1.2 String
类型 与 str
类型的区别
- str 类型是不可变的,而 String 类型是可变的。
- str 类型通常用于函数参数和返回值,而 String 类型通常用于变量和常量。
- str 类型通常使用 字符串字面量 创建,而 String 类型通常使用 String::from 方法 创建。关于创建 String 的方法,请参考下一小节
4.1.3 如何创建String
通过使用 String::new 方法创建
使用 String::new 方法可以创建一个空的 String 类型的实例,例如:
let s1 = String::new(); // 创建一个空的 String 类型
通过使用 String::from 方法创建
String::from 方法可以直接使用字符串字面量创建 String,例如:
let s2 = String::from("hello");
实际上,你也可以理解为将一个 str 转换为对应的 String。
通过 str
类型的 to_string
方法创建
不可变字符串 str 上提供了一个 to_string
方法,返回对应的 String 字符串。例如:
let s3 = "world".to_string();
通过 format!
宏创建
你还可以使用 format! 宏
来创建 String 字符串。例如:
let s4 = format!("{} {}", "hello", "world");
关于 format! 宏
,请参考 4.3 format! 宏 与 模板字符串的使用
4.2 String
用法解析
4.2.1 字符串拼接
使用 +
操作符可以用于拼接两个字符串例如:
let s1 = String::from("Hello "); let s2 = String::from("world!"); let s3 = s1 + &s2; // 使用 + 运算符连接两个字符串 println!("{}", s3); // 输出:Hello world!
4.2.2 字符串的切片
所谓 切片,指的是通过指定一对起止字符在字符串中的 位序,从而获得一个子字符串的方法。和诸多编程语言一样(如Python),可以使用 []
语法对字符串进行切片。如:
let s1 = String::from("hello"); let s2 = &s1[0..2]; // 使用切片获取字符串的一部分 println!("{}", s2); // 输出:he
4.2.3 clone 方法拷贝字符串
String 的 clone
方法从原 String 对象实例上拷贝得到一个副本,例如:
let s1 = String::from("hello"); let s2 = s1.clone(); // 使用 clone 方法复制一个字符串 println!("{}", s2); // 输出:hello
4.2.4 trim 方法去除两端空白字符
在字符串处理的时候经常需要处理掉字符串两端的空白字符,使用 String 的trim
方法即可轻松实现,例如:
例如:
let s1 = String::from("hello"); let s2 = s1.trim(); println!("{}", s9); // 输出:hello
4.2.5 chars 方法及其应用
String 的 chars 方法
String 的chars
方法用于获取字符串的字符迭代器。
例如:
let s1 = String::from("hello"); let s2 = s1.chars(); // 获取字符串的字符迭代器 for c in s23 { println!("{}", c); // 逐个输出字符串的字符 }
通过 chars 方法获取 字符串首字符
例如:
let s1 = String::from("hello"); let s2 = s1.chars().nth(0).unwrap(); // 获取字符串的第一个字符 println!("{}", s2); // 输出:h
通过 chars 方法获取 字符串尾字符
例如:
let s1 = String::from("hello"); let s2 = s1.chars().last().unwrap(); // 获取字符串的最后一个字符 println!("{}", s2); // 输出:o
通过 chars 方法获取 字符串的字符数
例如:
let s1 = String::from("hello"); let s2 = s1.chars().count(); // 获取字符串的字符数 println!("{}", s2); // 输出:5
通过 chars 方法 反转字符串
例如:
let s1 = String::from("hello"); let s2 = s1.chars().rev().collect::<String>(); // 将字符串反转 println!("{}", s2); // 输出:olleh
4.2.6 to_uppercase 方法将字符串统一为大写字母形式
例如:
let s1 = String::from("hEllo"); let s2 = s1.to_uppercase(); // 将字符串转换为大写字母形式 println!("{}", s2); // 输出:HELLO
4.2.7 to_lowercase 方法将字符串统一为大写字母形式
例如:
let s1 = String::from("heLLo"); let s2 = s1.to_lowercase(); // 将字符串转换为小写字母形式 println!("{}", s2); // 输出:hello
4.2.8 len 方法获取字符串的长度
例如:
let length = String::from("world").len(); // 5
4.2.9 contains 方法判断字符串是否包含指定的子串
例如:
let isContains = String::from("Hello").contains("llo"); // true
4.2.10 push 方法在字符串末尾添加单个字符
例如:
let mut s = String::from("Hello"); s.push('!'); println!("{}", s); // 输出:Hello!
4.2.11 push_str 方法在字符串末尾添加字符串字面量
例如:
let mut s = String::from("hello"); s.push_str(", world!"); println!("{}", s); // 输出:hello, world!
4.2.12 pop 方法移除字符串末尾的字符
例如:
let mut s = String::from("hello!"); s.pop(); // 移除字符串末尾的字符 println!("{}", s); // 输出:hello
4.2.13 remove 方法移除字符串指定为序处的字符
例如:
let mut s = String::from("hello"); s.remove(2); println!("{}", s); // 输出:helo
4.2.14 insert 方法在字符串指定位置插入内容
内容可以是 char
和 str
。
例如在字符串指定位置插入单个字符:
let mut s = String::from("helo"); s.insert(2, 'l'); // println!("{}", s); // 输出:hello
又如在字符串指定位置插入字符串字面量:
let mut s = String::from("heo"); s.insert(1, "ll"); println!("{}", s); // 输出:hello
4.2.15 replace_range 方法替换字符串指定范围内的内容
例如:
let mut s = String::from("hello"); s.replace_range(1..3, "a"); println!("{}", s); // 输出:hallo
4.2.16 truncate 方法截断字符串
truncate 方法可以用于截断字符串,只保留指定长度的内容。例如:
let mut s = String::from("hello"); s.truncate(3); println!("{}", s); // 输出:hel
4.2.17 clear 方法清空字符串的内容
字符串是字符的容器,清空字符串就得到一个没有任何字符的字符串,也称空字符串。
例如:
let mut s = String::from("hello"); s.clear(); println!("{}", s); // 输出:
4.2.18 字符串中使用转义符、原始字符串
字符串中使用转义符
在介绍 Rust 字符的时候我们介绍过转义符,Rust 中的字符串类型也支持转义字符,例如:
let s1 = "hello\nworld"; // 换行符 let s2 = "hello\tworld"; // 制表符 let s3 = "hello\'world"; // 单引号 let s4 = "hello\\world"; // 反斜杠
也可以使用转义符表示 Unicode 标量值,例如:
let s1 = "\u{1F600}"; // Unicode 表情符号 let s2 = "\u{1F600}\u{1F601}\u{1F602}"; // 多个 Unicode 表情符号
原始字符串表示
在 Rust 中,字符串前的 r#
表示使用原始字符串表示法,不会转义字符。例如:
let s1 = r#"hello\nworld"#; println!("{}", s1); // 输出:hello\nworld
原始字符串表示法还支持使用多个 # 号,例如:
let s2 = r##"hello "world""##; println!("{}", s2); // 输出:hello "world"
使用原始字符串表示法可以避免一些转义字符带来的麻烦,比如在正则表达式中使用反斜杠。例如:
let s3 = r"\d+"; println!("{}", s3); // 输出:\d+
4.3 format! 宏
与 模板字符串的使用
Rust 中的 format! 宏
用于格式化字符串,比如:
let s1 = format!("{} {}", "hello", "world"); // 使用位置占位符 let s2 = format!("{name} {age}", name = "Alice", age = 18); // 使用命名占位符 let s3 = format!("{:?}", vec![1, 2, 3]); // 将 Vec 转换为字符串
format! 宏
还支持一些高级特性,如:
let s4 = format!("{:x}", 255); // 将数字转换为十六进制字符串 let s5 = format!("{:b}", 255); // 将数字转换为二进制字符串 let s6 = format!("{:o}", 255); // 将数字转换为八进制字符串 let s7 = format!("{:.2}", 3.1415926); // 将浮点数保留两位小数 let s8 = format!("{:+}", 123); // 将数字添加正负号 let s9 = format!("{:*>10}", "hello"); // 将字符串右对齐并填充 * let s10 = format!("{:^10}", "hello"); // 将字符串居中对齐