【问题标题】:Is it possible to decode bytes to UTF-8, converting errors to escape sequences in Rust?是否可以将字节解码为 UTF-8,将错误转换为 Rust 中的转义序列?
【发布时间】:2017-05-18 05:39:18
【问题描述】:

在 Rust 中,可以通过执行以下操作从字节中获取 UTF-8:

if let Ok(s) = str::from_utf8(some_u8_slice) {
    println!("example {}", s);
}

这要么有效,要么无效,但 Python 具有处理错误的能力,例如:

s = some_bytes.decode(encoding='utf-8', errors='surrogateescape');

在此示例中,参数surrogateescape 将无效的 utf-8 序列转换为转义码,因此不会忽略或替换无法解码的文本,而是将它们替换为有效的字节文字表达式 @987654325 @。详情请见:Python docs

Rust 是否有办法从字节中获取 UTF-8 字符串,从而避免错误而不是完全失败?

【问题讨论】:

    标签: utf-8 rust unicode-escapes


    【解决方案1】:

    是的,通过String::from_utf8_lossy

    fn main() {
        let text = [104, 101, 0xFF, 108, 111];
        let s = String::from_utf8_lossy(&text);
        println!("{}", s); // he�lo
    }
    

    如果您需要对流程进行更多控制,可以使用std::str::from_utf8,正如other answer 所建议的那样。但是,没有理由像它所建议的那样双重验证字节。

    一个快速破解的例子:

    use std::str;
    
    fn example(mut bytes: &[u8]) -> String {
        let mut output = String::new();
    
        loop {
            match str::from_utf8(bytes) {
                Ok(s) => {
                    // The entire rest of the string was valid UTF-8, we are done
                    output.push_str(s);
                    return output;
                }
                Err(e) => {
                    let (good, bad) = bytes.split_at(e.valid_up_to());
    
                    if !good.is_empty() {
                        let s = unsafe {
                            // This is safe because we have already validated this
                            // UTF-8 data via the call to `str::from_utf8`; there's
                            // no need to check it a second time
                            str::from_utf8_unchecked(good)
                        };
                        output.push_str(s);
                    }
    
                    if bad.is_empty() {
                        //  No more data left
                        return output;
                    }
    
                    // Do whatever type of recovery you need to here
                    output.push_str("<badbyte>");
    
                    // Skip the bad byte and try again
                    bytes = &bad[1..];
                }
            }
        }
    }
    
    fn main() {
        let r = example(&[104, 101, 0xFF, 108, 111]);
        println!("{}", r); // he<badbyte>lo
    }
    

    您可以扩展它以获取替换坏字节的值,处理坏字节的闭包等。例如:

    fn example(mut bytes: &[u8], handler: impl Fn(&mut String, &[u8])) -> String {
        // ...    
                    handler(&mut output, bad);
        // ...
    }
    
    let r = example(&[104, 101, 0xFF, 108, 111], |output, bytes| {
        use std::fmt::Write;
        write!(output, "\\U{{{}}}", bytes[0]).unwrap()
    });
    println!("{}", r); // he\U{255}lo
    

    另见:

    【讨论】:

    • 请注意,from_utf8_lossy 没有像 Python 那样提供不同的错误处理方式。而不是转义,无效的 utf-8 序列被替换为 U+FFFD(匹配 Python 的 replace 行为)。所以我认为这个问题的简短答案是,尽管它仍然值得一提from_utf8_lossy
    • 对所提出问题的简短回答(“是否可以将字节解码为 UTF-8,在 Rust 中将错误转换为转义序列?”或“Rust 是否有办法获得 UTF -8 来自字节的字符串,它可以处理错误而不会完全失败?") 是 no?我很确定这段代码就是这样做的。
    • from_utf8_lossy 状态的文档:"在此转换过程中,from_utf8_lossy() 将用 U+FFFD 替换字符替换任何无效的 UTF-8 序列,如下所示:�"。所以这是一个替换,而不是一个转义序列。该答案的第一部分显示了如何使用转义序列进行转换:stackoverflow.com/a/41450295/432509
    • @ideasman42 在这种情况下,转义序列是什么意思?有什么例子?
    • 转义序列不是替换字符,而是使用某些标识符 \N{...} 来显示字符,因此它不是 有损,而是包含字符串中的字符 (通常作为数字)。有关一些示例,请参阅:docs.python.org/3/library/codecs.html#error-handlers。如 OP 中所述,Python 可以为此使用surrogateescape。将澄清这个问题,因为任何不熟悉 Python 的人都不会觉得它很有帮助。
    【解决方案2】:

    您可以:

    1. 使用strict UTF-8 decoding 自己构建它,它会返回一个错误,指示解码失败的位置,然后您可以转义。但这效率低下,因为您将对每次失败的尝试进行两次解码。

    2. 试试3rd party crates,它提供了更多可定制的字符集解码器。

    【讨论】:

    • 将每次失败的尝试解码两次——你能在这方面进一步扩展吗?我没有看到双重解码尝试。
    • 回复:“但这样效率低下,因为每次失败的尝试都会解码两次。” 似乎应该有更好的方法可以在一个小函数中完成,类​​似于这个答案,但支持有效的 utf8:stackoverflow.com/a/41450295/432509
    • @Shepmaster,在出现错误的情况下,您认为单次通过有可能实现吗?
    • @ideasman42 更好的方法是我建议的第二个选项。
    • 从头开始,解析直到遇到错误,跳过错误/添加所需的任何标记,然后在错误后继续解析。您只读取每个字节一次,对所有数据进行一次传递。这就是为什么我要问我错过了什么。
    猜你喜欢
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 2018-02-15
    • 2012-06-20
    • 2014-08-15
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    相关资源
    最近更新 更多