是否可以将字节解码为 UTF-8,将错误转换为 Rust 中的转义序列?
Posted
技术标签:
【中文标题】是否可以将字节解码为 UTF-8,将错误转换为 Rust 中的转义序列?【英文标题】:Is it possible to decode bytes to UTF-8, converting errors to escape sequences in 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 字符串,从而避免错误而不是完全失败?
【问题讨论】:
【参考方案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\U255lo
另见:
How do I convert a Vector of bytes (u8) to a string How to print a u8 slice as text if I don't care about the particular encoding?。【讨论】:
请注意,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 序列,如下所示:�"。所以这是一个替换,而不是一个转义序列。该答案的第一部分显示了如何使用转义序列进行转换:***.com/a/41450295/432509
@ideasman42 在这种情况下,转义序列是什么意思?有什么例子?
转义序列不是替换字符,而是使用某些标识符 \N...
来显示字符,因此它不是 有损,而是包含字符串中的字符 (通常作为数字)。有关一些示例,请参阅:docs.python.org/3/library/codecs.html#error-handlers。如 OP 中所述,Python 可以为此使用surrogateescape
。将澄清这个问题,因为任何不熟悉 Python 的人都不会觉得它很有帮助。【参考方案2】:
您可以:
使用strict UTF-8 decoding 自己构建它,它会返回一个错误,指示解码失败的位置,然后您可以转义。但这效率低下,因为您将对每次失败的尝试进行两次解码。
试试3rd party crates,它提供了更多可定制的字符集解码器。
【讨论】:
将每次失败的尝试解码两次——你能在这方面进一步扩展吗?我没有看到双重解码尝试。 回复:“但这样效率低下,因为每次失败的尝试都会解码两次。” 似乎应该有更好的方法可以在一个小函数中完成,类似于这个答案,但支持有效的 utf8:***.com/a/41450295/432509 @Shepmaster,在出现错误的情况下,您认为单次通过有可能实现吗? @ideasman42 更好的方法是我建议的第二个选项。 从头开始,解析直到遇到错误,跳过错误/添加所需的任何标记,然后在错误后继续解析。您只读取每个字节一次,对所有数据进行一次传递。这就是为什么我要问我错过了什么。以上是关于是否可以将字节解码为 UTF-8,将错误转换为 Rust 中的转义序列?的主要内容,如果未能解决你的问题,请参考以下文章
在页面js 中,怎么将中文字符串转换成2个字节长度16进制数;并在js 环境下解码16进
Ruby 1.9:将字节数组转换为具有多字节 UTF-8 字符的字符串