【问题标题】:Read an arbitrary number of bytes from type implementing Read从实现 Read 的类型中读取任意数量的字节
【发布时间】:2015-08-15 06:10:44
【问题描述】:

我有一个Read;目前是File。我想从中读取一些仅在运行时才知道的字节(二进制数据结构中的长度前缀)。

所以我尝试了这个:

let mut vec = Vec::with_capacity(length);
let count = file.read(vec.as_mut_slice()).unwrap();

count 为零,因为vec.as_mut_slice().len() 也为零。

[0u8;length] 当然不起作用,因为大小必须在编译时知道。

我想做

let mut vec = Vec::with_capacity(length);
let count = file.take(length).read_to_end(vec).unwrap();

但是take 的接收器参数是T,而我只有&mut T(而且我不确定为什么需要它)。

我想我可以用BufReader 替换File 并用fill_bufconsume 跳舞,这听起来很复杂,但我仍然想知道:我是否忽略了什么?

【问题讨论】:

    标签: io rust


    【解决方案1】:

    与迭代器适配器一样,IO 适配器采用 self 的值以尽可能高效。与迭代器适配器一样,对Read 的可变引用也是Read

    要解决你的问题,你只需要Read::by_ref:

    use std::io::Read;
    use std::fs::File;
    
    fn main() {
        let mut file = File::open("/etc/hosts").unwrap();
        let length = 5;
    
        let mut vec = Vec::with_capacity(length);
        file.by_ref().take(length as u64).read_to_end(&mut vec).unwrap();
    
        let mut the_rest = Vec::new();
        file.read_to_end(&mut the_rest).unwrap();
    }
    

    【讨论】:

    • 我发现我什至不必打电话给by_ref()。 Rust 似乎会在 &File 上自动执行此操作,它似乎也不需要可变。这让我非常困惑! by_ref() 是不是某种神奇的方法?
    • @musiKk 不,这不是魔术,它实际上非常简单,its implementation 是一行,正文是一个关键字。参考作品的事实似乎......对我来说不正确。我可能不得不问我自己的问题!
    • 别担心,I'm done with one of my own。 ;)
    【解决方案2】:

    1。填充这个向量版本

    您的第一个解决方案即将生效。您发现了问题,但没有尝试解决它!问题是无论向量的容量如何,它仍然是空的 (vec.len() == 0)。相反,您实际上可以用空元素填充它,例如:

    let mut vec = vec![0u8; length];
    

    以下完整代码有效:

    #![feature(convert)] // needed for `as_mut_slice()` as of 2015-07-19
    
    use std::fs::File;
    use std::io::Read;
    
    fn main() {
        let mut file = File::open("/usr/share/dict/words").unwrap();
        let length: usize = 100;
        let mut vec = vec![0u8; length];
        let count = file.read(vec.as_mut_slice()).unwrap();
        println!("read {} bytes.", count);
        println!("vec = {:?}", vec);
    }
    

    当然,你还是要检查是否count == length,如果不是的话,再往缓冲区里读更多的数据。


    2。迭代器版本

    您的第二个解决方案更好,因为您不必检查已读取多少字节,也不必重新读取以防count != length。您需要在 Read 特征(由 File 实现)上使用 bytes() 函数。这会将文件转换为流(即迭代器)。因为仍然可能发生错误,所以您不会得到Iterator<Item=u8>,而是Iterator<Item=Result<u8, R::Err>>。因此,您需要在迭代器中明确处理故障。为简单起见,我们将在此处使用unwrap()

    use std::fs::File;
    use std::io::Read;
    
    fn main() {
        let file = File::open("/usr/share/dict/words").unwrap();
        let length: usize = 100;
        let vec: Vec<u8> = file
            .bytes()
            .take(length)
            .map(|r: Result<u8, _>| r.unwrap()) // or deal explicitly with failure!
            .collect();
        println!("vec = {:?}", vec);
    }
    

    【讨论】:

    • 我也想过这样的事情,但不是效率很低吗?看起来Vec 的分配从 O(1)ish 变为 O(n)。
    • 当然可以,但是在填充向量时无论如何你都会去 O(n),所以这是 2nn 的问题,这不是 那个坏的。我会更担心第二个版本中避免的检查count == length(见编辑)。
    • 我想这个问题可以归结为“我如何创建具有特定长度的Vec”。当我考虑到这一点时,保护开发人员免受此影响的高级语言可能会在后面这样做场景也是。否则我知道错误处理和检查实际读取的字节数。为了简洁起见,我把它省略了。谢谢。:)
    • 您使用bytes() 的解决方案实际上与我的take() 存在相同的问题:它使用file,但我只有对该文件的借用可变引用,因此这不起作用。 - 好吧,我不知道发生了什么。它确实适用于对文件的引用,但如果我有对包含文件的结构的引用,则不会。
    • 请使用vec![0u8; length]构建向量,它更短,性能更好,应该是成语。
    【解决方案3】:

    您始终可以使用一点unsafe 来创建未初始化内存的向量。使用原始类型是完全安全的:

    let mut v: Vec<u8> = Vec::with_capacity(length);
    unsafe { v.set_len(length); }
    let count = file.read(vec.as_mut_slice()).unwrap();
    

    这样,vec.len() 将被设置为其容量,并且其中的所有字节都将未初始化(可能为零,但也可能是一些垃圾)。这样可以避免将内存归零,这对于原始类型来说非常安全。

    请注意,Read 上的 read() 方法不能保证填充整个切片。它可能以小于切片长度的字节数返回。有几个 RFC 关于添加方法来填补这一空白,例如,this 一个。

    【讨论】:

    • 这很有趣。现在我选择了file.take(length).read_to_end(vec)。我在搬出借来的结构时遇到了问题,我现在设法解决了这个问题。这就是为什么我在我的问题中忽略了这个解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-03
    • 2011-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多