如何将一个字节切片的缓冲区(&[u8])转换为整数?

50

我正在从文件中读取原始数据,我想将它转换为整数:

fn main() {
    let buf: &[u8] = &[0, 0, 0, 1];
    let num = slice_to_i8(buf);
    println!("1 == {}", num);
}

pub fn slice_to_i8(buf: &[u8]) -> i32 {
    unimplemented!("what should I do here?")
}

我会在C中使用类型转换,但在Rust中该怎么做呢?

4个回答

62

我建议使用byteorder crate(在无标准库环境下也可用):

use byteorder::{BigEndian, ReadBytesExt}; // 1.2.7

fn main() {
    let mut buf: &[u8] = &[0, 0, 0, 1];
    let num = buf.read_u32::<BigEndian>().unwrap();

    assert_eq!(1, num);
}

这个功能可以处理不同大小的切片,并自动推进缓冲区,以便您可以读取多个值。

从 Rust 1.32 开始,您还可以在整数上使用 from_le_bytes / from_be_bytes / from_ne_bytes 内在方法:

fn main() {
    let buf = [0, 0, 0, 1];
    let num = u32::from_be_bytes(buf);

    assert_eq!(1, num);
}

这些方法仅处理固定长度的数组,以避免在数据不足时出现错误。如果您有一个切片,您需要将其转换为数组

另请参阅:


8
如果你只需要读取一个项目,那么你也可以使用 BigEndian::read_i32(&bytes[..])。 - moveaway00
如果buf是一个vec<8>会怎样? - user2284570
@Shepmaster,更确切地说,我需要保留现有的字节顺序,并且代码将在支持不对齐内存访问的 CPU 上运行。那么,没有使用from_be_bytes该如何实现相同的功能,因为似乎没有from_ptr()函数可用?还有,似乎Vec没有read_u32 - user2284570
@Shepmaster 并且在仍然使用 NativeEndian 的情况下对数组进行操作? - user2284570
显示剩余5条评论

26

我想在这里提供以下额外细节的答案:

  1. 一个有效的代码片段,将slices转换为整数(两种方法)。
  2. 一个在no_std环境中的有效解决方案。
  3. 为了让从搜索引擎过来的人能够在一个地方找到所有信息。

在Rust 1.32及更高版本中,即使是no_std构建,下面的方法也适用于从slices转换为整数:

方法1(try_into + from_be_bytes

use core::convert::TryInto;

let src = [1, 2, 3, 4, 5, 6, 7];

// 0x03040506
u32::from_be_bytes(src[2..6].try_into().unwrap());

use core::convert::TryInto 适用于 no_std 构建。 使用标准库的方法如下:use std::convert::TryInto;

(至于字节序,已经有答案了,但让我在这里放一下:使用from_le_bytesfrom_be_bytesfrom_ne_bytes - 根据整数在内存中的表示方式来选择使用哪个函数)。

方法2 (clone_from_slice + from_be_bytes)

let src = [1, 2, 3, 4, 5, 6, 7];
let mut dst = [0u8; 4];

dst.clone_from_slice(&src[2..6]);

// 0x03040506
u32::from_be_bytes(dst);

结果

无论哪种情况,整数将等于0x03040506


1
这并没有为现有的答案增加任何内容,因为它已经讨论了 from_le_bytes / from_be_bytes / from_ne_bytes 并链接到一个问题,展示了如何从切片中获取固定大小的数组。 - Shepmaster
3
@Shepmaster很遗憾你这样认为。我并不完全同意,因为我收集了所有stackoverflow答案的信息,并决定将它们放在这里以便大家查看。在这个答案中,我加入了几个要点:1. 在no_std环境下该怎么做。2. 如何处理切片,就像原问题所述(而不是跳转到链接)。3. 在没有使用byteorder的情况下该怎么做。 - Alexander Fadeev
1
byteorder 在 no-std 环境下工作,点击链接不会对互联网造成负担,现有的答案讨论了避免 byteorder 的方法。 - Shepmaster
3
顺便说一句,我没有注意到这是你的回答/问题派对。;) 但你应该知道,“另请参阅”注释在你的答案中并不是非常显眼,而且看到有关如何将数组转换为整数的代码片段,而不是将切片转换为整数,会让人感到相当困惑。你可能会有不同的看法,我只是告诉你它看起来是什么样子的。无论如何,你只能改进自己的答案,对吧?(附言:“点击链接不会对互联网造成负担” - 这是你主观的观点,与我的观点不同) - Alexander Fadeev
6
作为一个初学者,我非常感激这个答案,因为它展示了如何从切片的中间开始读取。 - Kofthefens
显示剩余2条评论

0
这是我的实现(针对不同的用例),它会丢弃超过8个字节的任何附加字节(因此如果不完全匹配就不需要恐慌):
pub fn u64_from_slice(slice: &[u8]) -> u64 {
    u64::from_ne_bytes(slice.split_at(8).0.try_into().unwrap())
}

split_at() 方法返回两个切片的元组:一个从索引 0 到指定索引,另一个从指定索引到结尾。因此,通过使用 .0 访问 .split_at(8) 返回的元组的第一个成员,它确保仅传递前 8 个字节给 u64::to_ne_bytes(),丢弃剩余部分。然后,当然,在该.0元组成员上调用try_into方法,并使用.unwrap(),因为split_at为您执行所有自定义崩溃。


1
我建议使用 slice[..8] 而不是 slice.split_at(8).0 - JimmyZ

0

这个自定义的 serialize_deserialize_u8_i32 库可以安全地在 u8 数组和 i32 数组之间进行转换,即序列化函数将把所有的 u8 值打包成 i32 值,而反序列化函数将接受此库的自定义 i32 值并将其转换回您最初使用的原始 u8 值。

这是为特定目的构建的,但根据您是否需要像这样快速/自定义的转换器,它可能会派上用场。

https://github.com/second-state/serialize_deserialize_u8_i32


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接