我有一个程序,需要在其中操作不同类型的文件。我希望以下程序的输入和输出文件是相同的。
#include<iostream>
#include<string>
#include<fstream>
#include<sstream>
typedef unsigned char u8;
using namespace std;
char* readFileBytes(string name)
{
ifstream fl(name);
fl.seekg( 0, ios::end );
size_t len = fl.tellg();
char *ret = new char[len];
fl.seekg(0, ios::beg);
fl.read(ret, len);
fl.close();
return ret;
}
int main(int argc, char *argv[]){
string name = "file.pdf";
u8* file = (u8*) readFileBytes(name);
// cout<<str<<endl;
int len = 0;
while(file[len] != ' ')
len++;
cout<<"FILESIZE : "<<len<<endl;
string filename = "file2.pdf";
ofstream outfile(filename,ios::out | ios::binary);
outfile.write((char*) file,len);
outfile.close();
exit(0);
}
使用diff
检查输出文件和输入文件之间的差异
diff file.pdf file2.pdf
我应该怎么做才能使file2.pdf
与file.pdf
相同?
我曾尝试使用xxd
将二进制文件更改为十六进制,但缺点是总大小加倍。所以我只想用二进制运算。
size_t len = fl.tellg();
char *ret = new char[len];
通过这种方式,显示的代码决定了文件中的字符数。这很好。它唯一的问题是,在读取了这么多字符后,这些非常重要的信息会被完全遗忘和丢弃。这个函数只返回这个ret
指针,其中的实际字符数现在是一个无法解决的谜。
但随后,main()
试图解开这个谜团如下:
int len = 0;
while(file[len] != ' ')
len++;
这试图通过查找缓冲区中的第一个0字节来反向工程字符数。
这与任何事情都毫无关系。文件中的第一个字符可能是0字节,因此这将计算出文件是空的,而不是10 GB大小。
或者该文件可以仅包含字符串";Hello world";,这个for
循环将愉快地通过,然后在这个缓冲区之后的一些随机存储器中开始扎根,导致未定义的行为。
这就是所示代码中致命的逻辑缺陷:文件的实际大小被丢弃,而是以一种有缺陷的方式进行反向工程。
您需要重新编写代码,这样文件中的字符数,即原始len
,也会返回给main()
,并且它会使用它,而不是试图猜测它最初是什么。
附言:delete
——在使用完ret
缓冲区后,使用它也是一个好主意。一个更好的想法是避免使用new
,而是使用vector
,这将很乐意在任何时候向您提供size()
,而且您不必担心删除分配的内存。
为了正确处理二进制数据,必须存储大小,并且不能从sentinel null字节计算,因为null字节可以是二进制文件中的合法字节。因此,除了缓冲区之外,您还应该返回读取长度,或者更好地将每个缓冲区复制到新文件,直到您用完输入文件:
int main(int argc, char *argv[]){
constexpr size_t sz = 10240; // size of buffer
char buffer[sz];
string name = "file.pdf";
string filename = "file2.pdf";
ifstream fl(name);
ofstream outfile(filename,ios::out | ios::binary);
int len = 0, buflen;
for (;;) {
buflen = fl.read(buf, len);
if (buflen == 0) break; // reached EOF
len += buflen;
if (buflen != outfile.write(buf, buflen)) {
// display an error message
return 1;
}
}
fl.close();
outfile.close()
cout<<"FILESIZE : "<<len<<endl;
exit(0);
}