将“Vec<u16>"内容写入文件的正确方法是什么? [英] What is the correct way to write `Vec<u16>` content to a file?

查看:28
本文介绍了将“Vec<u16>"内容写入文件的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在将 Vec 内容写入文件时遇到问题:

使用 std::fs::File;使用 std::io::{Write, BufWriter};使用 std::mem;#[派生(调试,复制,克隆,PartialEq)]酒吧枚举 ImageFormat {灰度,RGB32,}#[派生(调试,复制,克隆,PartialEq)]pub 结构 ImageHeader {酒吧宽度:使用,酒吧高度:使用,发布格式:ImageFormat,}酒吧结构图像{发布头:ImageHeader,发布数据:Vec,}fn write_to_file(path: &str, img: &Image) ->std::io::Result<()>{让 f = try!(File::create(path));让 mut bw = BufWriter::new(f);让 slice = &img.data[..];println!("长度前:{}", slice.len());让 sl: &[u8];不安全{sl = mem::transmute::<&[u16], &[u8]>(slice);}println!("长度后:{}", sl.len());试试吧!(bw.write_all(sl));返回 Ok(());}fn main() {}

由于 write_all() 要求 &[u8],我正在对 &[u16] 进行不安全的转换&[u8].因为转换不会改变切片长度(slice.len()sl.len() 具有相同的值),所以只有一半的图像数据输出到文件.

如果我不需要任何不安全的转换或复制会更好.

解决方案

要直接执行此操作,您需要使用 std::slice::from_raw_parts():

use std::{mem, slice};fn 主(){让 slice_u16: &[u16] = &[1, 2, 3, 4, 5, 6];println!("u16s: {:?}", slice_u16);让 slice_u8: &[u8] = 不安全 {切片::from_raw_parts(slice_u16.as_ptr() 作为 *const u8,slice_u16.len() * mem::size_of::(),)};println!("u8s: {:?}", slice_u8);}

它确实需要 unsafe 因为 from_raw_parts() 不能保证你传递了一个有效的指针给它,它也可以创建具有任意生命周期的切片.>

另见:

这种方法不仅可能不安全,而且也不可移植.当您使用大于 1 个字节的整数时,会立即出现字节序问题.如果您在 x86 机器上以这种方式写入文件,那么您将在 ARM 机器上读取垃圾.正确的方法是使用像 byteorder 这样的库,它允许您指定字节序明确:

use byteorder::{LittleEndian, WriteBytesExt};//1.3.4fn 主(){让 slice_u16: &[u16] = &[1, 2, 3, 4, 5, 6];println!("u16s: {:?}", slice_u16);让 mut 结果:Vec<u8>= Vec::new();for &n in slice_u16 {让 _ = result.write_u16::(n);}println!("u8s: {:?}", result);}

请注意,我在这里使用了 Vec,但它实现了 Writewrite_u16() 以及来自WriteBytesExt 特性定义在任何 Write,例如,您可以直接在 BufWriter 上使用这些方法.

编写后,您可以使用 ReadBytesExt 中的方法 trait 读取数据.

虽然这可能比重新解释一段内存效率稍低,但它是安全且可移植的.

另见:

I'm having trouble writing Vec<u16> content to a file:

use std::fs::File;
use std::io::{Write, BufWriter};
use std::mem;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ImageFormat {
    GrayScale,
    Rgb32,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ImageHeader {
    pub width: usize,
    pub height: usize,
    pub format: ImageFormat,
}

pub struct Image {
    pub header: ImageHeader,
    pub data: Vec<u16>,
}

fn write_to_file(path: &str, img: &Image) -> std::io::Result<()> {
    let f = try!(File::create(path));
    let mut bw = BufWriter::new(f);
    let slice = &img.data[..];
    println!("before length: {}", slice.len());
    let sl: &[u8];
    unsafe {
        sl = mem::transmute::<&[u16], &[u8]>(slice);
    }
    println!("after length: {}", sl.len());
    try!(bw.write_all(sl));
    return Ok(());
}

fn main() {}

Since write_all() asks for a &[u8], I'm doing an unsafe conversion of &[u16] to &[u8]. Because the conversion does not change the slice length (slice.len() and sl.len() have the same values), only half of the image data is output to the file.

It would be better if I don't need any unsafe conversion or copying.

解决方案

To do it directly you'd want to use std::slice::from_raw_parts():

use std::{mem, slice};

fn main() {
    let slice_u16: &[u16] = &[1, 2, 3, 4, 5, 6];
    println!("u16s: {:?}", slice_u16);

    let slice_u8: &[u8] = unsafe {
        slice::from_raw_parts(
            slice_u16.as_ptr() as *const u8,
            slice_u16.len() * mem::size_of::<u16>(),
        )
    };

    println!("u8s: {:?}", slice_u8);
}

It does require unsafe because from_raw_parts() can't guarantee that you passed a valid pointer to it, and it can also create slices with arbitrary lifetimes.

See also:

This approach is not only potentially unsafe, it is also not portable. When you work with integers larger than one byte, endianness issues immediately arise. If you write a file in this way on a x86 machine, you would then read garbage on an ARM machine. The proper way is to use libraries like byteorder which allow you to specify endianness explicitly:

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

fn main() {
    let slice_u16: &[u16] = &[1, 2, 3, 4, 5, 6];
    println!("u16s: {:?}", slice_u16);

    let mut result: Vec<u8> = Vec::new();
    for &n in slice_u16 {
        let _ = result.write_u16::<LittleEndian>(n);
    }

    println!("u8s: {:?}", result);
}

Note that I've used Vec<u8> here, but it implements Write, and write_u16() and other methods from the WriteBytesExt trait are defined on any Write, so you could use these methods directly on a BufWriter, for example.

Once written, you can use methods from the ReadBytesExt trait to read the data back.

While this may be slightly less efficient than reinterpreting a piece of memory, it is safe and portable.

See also:

这篇关于将“Vec&lt;u16&gt;"内容写入文件的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆