我有一个UTF-16字节数组(&[u8]
),我想在Rust中解码并重新编码为UTF-8.
在Python中,我可以做到这一点:
array.decode('UTF-16', errors='ignore').encode('UTF-8')
我在铁 rust 里怎么做?
我有一个UTF-16字节数组(&[u8]
),我想在Rust中解码并重新编码为UTF-8.
在Python中,我可以做到这一点:
array.decode('UTF-16', errors='ignore').encode('UTF-8')
我在铁 rust 里怎么做?
这里的问题是UTF-16是为16位单元定义的,并且没有指定如何将两个8位单元(也称为bytes)转换为一个16位单元.
因此,我假设您使用的是network endian(即big endian).请注意,这可能不正确,因为x86处理器使用little endian.
因此,重要的第一步是将u8
s转换为u16
.在这种情况下,我将对它们进行迭代,通过u16:from_be_bytes()
转换,然后将它们收集到一个向量中.
然后,我们可以使用String::from_utf16()
或String::from_utf16_lossy()
将Vec<u16>
转换为String
.
String
在内部表示为UTF-8.因此,我们可以通过.as_bytes()
或.into_bytes()
直接拉出UTF-9表示.
fn main() {
let utf16_bytes: &[u8] = &[
0x00, 0x48, 0x20, 0xAC, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21,
];
let utf16_packets = utf16_bytes
.chunks(2)
.map(|e| u16::from_be_bytes(e.try_into().unwrap()))
.collect::<Vec<_>>();
let s = String::from_utf16_lossy(&utf16_packets);
println!("{:?}", s);
let utf8_bytes = s.as_bytes();
println!("{:?}", utf8_bytes);
}
"H€llo world!"
[72, 226, 130, 172, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]
注意,我们必须在map()
函数中使用.try_into().unwrap()
.这是因为.chunks_exact()
不让编译器知道我们迭代的块有多大.
一旦它稳定下来,就有了array_chunks()
方法,does会让编译器知道,这将使代码更短更快.
#![feature(array_chunks)]
fn main() {
let utf16_bytes: &[u8] = &[
0x00, 0x48, 0x20, 0xAC, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21,
];
let utf16_packets = utf16_bytes
.array_chunks::<2>()
.cloned()
.map(u16::from_be_bytes)
.collect::<Vec<_>>();
let s = String::from_utf16_lossy(&utf16_packets);
println!("{:?}", s);
let utf8_bytes = s.as_bytes();
println!("{:?}", utf8_bytes);
}
> cargo +nightly run
"H€llo world!"
[72, 226, 130, 172, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]
这假设我们的输入可以完全转换为u16
个单位.在生产代码中,判断字节数是否不均匀是可取的.
为了用错误处理正确地编写它,我将把它提取到一个方法中并传播错误:
use thiserror::Error;
#[derive(Error, Debug)]
enum ParseUTF16Error {
#[error("UTF-16 data needs to contain an even amount of bytes")]
UnevenByteCount,
#[error("The given data does not contain valid UTF16 data")]
InvalidContent,
}
fn parse_utf16(data: &[u8]) -> Result<String, ParseUTF16Error> {
let data16 = data
.chunks(2)
.map(|e| e.try_into().map(u16::from_be_bytes))
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ParseUTF16Error::UnevenByteCount)?;
String::from_utf16(&data16).map_err(|_| ParseUTF16Error::InvalidContent)
}
fn main() {
let utf16_bytes: &[u8] = &[
0x00, 0x48, 0x20, 0xAC, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21,
];
let s = parse_utf16(utf16_bytes).unwrap();
println!("{:?}", s);
let utf8_bytes = s.as_bytes();
println!("{:?}", utf8_bytes);
}
"H€llo world!"
[72, 226, 130, 172, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]