【问题标题】:How can I convert a buffer of a slice of bytes (&[u8]) to an integer?如何将字节切片 (&[u8]) 的缓冲区转换为整数?
【发布时间】:2024-04-20 02:50:01
【问题描述】:

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

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 中我该怎么做?

【问题讨论】:

    标签: arrays casting rust slice


    【解决方案1】:

    我想在这里给出这个答案以提交以下附加细节:

    1. 工作代码 sn-p 将 slice 转换为整数(有两种方法)。
    2. no_std 环境中的有效解决方案。
    3. 将所有内容集中在一个地方,以供从搜索引擎访问的用户使用。

    如果没有外部 crate,以下方法适用于从 slices 转换为整数,即使对于从 Rust 1.32 开始的 no_std 构建:

    方法一(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::conver::TryInto 用于 no_std 构建。标准 crate 的使用方法如下:use std::convert::TryInto;.

    (关于字节序,它已经得到解答,但让我把它放在一个地方:from_le_bytesfrom_be_bytesfrom_ne_bytes - 使用它们取决于整数在内存中的表示方式)。

    方法二(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

    【讨论】:

    • 这并没有真正为现有答案添加任何内容,现有答案已经讨论了 from_le_bytes / from_be_bytes / from_ne_bytes 并链接到一个问题,该问题显示如何从切片中获取固定大小的数组。
    • @Shepmaster 很遗憾你这么认为。我不太同意,因为我收集了所有 * 答案中的所有这些信息,并决定将其放在这里以方便起见。在这个答案中,我补充几点: 1. 在no_std 环境中做什么。 2.如何处理原始问题所述的切片(不跳转到链接)。 3.没有byteorder怎么办。
    • 字节序在无标准环境下工作,点击链接不是互联网的负担,现有答案谈到避免字节序。
    • @Shepmaster 顺便说一句,我没有注意到这是您的回答/提问派对。 ;) 但是您应该知道“另请参阅”注释在您的答案中并不是很明显,而且在您的答案中看到关于如何将数组转换为整数而不是将切片转换为整数的代码 sn-p 也很令人困惑。你可能会有不同的想法,我只是告诉它看起来如何。无论如何,您只能改进自己的答案,对吗? (P.S.:“点击链接不是互联网的负担”——这是你的主观观点,与我的不同)
    • 作为一个新手,我非常感谢这个答案,因为它展示了如何从切片中间读取
    【解决方案2】:

    这个自定义 serialize_deserialize_u8_i32 库将安全地在 u8 数组和 i32 数组之间来回转换,即 serialise 函数将获取所有 u8 值并将它们打包成 i32 值,而反序列化函数将获取该库的自定义 i32 值并转换它们回到你开始使用的原始 u8 值。

    这是为特定目的而构建的,但它可能会在其他用途​​中派上用场;取决于您是否想要/需要这样的快速/自定义转换器。

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

    【讨论】:

      【解决方案3】:

      我建议使用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);
      }
      

      这些方法只处理固定长度的数组,以避免在没有足够数据时处理错误。如果你有切片,你需要convert it into an array

      另见:

      【讨论】:

      • 如果您只阅读单个项目,也可以使用 BigEndian::read_i32(&bytes[..])。
      • 如果 buf 是 vec 怎么办?
      • @user2284570 你不必为Vec&lt;u8&gt; 做任何特别的事情。另见How do I get a slice of a Vec<T> in Rust?。您必须关心数据的字节顺序。这是1234554321 之间的区别。只要作者和读者都同意数据的字节顺序,就可以了。如果您正在编写它,请选择一个并始终如一地使用它。大多数人可能会选择小端。
      • @Shepmaster 我需要保留现有的字节顺序,并且代码将在支持未对齐内存访问的 cpu 上运行。那么如何在没有from_be_bytes 的情况下做同样的事情,因为似乎没有from_ptr() 功能?似乎Vec 也没有read_u32