代码之家  ›  专栏  ›  技术社区  ›  Jarak

为什么为无符号整数复制c's fread的泛型函数总是返回零?

  •  2
  • Jarak  · 技术社区  · 8 年前

    我正在尝试从16位体系结构中读取二进制16位机器指令(这里与此无关),并将其打印回十六进制值。在C中,我通过使用 fread 函数将16位读入 uint16_t .

    我想我会尝试复制 弗雷德 生锈了。如果我能提前知道正在被读取的变量的确切大小,并且我已经将其专门用于16位,那么这似乎是相当微不足道的。

    我决定我要尝试 弗雷德 各种内置无符号整数类型的通用函数。为此,我使用num-crate中的一些特性提出了以下功能:

    fn fread<T>(
        buffer: &mut T,
        element_count: usize,
        stream: &mut BufReader<File>,
    ) -> Result<usize, std::io::Error>
    where
        T: num::PrimInt + num::Unsigned,
    {
        let type_size = std::mem::size_of::<T>();
        let mut buf = Vec::with_capacity(element_count * type_size);
        let buf_slice = buf.as_mut_slice();
    
        let bytes_read = match stream.read_exact(buf_slice) {
            Ok(()) => element_count * type_size,
            Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => 0,
            Err(e) => panic!("{}", e),
        };
    
        *buffer = buf_slice
            .iter()
            .enumerate()
            .map(|(i, &b)| {
                let mut holder2: T = num::zero();
                holder2 = holder2 | T::from(b).expect("Casting from u8 to T failed");
                holder2 << ((type_size - i) * 8)
            })
            .fold(num::zero(), |acc, h| acc | h);
        Ok(bytes_read)
    }
    

    问题是,当我在主函数中调用它时,我似乎总是 0x00 退出,但函数返回的读取字节数始终为2,因此程序进入无限循环:

    extern crate num;
    
    use std::fs::File;
    use std::io::BufReader;
    use std::io::prelude::Read;
    
    fn main() -> Result<(), std::io::Error> {
        let cmd_line_args = std::env::args().collect::<Vec<_>>();
    
        let f = File::open(&cmd_line_args[1])?;
        let mut reader = BufReader::new(f);
        let mut instructions: Vec<u16> = Vec::new();
    
        let mut next_instruction: u16 = 0;
        fread(&mut next_instruction, 1, &mut reader)?;
    
        let base_address = next_instruction;
    
        while fread(&mut next_instruction, 1, &mut reader)? > 0 {
            instructions.push(next_instruction);
        }
    
        println!("{:#04x}", base_address);
    
        for i in instructions {
            println!("0x{:04x}", i);
        }
    
        Ok(())
    }
    

    在我看来,我从未从文件中读取过任何内容,所以函数总是返回它应该读取的字节数。显然我在这里没有正确地使用某些东西,但我真的不确定我做错了什么。

    这是在Rust 1.26上编译的,如果这很重要的话,它是为Windows稳定的。

    我做错了什么,我应该做什么不同的复制 弗雷德 ?我知道这可能是xy问题的一个例子(几乎可以肯定,有一种更好的方法可以从文件中反复读取一些字节,然后将它们打包成一个无符号整数),但是我真的很好奇我在这里做了什么错误。

    2 回复  |  直到 8 年前
        1
  •  4
  •   Shepmaster Tim Diekmann    8 年前

    你的问题是这条线:

    let mut buf = Vec::with_capacity(element_count * type_size);
    

    创建一个 零长度 向量,即使它为 element_count * type_size 字节。所以你在问 stream.read_exact 读取零字节。解决此问题的一种方法是将上面的行替换为:

    let mut buf = vec![0; element_count * type_size];
    

    旁注:当读取成功时, bytes_read 接收预期读取的字节数,而不是实际读取的字节数。你应该使用 std::mem::size_of_val (buf_slice) 以获取真正的字节计数。

        2
  •  2
  •   Shepmaster Tim Diekmann    8 年前

    在这种情况下,几乎可以肯定有一种更好的可靠方法,可以反复从文件中读取一些字节,并将它们打包成一个无符号整数。

    是的,使用 byteorder crate . 这不需要不必要的堆分配 Vec 在原始代码中):

    extern crate byteorder;
    
    use byteorder::{LittleEndian, ReadBytesExt};
    use std::{
        fs::File, io::{self, BufReader, Read},
    };
    
    fn read_instructions_to_end<R>(mut rdr: R) -> io::Result<Vec<u16>>
    where
        R: Read,
    {
        let mut instructions = Vec::new();
        loop {
            match rdr.read_u16::<LittleEndian>() {
                Ok(instruction) => instructions.push(instruction),
                Err(e) => {
                    return if e.kind() == std::io::ErrorKind::UnexpectedEof {
                        Ok(instructions)
                    } else {
                        Err(e)
                    }
                }
            }
        }
    }
    
    fn main() -> Result<(), std::io::Error> {
        let name = std::env::args().skip(1).next().expect("no file name");
    
        let f = File::open(name)?;
        let mut f = BufReader::new(f);
    
        let base_address = f.read_u16::<LittleEndian>()?;
        let instructions = read_instructions_to_end(f)?;
    
        println!("{:#04x}", base_address);
    
        for i in &instructions {
            println!("0x{:04x}", i);
        }
    
        Ok(())
    }
    
    推荐文章