在Rust中将ASCII数组转换为字符串的惯用方法

3

从字节数组中,我希望使用ASCII编码将一个切片转换为字符串。

解决方案:

fn main() {
    let buffer: [u8; 9] = [255, 255, 255, 255, 77, 80, 81, 82, 83];
    let s = String::from_iter(buffer[5..9].iter().map(|v| { *v as char }));
    println!("{}", s);
    assert_eq!("PQRS", s);
}

这种写法似乎不太符合惯用语,而且性能较差。 我们能做得更好吗? 不使用外部 crate?

2个回答

3
一个 Rust 字符串可以直接从一个 UTF-8 编码的字节缓冲区创建,方法如下:
fn main() {
    let buffer: [u8; 9] = [255, 255, 255, 255, 77, 80, 81, 82, 83];
    let s = std::str::from_utf8(&buffer[5..9]).expect("invalid utf-8 sequence");
    println!("{}", s);
    assert_eq!("PQRS", s);
}

如果输入缓冲区包含无效的UTF-8序列,则操作可能失败,但ASCII字符是有效的UTF-8,因此在这种情况下可以正常工作。

请注意,这里s的类型为&str,意味着它是对buffer的引用。这里不会进行任何分配,因此操作非常高效。

Playground链接中查看它的运行情况。


2
所以解决方案更多地涉及UTF-8:_ASCII字符是有效的UTF-8_。我不知道。 - Wolfgang Kuehn
2
这是正确的。这甚至是 UTF-8 的最初设计目标之一,与 ASCII 向后兼容。 - SirDarius
出于好奇,如何做相反的操作?例如将ASCII字符串‘a’转换为数值(0x61)。 - Raleigh L.

1

正如SirDarius所说的, 您可以尝试使用core::str::from_utf8。但是您需要了解并不是每个UTF8字符串都是ASCII字符串。我的意思是:仅因为一个字节数组可以被解释为UTF8字符串,并不意味着它可以被解释为ASCII字符串。

换句话说,只有当您已经知道字节数组确实是ASCII时,core::str::from_utf8才能正常工作。

但在这种情况下,更有效的方法是直接使用core::str::from_utf_unchecked,因为from_utf8的文档中写道:

如果您确定字节片段是有效的UTF-8,并且您不想承担有效性检查的开销,则有一个不安全版本的此函数,from_utf8_unchecked,其行为相同但跳过了检查。

这是一个示例,您可以从一个无效的ASCII数组中获取有效字符串:
fn main() {
    let buffer = [ 226, 154, 160 ];
    //             ^^^  ^^^  ^^^ None of these are valid ASCII characters
    let str = core::str::from_utf8(&buffer).unwrap(); // Doesn't panic
    println!("{}", str); // Prints "⚠"
}

自己运行这个例子

相反,您需要先扫描字节数组以查找无效的ASCII字符。

解决方案

fn get_ascii_str<'a>(buffer: &'a [u8]) -> Result<&'a str, ()> {
    for byte in buffer.into_iter() {
        if byte >= &128 {
            return Err(());
        }
    }
    Ok(unsafe {
        // This is safe because we verified above that it's a valid ASCII
        // string, and all ASCII strings are also UTF8 strings
        core::str::from_utf8_unchecked(buffer)
    })
}

注意:此函数适用于 [no_std] 环境。
示例:
fn main() {
    let buffer = [ 226, 154, 160 ]; // UTF8 bytes for "⚠"
    //             ^^^  ^^^  ^^^ None of these are valid ASCII characters
    assert_eq!(Err(()), get_ascii_str(&buffer)); // Correctly fails to interpret as ASCII
    let buffer = [
        'H' as u8,
        'e' as u8,
        'l' as u8,
        'l' as u8,
        'o' as u8,
        ',' as u8,
        ' ' as u8,
        'w' as u8,
        'o' as u8,
        'r' as u8,
        'l' as u8,
        'd' as u8,
        '!' as u8,
    ];
    let str = get_ascii_str(&buffer).unwrap();
    println!("{}", str); // Prints "Hello, world!"
}

fn get_ascii_str<'a>(buffer: &'a [u8]) -> Result<&'a str, ()> {
    // See implementation above
}

自己运行这个例子


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接