我正在把一个二进制文件读入一个Rust程序,使用Vec<u8>作为缓冲区.流中的两个字节代表一个大端字节u16.

到目前为止,我唯一知道如何转换为基元u16的方法是首先将两个元素转换为String,它看起来是terrible.

代码:

let vector: Vec<u8> = [1, 16].to_vec();
let vector0: String = format!("{:02x}", vector[0]);
let vector1: String = format!("{:02x}", vector[1]);
let mut vector_combined = String::new();
vector_combined = vector_combined + &vector0.clone();
vector_combined = vector_combined + &vector1.clone();
let number: u16 = u16::from_str_radix(&vector_combined.to_string(), 16).unwrap();

println!("vector[0]: 0x{:02x}", vector[0]);
println!("vector[1]: 0x{:02x}", vector[1]);
println!("number: 0x{:04x}", number);

输出:

vector[0]: 0x01
vector[1]: 0x10
number: 0x0110

推荐答案

如果你有两个不同的u8,传统的解决方案包括按位操作,特别是移位和按位或.这需要零堆分配,而且非常高效:

let number = ((vector[0] as u16) << 8) | vector[1] as u16;

还有一个图形说明:

             A0                   B0
        +--------+           +--------+
        |XXXXXXXX|           |YYYYYYYY|
        +-------++           +-------++
                |                    |
 A1 = A0 as u16 |     B1 = B0 as u16 |
+---------------v+   +---------------v+
|00000000XXXXXXXX|   |00000000YYYYYYYY|
+---------------++   +---------------++
                |                    |
   A2 = A1 << 8 |                    |
+---------------v+                   |
|XXXXXXXX00000000|                   |
+---------------++                   |
                |              +--+  |
                +-------------->OR<--+
                               +-++
                                 |
                     V = A2 | B1 |
                 +----------+----v+
                 |XXXXXXXXYYYYYYYY|
                 +----------------+

然而,你真的把问题看得太狭隘了.你没有两个u8,你有一个&[u8].

在这种情况下,使用byteorder crate:

use byteorder::{ByteOrder, LittleEndian}; // 1.3.4

fn main() {
    let data = [1, 16];
    let v = LittleEndian::read_u16(&data);
    println!("{}", v);
}

当您想要处理通过缓冲区的读取时,这显示了它的威力:

use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; // 1.3.4

fn main() {
    let data = [1, 16, 1, 2];
    let mut current = &data[..];

    let v1 = current.read_u16::<LittleEndian>();
    let v2 = current.read_u16::<BigEndian>();

    println!("{:?}, {:?}", v1, v2); // Ok(4097), Ok(258)
}

正如你所看到的,你需要意识到你输入的endianness个数据.

你也可以先用get a fixed-size array from your slice,然后用u16::from_le_bytes.如果你有一个&[u8],想得到一个Vec<u16>,你可以使用chunks_exact(或array_chunks)在适当大小的切片上迭代.

另见:


在您的原始帖子上免费查看代码:

  • 这里不需要使用to_vec,而是使用vec!.

  • 无需指定绝大多数类型.

    let vector = [1u8, 16].to_vec();
    
    let vector0 = format!("{:02x}", vector[0]);
    let vector1 = format!("{:02x}", vector[1]);
    let mut vector_combined = String::new();
    vector_combined = vector_combined + &vector0.clone();
    vector_combined = vector_combined + &vector1.clone();
    let number = u16::from_str_radix(&vector_combined.to_string(), 16).unwrap();
    
  • 添加时引用字符串之前,无需克隆字符串.

  • 没有必要把String换成...from_str_radix人中还有String人.

    let vector0 = format!("{:02x}", vector[0]);
    let vector1 = format!("{:02x}", vector[1]);
    let mut vector_combined = String::new();
    vector_combined = vector_combined + &vector0;
    vector_combined = vector_combined + &vector1;
    let number = u16::from_str_radix(&vector_combined, 16).unwrap();
    
  • 不需要创建一个空的String来追加,只需使用vector0即可

    let vector0 = format!("{:02x}", vector[0]);
    let vector1 = format!("{:02x}", vector[1]);
    let vector_combined = vector0 + &vector1;
    let number = u16::from_str_radix(&vector_combined, 16).unwrap();
    
  • 根本不需要创建两个字符串,一个可以:

    let vector_combined = format!("{:02x}{:02x}", vector[0], vector[1]);
    let number = u16::from_str_radix(&vector_combined, 16).unwrap();
    

当然,这仍然不是正确的解决方案,但它是better.

Rust相关问答推荐

访问Rust中的隐藏变量

如何处理动态 struct 实例化?

如何导出 rust 色二进制文件中的符号

我是否可以在Ruust中修改 struct 实例上的字符串,以使其在修改后具有相同的字符串生存期?

Trait bound i8:来自u8的不满意

如何在不调用Collect()的情况下为新型vec实现IntoIterator?

完全匹配包含大小写的整数范围(&Q;)

Rust面向对象设计模式

在 Rust 中,在第一个空格上分割字符串一次

使用 Option 来分配?

如何以与平台无关的方式将OsString转换为utf-8编码的字符串?

std mpsc 发送者通道在闭包中使用时关闭

部署Rust发布二进制文件的先决条件

Rust中的位移操作对范围有什么影响?

Rust编译器通过哪些规则来确保锁被释放?

为什么要这样编译?

了解 Rust 闭包:为什么它们持续持有可变引用?

Rust 中的方法调用有什么区别?

如何使用 rust bindgen 生成的 std_vector

使用 rust-sqlx/tokio 时如何取消长时间运行的查询