我有一个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.

因此,重要的第一步是将u8s转换为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]

Rust相关问答推荐

如何创建引用构造函数拥有的变量的对象?

限制未使用的泛型导致编译错误

无法从流中读取Redis请求

使用极点数据帧时,找不到枚举结果的方法lazy()

有没有可能让泛型Rust T总是堆分配的?

如果变量本身不是None,如何返回;如果没有,则返回None&Quot;?

如何使用reqwest进行异步请求?

如何将生存期参数添加到框<>;具有dyn类型别名

为什么 Rust 创建的 f32 小于 f32::MIN_POSITIVE?

无法将`&Vec>`转换为`&[&str]`

为什么需要同时为值和引用实现`From`?方法不应该自动解引用或borrow 吗?(2023-06-16)

(let b = MyBox(5 as *const u8); &b; ) 和 (let b = &MyBox(5 as *const u8); ) 之间有什么区别

我如何取消转义,在 Rust 中多次转义的字符串?

返回迭代器的特征

使用 HashMap 条目时如何避免字符串键的短暂克隆?

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

当我在 struct 中存储异步函数时,为什么它需要生命周期

Rust HRTB 是相同的,但编译器说一种类型比另一种更通用

基于名称是否存在的条件编译

Rust:为什么在 struct 中borrow 引用会borrow 整个 struct?