Rust 字符串存储为表示 UTF-8 编码字符的字节序列。 UTF-8 是一种可变宽度编码,因此字节索引可能会将您留在一个字符内,这显然是不安全的。但是通过索引获取代码点是一个 O(n) 操作。此外,索引代码点并不是您真正想要做的,因为有些代码点甚至没有关联的字符,例如变音符号或其他修饰符。索引字素簇更接近正确的方法,但通常在文本渲染或语言处理中需要。
我的意思是索引字符串很难正确定义,而且大多数人通常想要的都是错误的。因此 Rust 不提供对字符串的通用索引操作。
但是,有时您确实需要为字符串编制索引。例如,如果您事先知道您的字符串仅包含 ASCII 字符,或者您正在处理二进制数据。当然,在这种情况下,Rust 提供了所有必要的手段。
首先,您始终可以获得底层字节序列的视图。 &str 具有 as_bytes() 方法,该方法返回 &[u8],字符串包含的字节切片。然后就可以使用通常的索引操作了:
x.as_bytes()[0] != b'#'
注意特殊符号:b'#' 表示“u8 类型的 ASCII 字符 #”,即它是字节字符文字(另请注意,您不需要写 "#".chars().next() 来获取字符 @ 987654330@,你可以只写'#' - 一个纯字符文字)。但是,这是不安全的,因为&str 是 UTF-8 编码的字符串,并且第一个字符可以包含多个字节。
在 Rust 中处理 ASCII 数据的正确方法是使用 ascii crate。您可以使用as_ascii_str() 方法从&str 转到&AsciiStr。然后你可以这样使用它:
extern crate ascii;
use ascii::{AsAsciiStr, AsciiChar};
// ...
x.as_ascii_str().unwrap()[0] != AsciiChar::Hash
这样您将需要更多的输入,但您会获得更多的安全性作为回报,因为as_ascii_str() 会检查您是否只使用 ASCII 数据。
然而,有时您只想处理二进制数据,而不是将其真正解释为字符,即使源包含一些 ASCII 字符。例如,当您为某些标记语言(如 Markdown)编写解析器时,可能会发生这种情况。在这种情况下,您可以将整个输入视为一个字节序列:
use std::io::{Read, BufReader};
use std::fs::File;
fn main() {
let mut file = BufReader::new(File::open("/etc/hosts").unwrap());
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
let mut iter = buf.split(|&c| c == b'\n').filter(|line| line[0] != b'#');
println!("{:?}", iter.next().unwrap());
}