如何从std::ifstream读取文件并存储为unsigned char数组?

7

通常我会做这样的事情:

    std::ifstream stream;
    int buff_length = 8192;
    boost::shared_array<char> buffer( new char[buff_length]);
    stream.open( path.string().c_str(), std::ios_base::binary);
    while (stream)
    {
            stream.read(buffer.get(), buff_length);
            //boost::asio::write(*socket, boost::asio::buffer(buffer.get(), stream.gcount()));
    }
    stream.close();

我想了解如何读取unsigned char缓冲区的内容(boost::shared_array<unsigned char> buffer( new unsigned char[buff_length]);)。

2
reinterpret_cast<> 是实际上正确的方法之一。 - Chad
此外,我更喜欢使用shared_ptr<std::vector<uint8_t>>而不是shared_array。 - Viktor Sehr
1个回答

14
以最简单的形式来说:
std::vector<unsigned char> vec(
      std::istreambuf_iterator<char>(std::cin)
    , std::istreambuf_iterator<char>()
    );

请将 std::cin 替换为实际的流。
上述代码可能会进行多次内存分配(对于大于几个字节的文件),因为 std::istreambuf_iterator<> 是一个输入迭代器,而不是随机访问或前向迭代器,所以无法通过减去迭代器(如 end - begin)或调用 std :: distance(begin,end) 来测量文件的长度。如果首先创建空向量,则可以将其缩减为一次内存分配,然后调用 std::vector<> :: reserve() 为文件长度分配内存,最后使用上面的 begend 作为 std::istreambuf_iterator<> 调用范围插入 vec.insert(vec.end(), beg, end) 读取整个文件。
如果文件大小超过几千字节,则将其映射到进程内存中以避免从内核复制内存到用户空间可能是最有效的方法。
原因是实现使用了 std::char_traits<>,它通常只为 char 和 wchar_t 进行特化。因此,使用 std::istreambuf_iterator。不过,C 和 C++ 标准要求所有 char 类型具有相同的二进制布局,没有填充位,所以 char、unsigned char 和 signed char 之间的转换(它们都是不同的类型,不像 signed int 和 int 是相同类型)保留位模式,因此是安全的。

[basic.fundamental/1]


普通的 char、signed char 和 unsigned char 是三种不同的类型,统称为窄字符类型。char、signed char 和 unsigned char 占用相同的存储空间并具有相同的对齐要求;也就是说,它们具有相同的对象表示形式... 对于窄字符类型,对象表示形式的所有位都参与值表示形式... 对于无符号窄字符类型,值表示形式的每个可能位模式都代表一个不同的数字。这些要求不适用于其他类型。在任何特定的实现中,普通的 char 对象可以采用与 signed char 或 unsigned char 相同的值;哪一个是实现定义的。对于范围在 0 到 255(含)之间的 unsigned char 类型的每个值 i,存在一个 char 类型的值 j,使得从 i 到 char 的整数转换的结果为 j,并且从 j 到 unsigned char 的整数转换的结果为 i。

标准并不要求 char c = -1; unsigned char u = c; 的结果是 cu 具有相同的位模式。理论上,有符号字符可以使用 1 的补码或符号-大小表示法。 - M.M
有符号字符的非负值必须与相应的无符号字符具有相同的表示。但是,在补码中,例如,char c = -1; 具有位模式11111110,而 unsigned char u = c; 具有位模式11111111。这在大多数情况下都是学术性的,因为我不知道任何没有使用二进制补码的 C++ 实现。 - M.M
@M.M,我已经为您添加了引号。 - Maxim Egorushkin
它们显然没有相同的值表示方式;在signed char中,值-1具有值表示方式,但在unsigned char中则没有。 - M.M
@M.M 为您添加了更长的引用。_值表示_不同(符号位)。_对象表示_相同。 - Maxim Egorushkin
@MaximEgorushkin,这段(在C++14中添加的)文本似乎要求如果普通字符是有符号的,它必须遵循二进制补码。但首先,在C语言中并没有您所声称的等效要求;其次,它仍然允许signed char c = -1;使用一的补码(即获得位模式11111110),并且使普通字符无符号。 - M.M

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