Rust学习之旅——文本

前言

Rust 非常关注文本国际化与字节层级的问题,这意味着 Rust 有许多实用的工具来解决这些问题。

字符串常量(string literals)

字符串常量(String Literals)采用 Unicode 编码(注:下文提及的 utf-8 为 Unicode 的一部分)。

字符串常量的类型为 &'static str :

  • & 意味着该变量对内存中数据的引用,没有使用 &mut 代表编译器将不会允许对该变量修改
  • 'static 意味着字符串将会一直保存到程序结束(它不会在程序运行期间被释放 drop )
  • str 意味着该变量总是指向一串合法的 utf-8 字节序列

内存细节:

  • Rust编译器可能会将字符串储存在程序内存的数据段中
fn main() {
	let a: &'static str = "你好";
	println!("{} {}", a, a.len());
}

utf-8

随着在计算机上使用的语言增加,需要一个能比 ASCII 编码(1 字节表示 1 个字符,总共可表示 256 个字符)表示更多字符的编码来容纳其它语言的字符。

utf-8 编码这时被引入来解决这个问题,它用 1-4 个字节来表示 1 个字符,这使得可以表示的字符数大大增加。

使用可变长度的字节来表示字符有一个优点,就是常见的 ASCII 编码字符在 utf-8 编码中无需使用更多的字节(也是 1 字节表示 1 个字符,也就是说 utf-8 完全兼容 ASCII 编码)。

但是这样做也有缺点,在 utf-8 文本中通过索引来匹配字符(例:my_text[3] 获取 my_text 的第 4 个字符)将不能像以前的编码标准那么快(以前编码标准花费 O(1) 常数时间)。 这是因为前面的字符具有可变的对应字节,从而无法直接确定第 4 个字符在字节序列中的起始字节。

我们需要遍历 utf-8 的字节序列才可以得到对应 Unicode 字符的起始位置(这将花费 O(n) 线性时间)。

转义字符

有些字符难以使用可视字符表示,这是可通过转义字符来表示这些字符。

Rust支持类 C 语言中的常见转义字符:

  • \n - 换行符
  • \r - 回车符(回到本行起始位置)
  • \t - 水平制表符(即键盘 tab 键)
  • \ - 代表单个反斜杠 \
  • \0 - 空字符(null)
  • ’ - 表示单引号 ’

完整的转义字符在这

多行字符串常量

Rust 中字符串默认支持分行。使用 \ 可以使多行字符串不换行。

fn main() {
	let haiku: &'static str = "
	 	我写下,擦掉,
	 	再写,再擦,
	 	然后一朵罂粟花开了。
	 	- 葛饰北斋";
	println!("{}", haiku);

	println!("你好 \
	世界"); // 注意 世 字前面的空格会被忽略
}

原始字符串常量

原始字符串支持写入原始的文本而无需为特殊字符转义,因而不会导致可读性下降(如双引号与双斜杠无需写为 " 和 \ ),只需以 r#" 开头,以 "# 结尾。

fn main() {
	let a: &'static str = r#"
		<div class="advice">
			原始字符串在一些情景下非常有用。
		</div>
		"#;
	println!("{}", a);
}

文件中的字符串常量

如果你需要使用大量文本,可以尝试用宏 include_str! 来从本地文件中导入文本到程序中:

let hello_html = include_str!("hello.html")

字符串片段(string slice)

字符串片段是对内存中字节序列的引用,而且这段字节序列必须是合法的 utf-8 字节序列。

str 片段的字符串片段(子片段),也必须是合法的 utf-8 字节序列。

&str 的常用方法:

  • len 获取字符串常量的字节长度(不是字符长度)
  • starts_with / ends_with 用于基础测试
  • is_empty 长度为0时返回 true
  • find 返回 Option< usize > ,其中的 usize 为匹配到的第一个对应文本的索引值
fn main() {
    let a = "你好 🦀";
    println!("{}", a.len());
    let first_word = &a[0..6];
    let second_word = &a[7..11];
    // let half_crab = &a[7..9]; 报错
    // Rust 不接受无效 unicode 字符构成的片段
    println!("{} {}", first_word, second_word);
}

char

为了解决使用 Unicode 带来的麻烦,Rust 提供了将 utf-8 字节序列转化为类型 char 的 vector 的方法。
每个 char 长度都为4字节(可提高字符查找的效率)。

fn main() {
	// 收集字符并转换为类型为 char 的 vector
	let chars = "你好 🦀".chars().collect::<Vec<char>>();
	println!("{}", chars.len());  // 结果为 4
	// 由于 char 为 4字节长,我们可以将其转化为 u32
	println!("{}", chars[3] as u32);
}

字符串(String)

字符串 String 是一个结构体,其持有以堆(heap)的形式在内存中存储的 utf-8 字节序列。
由于它以堆的形式来存储,字符串可以用延长、修改等等。这些都是字符串常量(string literals)无法执行的操作。

常用方法:

  • push_str 用于在字符串结尾添加字符串常量(&str)
  • replace 用于将一段字符串替换为其他的
  • to_lowercase / to_uppercase 用于大小写转换
  • trim 用于去除字符串前后的空格

如果字符串 String 被释放(drop)了,其对应的堆内存片段也将被释放。
字符串 String 可以使用 + 运算符来在其结尾处连接一个 &str 并将其自身返回。但是这个方法不是那么人性化。

fn main() {
	 let mut helloworld = String::from("你好");
	 helloworld.push_str(" 世界");
	 helloworld = helloworld + "!";
	 println!("{}", helloworld);
}

将文本作为函数的参数

字符串常量(String literals)和字符串(String)一般以字符串片段(string slice)的形式传递给函数。这给许多场景提供了充足的灵活性,因为其所有权并未被传递。

fn say_it_loud(msg:&str){
	println!("{} !!!", msg.to_string().to_uppercase());
}

fn main() {
	// say_it_loud can borrow &'static str as a &str
	say_it_loud("你好");
	// say_it_loud can also borrow String as a &str
	say_it_loud(&String::from("再见"));
}

字符串构建

concat 和 join 可以以简洁而有效的方式构建字符串。

fn main() {
	let helloworld = ["你好", ["世界"], "!"].concat();
	let abc = ["a", "b", "c"].join(",");
    println!("{}", helloworld);
    println!("{}",abc);
}

字符串格式化

宏 format! 可用于创建一个使用占位符的参数化字符串。(例:{})

format! 和 println! 生成的参数化字符串相同,只是 format! 将其返回而 println! 将其打印出来。
涉及内容很多,如需完整内容可看这里

fn main() {
	let a = 42;
	let f = format!("生活诀窍:{}", a);
	println!("{}", f);
}

字符串转换

许多类型都可以通过 to_string 转换为字符串。
而泛型函数 parse 则可以将字符串或是字符串常量转换为其他类型,该函数会返回 Result 因为转换有可能失败。

fn main() -> Result<(), std::num:ParseIntError> {
	let a = 42;
	let a_string = a.to_string();
	let b = a_string.parese::<i32>()?;
	println!("{} {}", a, b);
	Ok(())
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值