Rust字符串以UTF-8编码的字节序列形式存储。由于UTF-8是一种可变宽度编码,因此字节索引可能会导致位于字符内部,这显然是不安全的。但通过索引获取代码点是O(n)操作。此外,索引代码点并不是您真正想要做的事情,因为有一些代码点甚至没有与之相关联的字符,比如变音符号或其他修饰符。索引字形集群更接近于正确的方法,但通常需要用于文本呈现或语言处理。
我的意思是,索引字符串很难准确定义,并且大多数人通常想要的是错误的。因此Rust不提供字符串上的通用索引操作。
然而,偶尔你确实需要索引字符串。例如,如果您事先知道您的字符串仅包含ASCII字符,或者如果您正在处理二进制数据。在这种情况下,Rust当然提供了所有必要的手段。
首先,您始终可以获得基础字节序列的视图。 &str
有一个as_bytes()
方法,该方法返回&[u8]
,字符串由此组成。然后,您可以使用常规的索引操作:
x.as_bytes()[0] != b'#'
请注意特殊符号的表示方法:b'#'
表示“类型为 u8
的 ASCII 字符 #
”,即它是一个字节字符字面量(也请注意,您不需要编写 "#".chars().next()
来获取字符 #
,您可以直接编写 '#'
- 一个普通的字符字面量)。然而,这样做是不安全的,因为 &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());
}
x[].as_bytes()[0] != b'#'
在任何有意义的情况下都不是不安全的。它不会威胁内存安全,也不涉及无效的char
值,不会对类型进行奇怪的操作,甚至不太可能做出无意义的事情。在UTF-8中,多字节码点仅由字节> 127(即非ASCII)组成,因此搜索具有值为35的字节是查找U + 0023代码点出现的完全合适的方法。但是可以承认:这是一种糟糕的风格,并且转换为字节是其他文本处理任务的一种不良习惯。 - user395760char
迭代器通常更好,并且许多算法已经由libstd提供了),但是让我们不要误解信息的本意。 - user395760