读写二进制文件

152

我试图编写代码将二进制文件读入缓冲区,然后将缓冲区写入另一个文件。我有以下代码,但是缓冲区仅存储来自文件第一行的几个ASCII字符,没有其他内容。

int length;
char * buffer;

ifstream is;
is.open ("C:\\Final.gif", ios::binary );
// get length of file:
is.seekg (0, ios::end);
length = is.tellg();
is.seekg (0, ios::beg);
// allocate memory:
buffer = new char [length];
// read data as a block:
is.read (buffer,length);
is.close();

FILE *pFile;
pFile = fopen ("C:\\myfile.gif", "w");
fwrite (buffer , 1 , sizeof(buffer) , pFile );

37
你应该决定使用iostream或C文件处理。请不要同时使用两种方法。 - frast
2
以上代码中关于缓冲变量的部分存在错误。它的类型应该是“unsigned char”,分配应该是“buffer = new unsigned char[length + 1]”,然后“buffer[length] = '\0'”。我知道这个问题已经发布了很多年,但没有人写过这个问题。 - Raluca Pandaru
1
@RalucaPandaru,鉴于输入文件是GIF格式并且明确以ios::binary方式读取,按照您的建议添加零终止符是没有意义的。此外,在这里使用charunsigned char之间的区别并不重要,因为代码并没有尝试解释文件内容。 上述代码的问题在于sizeof(buffer)的值为4或8(32/64位指针)。因此,它总是向输出文件写入4/8个字节。 作者应该使用length而不是sizeof(buffer) - Simon Rozman
1
@RalucaPandaru,鉴于输入文件是GIF并且明确读取为ios::binary,像你建议的那样添加零终结符是毫无意义的。在这里使用charunsigned char的区别并不大,因为代码并不试图解释文件内容。 上述代码的问题在于sizeof(buffer)是4或8(32/64位指针)。因此,它总是向输出文件写入4/8个字节。 作者应该使用length而不是sizeof(buffer) - undefined
8个回答

240

如果你想用C++的方式来做到这一点,可以这样做:

#include <fstream>
#include <iterator>
#include <algorithm>

int main()
{
    std::ifstream input( "C:\\Final.gif", std::ios::binary );
    std::ofstream output( "C:\\myfile.gif", std::ios::binary );

    std::copy( 
        std::istreambuf_iterator<char>(input), 
        std::istreambuf_iterator<char>( ),
        std::ostreambuf_iterator<char>(output));
}

如果你需要在缓冲区中修改数据或进行其他操作,可以使用以下方法:

#include <fstream>
#include <iterator>
#include <vector>

int main()
{
    std::ifstream input( "C:\\Final.gif", std::ios::binary );

    // copies all data into buffer
    std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(input), {});

}

6
如果我只想将数据的某个部分复制到缓冲区中,该如何做呢?比如说复制1024字节。 - likern
9
在这里你可以找到一些基准测试结果。链接是:http://insanecoding.blogspot.it/2011/11/how-to-read-in-file-in-c.html - Paolo M
3
据我所知,二进制文件有时包含无法读取的字符,实际上它们根本不是字符。这段代码用于读取非文本格式的文件是否安全?我的知识范围有限 :) - Andiana
8
在C/C++中,所谓的“char”用于存储字节(已经使用了40年)。只要不尝试将该数据实际用作字符(不要在其上使用strlen(),不要将其打印到控制台等),这样做是安全的。C++17引入了std::byte,用于此目的(实际上仍然是char,但伪装起来了)。 - d.Candela
2
@DavidTran 如果没有更多信息,很难给出答案——你应该创建一个最小的实例来重现问题,然后发布一个问题。 - Björn Pollex
显示剩余5条评论

17

以下是一个简短的例子,使用C++的rdbuf方法。这个例子来源于网络,但我找不到原始来源:

#include <fstream>
#include <iostream>

int main () 
{
  std::ifstream f1 ("C:\\me.txt",std::fstream::binary);

  std::ofstream f2 ("C:\\me2.doc",std::fstream::trunc|std::fstream::binary);

  f2<<f1.rdbuf();

  return 0;
}

12
最佳的、不可移植的方法是让操作系统复制你的文件。毕竟,这是它的本职工作的一部分;没有必要“重复发明轮子”。 - Thomas Matthews

15
 sizeof(buffer) == sizeof(char*) 

使用长度代替。

此外,最好使用带有“wb”参数的fopen......


不能使用 buffer.length(),因为缓冲区内可能存在NULL值,从而破坏了strlen/length()的目的。 - John Greene
最好使用sizeof(buffer) - John Greene

10

sizeof(buffer) 这个表达式返回的是指针的大小,而不是缓冲区的实际大小。在接下来的代码中,你需要使用你已经确定的“length”变量来获取缓冲区的实际大小。


6

应该将数据长度传递给fwrite而不是传递sizeof(buffer).


4
这是使用 vectorstuples 实现的标准 C++ 14,用于读写文本、二进制和十六进制文件
代码段:
try {
if (file_type == BINARY_FILE) {

    /*Open the stream in binary mode.*/
    std::ifstream bin_file(file_name, std::ios::binary);

    if (bin_file.good()) {
        /*Read Binary data using streambuffer iterators.*/
        std::vector<uint8_t> v_buf((std::istreambuf_iterator<char>(bin_file)), (std::istreambuf_iterator<char>()));
        vec_buf = v_buf;
        bin_file.close();
    }

    else {
        throw std::exception();
    }

}

else if (file_type == ASCII_FILE) {

    /*Open the stream in default mode.*/
    std::ifstream ascii_file(file_name);
    string ascii_data;

    if (ascii_file.good()) {
        /*Read ASCII data using getline*/
        while (getline(ascii_file, ascii_data))
            str_buf += ascii_data + "\n";

        ascii_file.close();
    }
    else {
        throw std::exception();
    }
}

else if (file_type == HEX_FILE) {

    /*Open the stream in default mode.*/
    std::ifstream hex_file(file_name);

    if (hex_file.good()) {
        /*Read Hex data using streambuffer iterators.*/
        std::vector<char> h_buf((std::istreambuf_iterator<char>(hex_file)), (std::istreambuf_iterator<char>()));
        string hex_str_buf(h_buf.begin(), h_buf.end());
        hex_buf = hex_str_buf;

        hex_file.close();
    }
    else {
        throw std::exception();
    }
}

完整的源代码可以在这里找到。


-1

使用以下代码片段中的简单命令即可完成。

复制任意大小的整个文件。没有大小限制!

只需使用此命令。已测试并可用!

#include<iostream>
#include<fstream>
using namespace std;
int main()
{
  ifstream infile;
  infile.open("source.pdf",ios::binary|ios::in);

  ofstream outfile;
  outfile.open("temppdf.pdf",ios::binary|ios::out);

  int buffer[2];
  while(infile.read((char *)&buffer,sizeof(buffer)))
  {
      outfile.write((char *)&buffer,sizeof(buffer));
  }

  infile.close();
  outfile.close();
  return 0;
}

在复制小文件时,使用较小的缓冲区大小会很有帮助。即使是 "char buffer[2]" 也可以完成任务。


9
如果文件大小不是缓冲区大小的倍数怎么办?此外,为什么要将缓冲区声明为int[]而不是char[] - firegurafiku
我已经提到它也适用于char[]和任意大小的文件,这意味着没有文件大小应该是缓冲区大小的倍数的条件。 - iMajetyHK
2
你说它能工作并不代表它真的能工作。它不能工作才意味着它不能工作。 - nunojpg
你至少可以将 'int buffer[2]' 更改为 'char buffer[1]',以使代码能够正常运行,而无需更改修复错误的代码。 - Ruud van Gaal

-2

有一种更简单的方法。它不关心是二进制文件还是文本文件。

使用noskipws。

char buf[SZ];
ifstream f("file");
int i;
for(i=0; f >> noskipws >> buffer[i]; i++);
ofstream f2("writeto");
for(int j=0; j < i; j++) f2 << noskipws << buffer[j];

或者你可以直接使用字符串而不是缓冲区。

string s; char c;
ifstream f("image.jpg");
while(f >> noskipws >> c) s += c;
ofstream f2("copy.jpg");
f2 << s;

通常情况下,流会跳过空格、换行符、制表符和其他控制字符等空白字符。但是noskipws选项会传输所有字符。因此,这不仅可以复制文本文件,还可以复制二进制文件。而且流在内部使用缓冲区,我认为速度不会很慢。

你可能想要详细说明一下,以使其更易理解。 - jvh

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